diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 9f13a16a3..8309a629a 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -87,6 +87,7 @@ import type { LockFunc, UserIdentity, SignInAnonymouslyCredentials, + WebAuthnResponse, } from './lib/types' polyfillGlobalThis() // Make "globalThis" available @@ -2383,6 +2384,16 @@ export default class GoTrueClient { } } + // TODO: find less hacky way + private hasPublicKey( + params: MFAVerifyParams + ): params is { factorId: string; challengeId: string; publicKey: WebAuthnResponse } { + return ( + (params as { factorId: string; challengeId: string; publicKey: WebAuthnResponse }) + .publicKey !== undefined + ) + } + /** * {@see GoTrueMFAApi#verify} */ @@ -2394,17 +2405,32 @@ export default class GoTrueClient { if (sessionError) { return { data: null, error: sessionError } } - - const { data, error } = await _request( - this.fetch, - 'POST', - `${this.url}/factors/${params.factorId}/verify`, - { - body: { code: params.code, challenge_id: params.challengeId }, - headers: this.headers, - jwt: sessionData?.session?.access_token, - } - ) + let response: { data: any; error: any } = { data: null, error: null } + if (this.hasPublicKey(params)) { + response = await _request( + this.fetch, + 'POST', + `${this.url}/factors/${params.factorId}/verify`, + { + body: { challenge_id: params.challengeId, ...params?.publicKey }, + headers: this.headers, + jwt: sessionData?.session?.access_token, + } + ) + } else { + response = await _request( + this.fetch, + 'POST', + `${this.url}/factors/${params.factorId}/verify`, + { + body: { code: params.code, challenge_id: params.challengeId }, + headers: this.headers, + jwt: sessionData?.session?.access_token, + } + ) + } + const { data, error } = response + console.log(data) if (error) { return { data: null, error } } @@ -2463,6 +2489,8 @@ export default class GoTrueClient { private async _challengeAndVerify( params: MFAChallengeAndVerifyParams ): Promise { + // TODO: Maybe restrict this for SMS and Webauthn?:w + // both _challenge and _verify independently acquire the lock, so no need // to acquire it here diff --git a/src/lib/types.ts b/src/lib/types.ts index 246bf5190..7699f0bfc 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -819,16 +819,43 @@ export type MFAUnenrollParams = { factorId: string } -export type MFAVerifyParams = { - /** ID of the factor being verified. Returned in enroll(). */ - factorId: string +export type WebAuthnResponse = { + id: string + rawId: string + response: { + attestationObject: string + clientDataJSON: string + transports: string[] + publicKeyAlgorithm: number + publicKey: string + authenticatorData: string + } + type: string + clientExtensionResults: Record + authenticatorAttachment: string +} - /** ID of the challenge being verified. Returned in challenge(). */ - challengeId: string +export type MFAVerifyParams = + | { + /** ID of the factor being verified. Returned in enroll(). */ + factorId: string - /** Verification code provided by the user. */ - code: string -} + /** ID of the challenge being verified. Returned in challenge(). */ + challengeId: string + + /** Verification code provided by the user. */ + code: string + } + | { + /** ID of the factor being verified. Returned in enroll(). */ + factorId: string + + /** ID of the challenge being verified. Returned in challenge(). */ + challengeId: string + + /** Webauthn Response */ + publicKey: WebAuthnResponse + } export type MFAChallengeParams = { /** ID of the factor to be challenged. Returned in enroll(). */ @@ -933,7 +960,7 @@ export type AuthMFAChallengeResponse = } | { data: { - // TODO: Get this from simplewebauthn types + // TODO: make thismore specific public_key_credential_request_options: Object id: string }