fix(auth): use correct Better Auth API method for password reset

- Fix `requestPasswordReset` method name (was incorrectly `forgetPassword`)
- Add proper TypeScript types for password reset, session, and JWT methods
- Consolidate API accessor from `orgApi` to `api` for all Better Auth calls
- Remove unnecessary `as any` casts by extending BetterAuthAPI interface
- Clean up unused imports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Wuesteon 2025-12-16 01:49:06 +01:00
parent 78cd59a77a
commit eeab9b7fea
3 changed files with 40 additions and 28 deletions

View file

@ -91,8 +91,8 @@ export function createBetterAuth(databaseUrl: string) {
* Password Reset Configuration
*
* Better Auth provides password reset via:
* - auth.api.forgetPassword({ email }) - Sends reset email
* - auth.api.resetPassword({ newPassword, token }) - Resets password
* - auth.api.requestPasswordReset({ body: { email } }) - Sends reset email
* - auth.api.resetPassword({ body: { newPassword, token } }) - Resets password
*
* @see https://www.better-auth.com/docs/authentication/email-password#password-reset
*/

View file

@ -56,11 +56,8 @@ import type {
OrganizationMember,
Organization,
BetterAuthAPI,
SignUpResponse,
SignInResponse,
CreateOrganizationResponse,
BetterAuthUser,
BetterAuthSession,
} from '../types/better-auth.types';
import * as jwt from 'jsonwebtoken';
import { jwtVerify, createRemoteJWKSet } from 'jose';
@ -89,11 +86,13 @@ export class BetterAuthService {
private databaseUrl: string;
/**
* Typed accessor for organization plugin API methods
* Better Auth's organization plugin adds methods dynamically, so we provide
* Typed accessor for Better Auth API methods
* Better Auth's plugins add methods dynamically, so we provide
* a typed accessor to avoid casting throughout the service.
*
* @see https://www.better-auth.com/docs/concepts/typescript
*/
private get orgApi(): BetterAuthAPI {
private get api(): BetterAuthAPI {
return this.auth.api as unknown as BetterAuthAPI;
}
@ -245,7 +244,7 @@ export class BetterAuthService {
try {
// Better Auth organization plugin uses auth.api.inviteMember
// See: https://www.better-auth.com/docs/plugins/organization
const result = await this.orgApi.inviteMember({
const result = await this.api.inviteMember({
body: {
email: dto.employeeEmail,
role: dto.role,
@ -286,7 +285,7 @@ export class BetterAuthService {
try {
// Better Auth organization plugin uses auth.api.acceptInvitation
// See: https://www.better-auth.com/docs/plugins/organization
const result = await this.orgApi.acceptInvitation({
const result = await this.api.acceptInvitation({
body: { invitationId: dto.invitationId },
headers: {
authorization: `Bearer ${dto.userToken}`,
@ -324,7 +323,7 @@ export class BetterAuthService {
try {
// Better Auth uses getFullOrganization to get org with members
// See: https://www.better-auth.com/docs/plugins/organization
const result = await this.orgApi.getFullOrganization({
const result = await this.api.getFullOrganization({
query: { organizationId },
});
@ -353,7 +352,7 @@ export class BetterAuthService {
// Better Auth organization plugin uses auth.api.removeMember
// Accepts memberIdOrEmail parameter
// See: https://www.better-auth.com/docs/plugins/organization
await this.orgApi.removeMember({
await this.api.removeMember({
body: {
memberIdOrEmail: dto.memberId,
organizationId: dto.organizationId,
@ -388,7 +387,7 @@ export class BetterAuthService {
try {
// Better Auth organization plugin uses auth.api.setActiveOrganization
// See: https://www.better-auth.com/docs/plugins/organization
const result = await this.orgApi.setActiveOrganization({
const result = await this.api.setActiveOrganization({
body: { organizationId: dto.organizationId },
headers: {
authorization: `Bearer ${dto.userToken}`,
@ -441,10 +440,8 @@ export class BetterAuthService {
// Generate JWT access token using Better Auth's JWT plugin
let accessToken = '';
try {
const api = this.auth.api as any;
// Use Better Auth's signJWT with the jwks table
const jwtResult = await api.signJWT({
const jwtResult = await this.api.signJWT({
body: {
payload: {
sub: user.id,
@ -537,7 +534,7 @@ export class BetterAuthService {
async signOut(token: string): Promise<SignOutResult> {
try {
// Better Auth uses auth.api.signOut
await (this.auth.api as any).signOut({
await this.api.signOut({
headers: {
authorization: `Bearer ${token}`,
},
@ -564,7 +561,7 @@ export class BetterAuthService {
async getSession(token: string): Promise<GetSessionResult> {
try {
// Better Auth uses auth.api.getSession
const result = await (this.auth.api as any).getSession({
const result = await this.api.getSession({
headers: {
authorization: `Bearer ${token}`,
},
@ -602,7 +599,7 @@ export class BetterAuthService {
*/
async listOrganizations(token: string): Promise<ListOrganizationsResult> {
try {
const result = await this.orgApi.listOrganizations({
const result = await this.api.listOrganizations({
headers: {
authorization: `Bearer ${token}`,
},
@ -633,7 +630,7 @@ export class BetterAuthService {
token?: string
): Promise<Organization & { members?: OrganizationMember[] }> {
try {
const result = await this.orgApi.getFullOrganization({
const result = await this.api.getFullOrganization({
query: { organizationId },
...(token && {
headers: {
@ -864,9 +861,9 @@ export class BetterAuthService {
redirectTo?: string
): Promise<{ success: boolean; message: string }> {
try {
// Better Auth's forgetPassword method
// Better Auth's requestPasswordReset method
// See: https://www.better-auth.com/docs/authentication/email-password#password-reset
await (this.auth.api as any).forgetPassword({
await this.api.requestPasswordReset({
body: {
email,
redirectTo,
@ -906,7 +903,7 @@ export class BetterAuthService {
try {
// Better Auth's resetPassword method
// See: https://www.better-auth.com/docs/authentication/email-password#password-reset
await (this.auth.api as any).resetPassword({
await this.api.resetPassword({
body: {
token,
newPassword,
@ -941,12 +938,9 @@ export class BetterAuthService {
*/
async getJwks(): Promise<{ keys: unknown[] }> {
try {
// Better Auth exposes JWKS via auth.api
const api = this.auth.api as any;
// Try to get JWKS from Better Auth
if (api.getJwks) {
const result = await api.getJwks();
const result = await this.api.getJwks();
if (result) {
return result;
}

View file

@ -268,11 +268,29 @@ export interface AuthenticatedRequest<TBody = unknown, TQuery = unknown> {
*
* This interface describes the methods available on auth.api
* when using the organization plugin.
*
* @see https://www.better-auth.com/docs/concepts/typescript
*/
export interface BetterAuthAPI {
// Core auth methods
signUpEmail(params: { body: SignUpEmailBody }): Promise<SignUpResponse>;
signInEmail(params: { body: { email: string; password: string } }): Promise<SignInResponse>;
signOut(params: AuthenticatedRequest): Promise<{ success: boolean }>;
getSession(
params: AuthenticatedRequest
): Promise<{ user: BetterAuthUser; session: BetterAuthSession }>;
// Password reset methods
requestPasswordReset(params: {
body: { email: string; redirectTo?: string };
}): Promise<{ status: boolean }>;
resetPassword(params: {
body: { newPassword: string; token: string };
}): Promise<{ status: boolean }>;
// JWT methods
signJWT(params: { body: { payload: Record<string, unknown> } }): Promise<{ token: string }>;
getJwks(): Promise<{ keys: unknown[] }>;
// Organization methods
createOrganization(