Skip to content

Commit

Permalink
[CA-35-15] Allow configuration of WebAuthn origin (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
rchapel authored May 6, 2024
1 parent 64bedc4 commit 04f60c8
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Add support for account recovery and passkey reset
- Make WebAuthn origin configurable

## [1.32.2] - 2023-03-21
- Add support for discoverable passkey login
Expand Down
20 changes: 15 additions & 5 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface Config {
domain: string
language?: string
locale?: string
webAuthnOrigin?: string
}

export type ApiClientConfig = {
Expand Down Expand Up @@ -135,7 +136,7 @@ export function createClient(creationConfig: Config): Client {
checkParam(creationConfig, 'domain')
checkParam(creationConfig, 'clientId')

const { domain, clientId, language, locale } = creationConfig
const { domain, clientId, language, locale, webAuthnOrigin } = creationConfig

const eventManager = createEventManager()

Expand Down Expand Up @@ -203,7 +204,7 @@ export function createClient(creationConfig: Config): Client {
)

function addNewWebAuthnDevice(accessToken: string, friendlyName?: string) {
return apiClients.then(clients => clients.webAuthn.addNewWebAuthnDevice(accessToken, friendlyName))
return apiClients.then(clients => clients.webAuthn.addNewWebAuthnDevice(accessToken, friendlyName, webAuthnOrigin))
}

function checkSession(options: AuthOptions = {}) {
Expand Down Expand Up @@ -275,7 +276,10 @@ export function createClient(creationConfig: Config): Client {
}

function loginWithWebAuthn(params: LoginWithWebAuthnParams) {
return apiClients.then(clients => clients.webAuthn.loginWithWebAuthn(params))
return apiClients.then(clients => clients.webAuthn.loginWithWebAuthn({
...params,
webAuthnOrigin
}))
}

function logout(params: LogoutParams = {}, revocationParams?: RevocationParams) {
Expand Down Expand Up @@ -324,7 +328,10 @@ export function createClient(creationConfig: Config): Client {
}

function resetPasskeys(params: ResetPasskeysParams) {
return apiClients.then(clients => clients.webAuthn.resetPasskeys(params))
return apiClients.then(clients => clients.webAuthn.resetPasskeys({
...params,
webAuthnOrigin
}))
}

function sendEmailVerification(params: EmailVerificationParams) {
Expand All @@ -340,7 +347,10 @@ export function createClient(creationConfig: Config): Client {
}

function signupWithWebAuthn(params: SignupWithWebAuthnParams, auth?: AuthOptions) {
return apiClients.then(clients => clients.webAuthn.signupWithWebAuthn(params, auth))
return apiClients.then(clients => clients.webAuthn.signupWithWebAuthn({
...params,
webAuthnOrigin
}, auth))
}

function startMfaEmailRegistration(params: StartMfaEmailRegistrationParams) {
Expand Down
25 changes: 14 additions & 11 deletions src/main/webAuthnClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import {
EmailLoginWithWebAuthnParams,
encodePublicKeyCredentialCreationOptions,
encodePublicKeyCredentialRequestOptions,
InternalLoginWithWebAuthnParams,
InternalSignupWithWebAuthnParams,
LoginWithWebAuthnParams,
PhoneNumberLoginWithWebAuthnParams,
publicKeyCredentialType,
RegistrationOptions,
serializeAuthenticationPublicKeyCredential,
serializeRegistrationPublicKeyCredential,
SignupWithWebAuthnParams
} from './webAuthnService'
import { ApiClientConfig } from './main'
import OAuthClient from './oAuthClient'
Expand All @@ -37,6 +38,8 @@ type SmsResetPasskeysParams = {

export type ResetPasskeysParams = EmailResetPasskeysParams | SmsResetPasskeysParams

export type InternalResetPasskeysParams = { webAuthnOrigin?: string } & ResetPasskeysParams

/**
* Identity Rest API Client
*/
Expand Down Expand Up @@ -78,10 +81,10 @@ export default class WebAuthnClient {
return (credentials as PublicKeyCredential).type === publicKeyCredentialType
}

addNewWebAuthnDevice(accessToken: string, friendlyName?: string): Promise<void> {
addNewWebAuthnDevice(accessToken: string, friendlyName?: string, webAuthnOrigin?: string): Promise<void> {
if (window.PublicKeyCredential) {
const body = {
origin: window.location.origin,
origin: webAuthnOrigin || window.location.origin,
friendlyName: friendlyName || window.navigator.platform
}

Expand Down Expand Up @@ -111,11 +114,11 @@ export default class WebAuthnClient {
}
}

resetPasskeys(params: ResetPasskeysParams): Promise<void> {
resetPasskeys(params: InternalResetPasskeysParams): Promise<void> {
if (window.PublicKeyCredential) {
const body = {
...params,
origin: window.location.origin,
origin: params.webAuthnOrigin || window.location.origin,
friendlyName: params.friendlyName || window.navigator.platform
}

Expand Down Expand Up @@ -155,16 +158,16 @@ export default class WebAuthnClient {
return typeof (params as DiscoverableLoginWithWebAuthnParams).conditionalMediation !== 'undefined'
}

private buildWebAuthnParams(params: LoginWithWebAuthnParams): Promise<LoginWithWebAuthnQueryParams> {
private buildWebAuthnParams(params: InternalLoginWithWebAuthnParams): Promise<LoginWithWebAuthnQueryParams> {
const body = this.isDiscoverable(params)
? {
clientId: this.config.clientId,
origin: window.location.origin,
origin: params.webAuthnOrigin || window.location.origin,
scope: resolveScope(params.auth, this.config.scope)
}
: {
clientId: this.config.clientId,
origin: window.location.origin,
origin: params.webAuthnOrigin || window.location.origin,
scope: resolveScope(params.auth, this.config.scope),
email: (params as EmailLoginWithWebAuthnParams).email,
phoneNumber: (params as PhoneNumberLoginWithWebAuthnParams).phoneNumber
Expand All @@ -181,7 +184,7 @@ export default class WebAuthnClient {
})
}

loginWithWebAuthn(params: LoginWithWebAuthnParams): Promise<AuthResult> {
loginWithWebAuthn(params: InternalLoginWithWebAuthnParams): Promise<AuthResult> {
if (!window.PublicKeyCredential) {
return Promise.reject(new Error('Unsupported WebAuthn API'))
}
Expand Down Expand Up @@ -231,10 +234,10 @@ export default class WebAuthnClient {
return this.http.remove<void>(`${this.registrationUrl}/${deviceId}`, { accessToken })
}

signupWithWebAuthn(params: SignupWithWebAuthnParams, auth?: AuthOptions): Promise<AuthResult> {
signupWithWebAuthn(params: InternalSignupWithWebAuthnParams, auth?: AuthOptions): Promise<AuthResult> {
if (window.PublicKeyCredential) {
const body = {
origin: window.location.origin,
origin: params.webAuthnOrigin || window.location.origin,
clientId: this.config.clientId,
friendlyName: params.friendlyName || window.navigator.platform,
profile: params.profile,
Expand Down
4 changes: 4 additions & 0 deletions src/main/webAuthnService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ export type LoginWithWebAuthnParams = { auth?: AuthOptions; signal?: AbortSignal
| DiscoverableLoginWithWebAuthnParams
)

export type InternalLoginWithWebAuthnParams = LoginWithWebAuthnParams & { webAuthnOrigin?: string }

export type SignupWithWebAuthnParams = {
profile: SignupProfileData
friendlyName?: string
redirectUrl?: string
returnToAfterEmailConfirmation?: string
}

export type InternalSignupWithWebAuthnParams = SignupWithWebAuthnParams & { webAuthnOrigin?: string }

export type RegistrationOptions = {
friendlyName: string
options: {
Expand Down

0 comments on commit 04f60c8

Please sign in to comment.