Skip to content

Commit

Permalink
feat: return weakPassword information after sign-in (#824)
Browse files Browse the repository at this point in the history
Returns the weak password reasons on sign-in.
  • Loading branch information
hf authored Dec 18, 2023
1 parent 6d6ec13 commit 280d908
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 7 deletions.
31 changes: 25 additions & 6 deletions src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -37,7 +44,9 @@ import { LockAcquireTimeoutError } from './lib/locks'
import type {
AuthChangeEvent,
AuthResponse,
AuthResponsePassword,
AuthTokenResponse,
AuthTokenResponsePassword,
AuthOtpResponse,
CallRefreshTokenResult,
GoTrueClientOptions,
Expand Down Expand Up @@ -78,6 +87,7 @@ import type {
AuthFlowType,
LockFunc,
UserIdentity,
WeakPassword,
} from './lib/types'

polyfillGlobalThis() // Make "globalThis" available
Expand Down Expand Up @@ -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<AuthTokenResponse> {
async signInWithPassword(
credentials: SignInWithPasswordCredentials
): Promise<AuthTokenResponsePassword> {
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`, {
Expand All @@ -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
Expand All @@ -455,7 +467,7 @@ export default class GoTrueClient {
password,
gotrue_meta_security: { captcha_token: options?.captchaToken },
},
xform: _sessionResponse,
xform: _sessionResponsePassword,
})
} else {
throw new AuthInvalidCredentialsError(
Expand All @@ -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 }
Expand Down
4 changes: 3 additions & 1 deletion src/lib/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { WeakPasswordReasons } from './types'

export class AuthError extends Error {
status: number | undefined
protected __isAuthError = true
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions src/lib/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expiresAt, looksLikeFetchResponse } from './helpers'
import {
AuthResponse,
AuthResponsePassword,
SSOResponse,
GenerateLinkProperties,
GenerateLinkResponse,
Expand Down Expand Up @@ -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 }
Expand Down
41 changes: 41 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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.
*
Expand Down Expand Up @@ -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: {
Expand Down

0 comments on commit 280d908

Please sign in to comment.