Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add bindings for Multi-Factor Authentication (Phone) #932

Merged
merged 11 commits into from
Aug 9, 2024
24 changes: 18 additions & 6 deletions src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2341,12 +2341,14 @@ export default class GoTrueClient {
return { data: null, error: sessionError }
}

const body = {
friendly_name: params.friendlyName,
factor_type: params.factorType,
...(params.factorType === 'phone' ? { phone: params.phone } : { issuer: params.issuer }),
}

const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors`, {
body: {
friendly_name: params.friendlyName,
factor_type: params.factorType,
issuer: params.issuer,
},
body,
headers: this.headers,
jwt: sessionData?.session?.access_token,
})
Expand All @@ -2355,7 +2357,12 @@ export default class GoTrueClient {
return { data: null, error }
}

if (data?.totp?.qr_code) {
// TODO: Remove once: https://github.com/supabase/auth/pull/1717 is deployed
J0 marked this conversation as resolved.
Show resolved Hide resolved
if (params.factorType === 'phone') {
delete data.totp
}

if (params.factorType === 'totp' && data?.totp?.qr_code) {
data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`
}

Expand Down Expand Up @@ -2429,6 +2436,7 @@ export default class GoTrueClient {
'POST',
`${this.url}/factors/${params.factorId}/challenge`,
{
body: { channel: params.channel },
headers: this.headers,
jwt: sessionData?.session?.access_token,
}
Expand Down Expand Up @@ -2483,11 +2491,15 @@ export default class GoTrueClient {
const totp = factors.filter(
(factor) => factor.factor_type === 'totp' && factor.status === 'verified'
)
const phone = factors.filter(
(factor) => factor.factor_type === 'phone' && factor.status === 'verified'
)

return {
data: {
all: factors,
totp,
phone,
},
error: null,
}
Expand Down
59 changes: 43 additions & 16 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,9 @@ export interface Factor {
friendly_name?: string

/**
* Type of factor. Only `totp` supported with this version but may change in
* future versions.
* Type of factor. `totp` and `phone` supported with this version
*/
factor_type: 'totp' | string
factor_type: 'totp' | 'phone' | string

/** Factor's status. */
status: 'verified' | 'unverified'
Expand Down Expand Up @@ -471,10 +470,6 @@ export interface Subscription {
unsubscribe: () => void
}

export interface UpdatableFactorAttributes {
friendlyName: string
}

export type SignInAnonymouslyCredentials = {
options?: {
/**
Expand Down Expand Up @@ -805,14 +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
}
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 MFAUnenrollParams = {
/** ID of the factor being unenrolled. */
Expand All @@ -833,6 +837,8 @@ export type MFAVerifyParams = {
export type MFAChallengeParams = {
/** ID of the factor to be challenged. Returned in enroll(). */
factorId: string
/** Messaging channel to use (e.g. whatsapp or sms). Only relevant for phone factors */
channel?: 'sms' | 'whatsapp'
}

export type MFAChallengeAndVerifyParams = {
J0 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -873,7 +879,7 @@ export type AuthMFAEnrollResponse =
/** ID of the factor that was just enrolled (in an unverified state). */
id: string

/** Type of MFA factor. Only `totp` supported for now. */
/** Type of MFA factor.*/
type: 'totp'

/** TOTP enrollment information. */
Expand All @@ -897,6 +903,22 @@ export type AuthMFAEnrollResponse =
}
error: null
}
| {
data: {
/** ID of the factor that was just enrolled (in an unverified state). */
id: string

/** Type of MFA factor. */
type: 'phone'

/** Friendly name of the factor, useful for distinguishing between factors **/
friendly_name?: string

/** Phone number of the MFA factor. Used to send messages */
J0 marked this conversation as resolved.
Show resolved Hide resolved
phone_number: string
J0 marked this conversation as resolved.
Show resolved Hide resolved
}
error: null
}
| {
data: null
error: AuthError
Expand All @@ -918,6 +940,9 @@ export type AuthMFAChallengeResponse =
/** ID of the newly created challenge. */
id: string

/** Factor Type which generated the challenge */
type: 'totp' | 'phone'

/** Timestamp in UNIX seconds when this challenge will no longer be usable. */
expires_at: number
}
Expand All @@ -933,6 +958,8 @@ export type AuthMFAListFactorsResponse =

/** Only verified TOTP factors. (A subset of `all`.) */
totp: Factor[]
/** Only verified Phone factors. (A subset of `all`.) */
phone: Factor[]
}
error: null
}
Expand Down