From 01ba4ffd53113ffccc01461eb053e68e9a9b8a35 Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 17 Sep 2024 02:00:32 +0900 Subject: [PATCH] fix: improve `mfa.enroll` return types Use TypeScript's operator overloading to return - TOTP data when `mfa.enroll` was passed `factorType: 'totp'`, - phone data when `mfa.enroll` was passed `factorType: 'phone'`. This fixes a breaking change in the types in b957c30782065e4cc421a526c62c101d35c443d4. --- src/GoTrueClient.ts | 11 ++++ src/lib/types.ts | 135 ++++++++++++++++++++------------------ test/GoTrueClient.test.ts | 14 ++++ 3 files changed, 97 insertions(+), 63 deletions(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 7a5e4f573..c105be617 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -88,6 +88,11 @@ import type { LockFunc, UserIdentity, SignInAnonymouslyCredentials, + MFAEnrollTOTPParams, + AuthMFAEnrollTOTPResponse, + AuthMFAEnrollErrorResponse, + MFAEnrollPhoneParams, + AuthMFAEnrollPhoneResponse, } from './lib/types' polyfillGlobalThis() // Make "globalThis" available @@ -2353,6 +2358,12 @@ export default class GoTrueClient { /** * {@see GoTrueMFAApi#enroll} */ + private async _enroll( + params: MFAEnrollTOTPParams + ): Promise + private async _enroll( + params: MFAEnrollPhoneParams + ): Promise private async _enroll(params: MFAEnrollParams): Promise { try { return await this._useSession(async (result) => { diff --git a/src/lib/types.ts b/src/lib/types.ts index fa24eca5b..a2b9c3c30 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -800,23 +800,23 @@ export type GenerateLinkType = | 'email_change_current' | 'email_change_new' -export type MFAEnrollParams = - | { - /** The type of factor being enrolled. */ - factorType: 'totp' - /** Domain which the user is enrolled with. */ - issuer?: string - /** Human readable name assigned to the factor. */ - friendlyName?: string - } - | { - /** The type of factor being enrolled. */ - factorType: 'phone' - /** Human readable name assigned to the factor. */ - friendlyName?: string - /** Phone number associated with a factor. Number should conform to E.164 format */ - phone: string - } +export type MFAEnrollTOTPParams = { + /** The type of factor being enrolled. */ + factorType: 'totp' + /** Domain which the user is enrolled with. */ + issuer?: string + /** Human readable name assigned to the factor. */ + friendlyName?: string +} +export type MFAEnrollPhoneParams = { + /** The type of factor being enrolled. */ + factorType: 'phone' + /** Human readable name assigned to the factor. */ + friendlyName?: string + /** Phone number associated with a factor. Number should conform to E.164 format */ + phone: string +} +export type MFAEnrollParams = MFAEnrollTOTPParams | MFAEnrollPhoneParams export type MFAUnenrollParams = { /** ID of the factor being unenrolled. */ @@ -873,56 +873,59 @@ export type AuthMFAVerifyResponse = error: AuthError } -export type AuthMFAEnrollResponse = - | { - data: { - /** ID of the factor that was just enrolled (in an unverified state). */ - id: string - - /** Type of MFA factor.*/ - type: 'totp' - - /** TOTP enrollment information. */ - totp: { - /** Contains a QR code encoding the authenticator URI. You can - * convert it to a URL by prepending `data:image/svg+xml;utf-8,` to - * the value. Avoid logging this value to the console. */ - qr_code: string - - /** The TOTP secret (also encoded in the QR code). Show this secret - * in a password-style field to the user, in case they are unable to - * scan the QR code. Avoid logging this value to the console. */ - secret: string - - /** The authenticator URI encoded within the QR code, should you need - * to use it. Avoid loggin this value to the console. */ - uri: string - } - /** Friendly name of the factor, useful for distinguishing between factors **/ - friendly_name?: string - } - error: null +export type AuthMFAEnrollTOTPResponse = { + data: { + /** ID of the factor that was just enrolled (in an unverified state). */ + id: string + + /** Type of MFA factor.*/ + type: 'totp' + + /** TOTP enrollment information. */ + totp: { + /** Contains a QR code encoding the authenticator URI. You can + * convert it to a URL by prepending `data:image/svg+xml;utf-8,` to + * the value. Avoid logging this value to the console. */ + qr_code: string + + /** The TOTP secret (also encoded in the QR code). Show this secret + * in a password-style field to the user, in case they are unable to + * scan the QR code. Avoid logging this value to the console. */ + secret: string + + /** The authenticator URI encoded within the QR code, should you need + * to use it. Avoid loggin this value to the console. */ + uri: string } - | { - data: { - /** ID of the factor that was just enrolled (in an unverified state). */ - id: string + /** Friendly name of the factor, useful for distinguishing between factors **/ + friendly_name?: string + } + error: null +} +export type AuthMFAEnrollPhoneResponse = { + data: { + /** ID of the factor that was just enrolled (in an unverified state). */ + id: string - /** Type of MFA factor. */ - type: 'phone' + /** Type of MFA factor. */ + type: 'phone' - /** Friendly name of the factor, useful for distinguishing between factors **/ - friendly_name?: string + /** Friendly name of the factor, useful for distinguishing between factors **/ + friendly_name?: string - /** Phone number of the MFA factor in E.164 format. Used to send messages */ - phone: string - } - error: null - } - | { - data: null - error: AuthError - } + /** Phone number of the MFA factor in E.164 format. Used to send messages */ + phone: string + } + error: null +} +export type AuthMFAEnrollErrorResponse = { + data: null + error: AuthError +} +export type AuthMFAEnrollResponse = + | AuthMFAEnrollTOTPResponse + | AuthMFAEnrollPhoneResponse + | AuthMFAEnrollErrorResponse export type AuthMFAUnenrollResponse = | { @@ -1007,6 +1010,12 @@ export interface GoTrueMFAApi { * Upon verifying a factor, all other sessions are logged out and the current session's authenticator level is promoted to `aal2`. * */ + enroll( + params: MFAEnrollTOTPParams + ): Promise + enroll( + params: MFAEnrollPhoneParams + ): Promise enroll(params: MFAEnrollParams): Promise /** diff --git a/test/GoTrueClient.test.ts b/test/GoTrueClient.test.ts index 6378ae1c4..7d426513d 100644 --- a/test/GoTrueClient.test.ts +++ b/test/GoTrueClient.test.ts @@ -904,6 +904,20 @@ describe('User management', () => { }) }) +describe('MFA', () => { + test('enroll({factorType: "totp"}) returns totp', async () => { + const { data, error } = await authWithSession.mfa.enroll({ + factorType: 'totp', + }) + + if (error) { + throw error + } + + expect(data.totp.qr_code).not.toBeNull() + }) +}) + describe('GoTrueClient with storageisServer = true', () => { const originalWarn = console.warn let warnings: any[][] = []