Email auth factor (#3602)

* Add email 2fa toggle

* Add UI elements needed for 2fa codes in login

* Wire up to the server

* Give a better failure message for bad 2fa code

* Handle enter key in login form 2fa field

* Trim spaces

* Improve error message
This commit is contained in:
Paul Frazee 2024-04-22 19:18:13 -07:00 committed by GitHub
parent cbb817b5b7
commit 710e913024
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 363 additions and 20 deletions

View file

@ -107,6 +107,7 @@ export interface PostLanguagesSettingsModal {
export interface VerifyEmailModal {
name: 'verify-email'
showReminder?: boolean
onSuccess?: () => void
}
export interface ChangeEmailModal {

View file

@ -11,6 +11,7 @@ const accountSchema = z.object({
handle: z.string(),
email: z.string().optional(),
emailConfirmed: z.boolean().optional(),
emailAuthFactor: z.boolean().optional(),
refreshJwt: z.string().optional(), // optional because it can expire
accessJwt: z.string().optional(), // optional because it can expire
deactivated: z.boolean().optional(),

View file

@ -59,6 +59,7 @@ export type ApiContext = {
service: string
identifier: string
password: string
authFactorToken?: string | undefined
},
logContext: LogEvents['account:loggedIn']['logContext'],
) => Promise<void>
@ -87,7 +88,10 @@ export type ApiContext = {
) => Promise<void>
updateCurrentAccount: (
account: Partial<
Pick<SessionAccount, 'handle' | 'email' | 'emailConfirmed'>
Pick<
SessionAccount,
'handle' | 'email' | 'emailConfirmed' | 'emailAuthFactor'
>
>,
) => void
}
@ -298,12 +302,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
const login = React.useCallback<ApiContext['login']>(
async ({service, identifier, password}, logContext) => {
async ({service, identifier, password, authFactorToken}, logContext) => {
logger.debug(`session: login`, {}, logger.DebugContext.session)
const agent = new BskyAgent({service})
await agent.login({identifier, password})
await agent.login({identifier, password, authFactorToken})
if (!agent.session) {
throw new Error(`session: login failed to establish a session`)
@ -319,6 +323,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
handle: agent.session.handle,
email: agent.session.email,
emailConfirmed: agent.session.emailConfirmed || false,
emailAuthFactor: agent.session.emailAuthFactor,
refreshJwt: agent.session.refreshJwt,
accessJwt: agent.session.accessJwt,
deactivated: isSessionDeactivated(agent.session.accessJwt),
@ -489,6 +494,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
handle: agent.session.handle,
email: agent.session.email,
emailConfirmed: agent.session.emailConfirmed || false,
emailAuthFactor: agent.session.emailAuthFactor || false,
refreshJwt: agent.session.refreshJwt,
accessJwt: agent.session.accessJwt,
deactivated: isSessionDeactivated(agent.session.accessJwt),
@ -546,6 +552,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
account.emailConfirmed !== undefined
? account.emailConfirmed
: currentAccount.emailConfirmed,
emailAuthFactor:
account.emailAuthFactor !== undefined
? account.emailAuthFactor
: currentAccount.emailAuthFactor,
}
return {