feat(dev): add u2f ui for managing devices and signing in (#2182)
* feat: add u2f ui for managing devices and signing in * refactor: change unnecessary useState to derived constant * fix: modal refactor * fix(web): hide u2f under feature trunk * fix(web): jest setup --------- Co-authored-by: Aman Harwara <amanharwara@protonmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { Username, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface AuthenticatorClientInterface {
|
||||
list(): Promise<Array<{ id: string; name: string }>>
|
||||
@@ -9,6 +9,5 @@ export interface AuthenticatorClientInterface {
|
||||
name: string,
|
||||
registrationCredential: Record<string, unknown>,
|
||||
): Promise<boolean>
|
||||
generateAuthenticationOptions(): Promise<Record<string, unknown> | null>
|
||||
verifyAuthenticationResponse(userUuid: Uuid, authenticationCredential: Record<string, unknown>): Promise<boolean>
|
||||
generateAuthenticationOptions(username: Username): Promise<Record<string, unknown> | null>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { AuthenticatorApiServiceInterface } from '@standardnotes/api'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { Username, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
@@ -79,9 +79,9 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
|
||||
}
|
||||
}
|
||||
|
||||
async generateAuthenticationOptions(): Promise<Record<string, unknown> | null> {
|
||||
async generateAuthenticationOptions(username: Username): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const result = await this.authenticatorApiService.generateAuthenticationOptions()
|
||||
const result = await this.authenticatorApiService.generateAuthenticationOptions(username.value)
|
||||
|
||||
if (result.data.error) {
|
||||
return null
|
||||
@@ -92,24 +92,4 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async verifyAuthenticationResponse(
|
||||
userUuid: Uuid,
|
||||
authenticationCredential: Record<string, unknown>,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.authenticatorApiService.verifyAuthenticationResponse(
|
||||
userUuid.value,
|
||||
authenticationCredential,
|
||||
)
|
||||
|
||||
if (result.data.error) {
|
||||
return false
|
||||
}
|
||||
|
||||
return result.data.success
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ChallengePrompt implements ChallengePromptInterface {
|
||||
public readonly secureTextEntry = true,
|
||||
public readonly keyboardType?: ChallengeKeyboardType,
|
||||
public readonly initialValue?: ChallengeRawValue,
|
||||
public readonly contextData?: Record<string, unknown>,
|
||||
) {
|
||||
switch (this.validation) {
|
||||
case ChallengeValidation.AccountPassword:
|
||||
@@ -37,6 +38,11 @@ export class ChallengePrompt implements ChallengePromptInterface {
|
||||
this.placeholder = placeholder ?? ''
|
||||
this.validates = true
|
||||
break
|
||||
case ChallengeValidation.Authenticator:
|
||||
this.title = title ?? ChallengePromptTitle.U2F
|
||||
this.placeholder = placeholder ?? ''
|
||||
this.validates = true
|
||||
break
|
||||
case ChallengeValidation.ProtectionSessionDuration:
|
||||
this.title = title ?? ChallengePromptTitle.RememberFor
|
||||
this.placeholder = placeholder ?? ''
|
||||
|
||||
@@ -6,4 +6,5 @@ export const ChallengePromptTitle = {
|
||||
Biometrics: 'Biometrics',
|
||||
RememberFor: 'Remember For',
|
||||
Mfa: 'Two-factor Authentication Code',
|
||||
U2F: 'Security Key',
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export type ChallengeRawValue = number | string | boolean
|
||||
export type ChallengeRawValue = number | string | boolean | Record<string, unknown>
|
||||
|
||||
@@ -6,4 +6,5 @@ export enum ChallengeValidation {
|
||||
AccountPassword = 2,
|
||||
Biometric = 3,
|
||||
ProtectionSessionDuration = 4,
|
||||
Authenticator = 5,
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ export const SessionStrings = {
|
||||
},
|
||||
SessionRestored: 'Your session has been successfully restored.',
|
||||
EnterMfa: 'Please enter your two-factor authentication code.',
|
||||
InputU2FDevice: 'Please authenticate with your U2F device.',
|
||||
MfaInputPlaceholder: 'Two-factor authentication code',
|
||||
EmailInputPlaceholder: 'Email',
|
||||
PasswordInputPlaceholder: 'Password',
|
||||
|
||||
Reference in New Issue
Block a user