diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 87b81b2c9..fd1e90235 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -12,7 +12,14 @@ import { isAuthError, isAuthRetryableFetchError, } from './lib/errors' -import { Fetch, _request, _sessionResponse, _userResponse, _ssoResponse } from './lib/fetch' +import { + Fetch, + _request, + _sessionResponse, + _sessionResponsePassword, + _userResponse, + _ssoResponse, +} from './lib/fetch' import { decodeJWTPayload, Deferred, @@ -37,7 +44,9 @@ import { LockAcquireTimeoutError } from './lib/locks' import type { AuthChangeEvent, AuthResponse, + AuthResponsePassword, AuthTokenResponse, + AuthTokenResponsePassword, AuthOtpResponse, CallRefreshTokenResult, GoTrueClientOptions, @@ -78,6 +87,7 @@ import type { AuthFlowType, LockFunc, UserIdentity, + WeakPassword, } from './lib/types' polyfillGlobalThis() // Make "globalThis" available @@ -430,11 +440,13 @@ export default class GoTrueClient { * email/phone and password combination is wrong or that the account can only * be accessed via social login. */ - async signInWithPassword(credentials: SignInWithPasswordCredentials): Promise { + async signInWithPassword( + credentials: SignInWithPasswordCredentials + ): Promise { try { await this._removeSession() - let res: AuthResponse + let res: AuthResponsePassword if ('email' in credentials) { const { email, password, options } = credentials res = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=password`, { @@ -444,7 +456,7 @@ export default class GoTrueClient { password, gotrue_meta_security: { captcha_token: options?.captchaToken }, }, - xform: _sessionResponse, + xform: _sessionResponsePassword, }) } else if ('phone' in credentials) { const { phone, password, options } = credentials @@ -455,7 +467,7 @@ export default class GoTrueClient { password, gotrue_meta_security: { captcha_token: options?.captchaToken }, }, - xform: _sessionResponse, + xform: _sessionResponsePassword, }) } else { throw new AuthInvalidCredentialsError( @@ -473,7 +485,14 @@ export default class GoTrueClient { await this._saveSession(data.session) await this._notifyAllSubscribers('SIGNED_IN', data.session) } - return { data: { user: data.user, session: data.session }, error } + return { + data: { + user: data.user, + session: data.session, + ...(data.weak_password ? { weakPassword: data.weak_password } : null), + }, + error, + } } catch (error) { if (isAuthError(error)) { return { data: { user: null, session: null }, error } diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 5c1352268..2e4336e22 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -1,3 +1,5 @@ +import { WeakPasswordReasons } from './types' + export class AuthError extends Error { status: number | undefined protected __isAuthError = true @@ -134,7 +136,7 @@ export class AuthWeakPasswordError extends CustomAuthError { /** * Reasons why the password is deemed weak. */ - reasons: ('length' | 'characters' | 'pwned' | string)[] + reasons: WeakPasswordReasons[] constructor(message: string, status: number, reasons: string[]) { super(message, 'AuthWeakPasswordError', status) diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index 7a63d1834..3bc4ef57b 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -1,6 +1,7 @@ import { expiresAt, looksLikeFetchResponse } from './helpers' import { AuthResponse, + AuthResponsePassword, SSOResponse, GenerateLinkProperties, GenerateLinkResponse, @@ -174,6 +175,25 @@ export function _sessionResponse(data: any): AuthResponse { return { data: { session, user }, error: null } } +export function _sessionResponsePassword(data: any): AuthResponsePassword { + const response = _sessionResponse(data) as AuthResponsePassword + + if ( + !response.error && + data.weak_password && + typeof data.weak_password === 'object' && + Array.isArray(data.weak_password.reasons) && + data.weak_password.reasons.length && + data.weak_password.message && + typeof data.weak_password.message === 'string' && + data.weak_password.reasons.reduce((a: boolean, i: any) => a && typeof i === 'string', true) + ) { + response.data.weak_password = data.weak_password + } + + return response +} + export function _userResponse(data: any): UserResponse { const user: User = data.user ?? (data as User) return { data: { user }, error: null } diff --git a/src/lib/types.ts b/src/lib/types.ts index 07f080d7e..fbe30a6f1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -82,6 +82,12 @@ export type GoTrueClientOptions = { lock?: LockFunc } +export type WeakPasswordReasons = 'length' | 'characters' | 'pwned' | string +export type WeakPassword = { + reasons: WeakPasswordReasons[] + message: string +} + export type AuthResponse = | { data: { @@ -98,6 +104,23 @@ export type AuthResponse = error: AuthError } +export type AuthResponsePassword = + | { + data: { + user: User | null + session: Session | null + weak_password?: WeakPassword | null + } + error: null + } + | { + data: { + user: null + session: null + } + error: AuthError + } + /** * AuthOtpResponse is returned when OTP is used. * @@ -129,6 +152,24 @@ export type AuthTokenResponse = error: AuthError } +export type AuthTokenResponsePassword = + | { + data: { + user: User + session: Session + weakPassword?: WeakPassword + } + error: null + } + | { + data: { + user: null + session: null + weakPassword?: null + } + error: AuthError + } + export type OAuthResponse = | { data: {