Skip to content

Commit

Permalink
rp.id => domain
Browse files Browse the repository at this point in the history
  • Loading branch information
dagnelies committed Apr 18, 2024
1 parent 321bc88 commit 78612fd
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 16 deletions.
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import * as webauthn from '@passwordless-id/webauthn'

```html
<script type="module">
import { client } from 'https://unpkg.com/@passwordless-id/[email protected].0/dist/webauthn.min.js'
import { client } from 'https://unpkg.com/@passwordless-id/[email protected].1/dist/webauthn.min.js'
</script>
```
### Import
Expand Down Expand Up @@ -391,6 +391,10 @@ The following options are available for both `register` and `authenticate`.
- `'local'`: use the local device (using TouchID, FaceID, Windows Hello or PIN)
- `'roaming'`: use a roaming device (security key or connected phone)
- `'both'`: prompt the user to choose between local or roaming device. The UI and user interaction in this case is platform specific.
- `domain`: by default, the current domain name is used. Also known as "relying party id". You may want to customize it for ...
- a parent domain to let the credential work on all subdomains
- browser extensions requiring specific IDs instead of domains ?
- specific iframes use cases?
- `debug`: If enabled, parses the "data" objects and provide it in a "debug" properties.


Expand All @@ -400,17 +404,23 @@ Registration options
- `discoverable`: (`'discouraged'`, `'preferred'` or `'required'`) If the credential is "discoverable", it can be selected using `authenticate` without providing credential IDs. In that case, a native pop-up will appear for user selection. This may have an impact on the "passkeys" user experience and syncing behavior of the key. *(Default: 'preferred')*
- `attestation`: If enabled, the device attestation and clientData will be provided as base64 encoded binary data. Note that this may impact the authenticator information available or the UX depending on the platform. *(Default: false)*
- `userHandle`: The user "handle" (also known as user "id") can be used to re-register credentials for an existing user, thus overriding the current credential key pair and username for that `userHandle`. *The default here is based on a hash of the `username`, and thus has some security implications as described in [issue](https://github.com/passwordless-id/webauthn/issues/29).*
- `rp:{id:..., name: ...}`: By default the domain name is used as relying party ID and name. However, there are uses cases where you may want to customize it:
- defining a parent domain to let the credential work on all subdomains
- for browser extensions requiring specific IDs
- for specific iframes use cases?


Authentication options
----------------------

- `mediation`: See https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get#mediation


Verification options
--------------------

- `userVerified`: to ensure that the user has been verified by the authenticator
- `counter`: this should be an incrementing value on each authentication, but it was made optional according to https://github.com/passwordless-id/webauthn/issues/38
- `domain`: in case you used a specific domain (relying party id) during registration/authentication, you need this too during verification
- `verbose`: prints more details to the console if enabled


Parsing data
------------

Expand Down
2 changes: 1 addition & 1 deletion demos/example-cdn.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


<script type="module">
import { client } from 'https://unpkg.com/@passwordless-id/[email protected].0/dist/webauthn.min.js'
import { client } from 'https://unpkg.com/@passwordless-id/[email protected].1/dist/webauthn.min.js'

window.register = async function() {
console.log('Registering...')
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@passwordless-id/webauthn",
"version": "1.6.0",
"version": "1.6.1",
"description": "A small wrapper around the webauthn protocol to make one's life easier.",
"type": "module",
"main": "dist/esm/index.js",
Expand Down
8 changes: 4 additions & 4 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ export async function register(username :string, challenge :string, options? :Re

const creationOptions :PublicKeyCredentialCreationOptions = {
challenge: utils.parseBase64url(challenge),
rp: options.rp ?? {
id: window.location.hostname,
name: window.location.hostname
rp: {
id: options.domain ?? window.location.hostname,
name: options.domain ?? window.location.hostname
},
user: {
id: options.userHandle ? utils.toBuffer(options.userHandle) : await utils.sha256(new TextEncoder().encode('passwordless.id-user:' + username)), // ID should not be directly "identifiable" for privacy concerns
Expand Down Expand Up @@ -177,7 +177,7 @@ export async function authenticate(credentialIds :string[], challenge :string, o

let authOptions :PublicKeyCredentialRequestOptions = {
challenge: utils.parseBase64url(challenge),
rpId: window.location.hostname,
rpId: options.domain ?? window.location.hostname,
allowCredentials: credentialIds.map(id => { return {
id: utils.parseBase64url(id),
type: 'public-key',
Expand Down
5 changes: 4 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface AuthenticationChecks {
origin: string | Function,
userVerified: boolean,
counter?: number, // Made optional according to https://github.com/passwordless-id/webauthn/issues/38
domain ?:string, // Same as `rp.id`
verbose?: boolean
}

Expand All @@ -67,6 +68,8 @@ export async function verifyAuthentication(authenticationRaw: AuthenticationEnco
throw new Error(`Invalid signature: ${authenticationRaw.signature}`)

const authentication = parseAuthentication(authenticationRaw)
if(expected.verbose)
console.debug(authentication)

if (authentication.client.type !== "webauthn.get")
throw new Error(`Unexpected clientData type: ${authentication.client.type}`)
Expand All @@ -78,7 +81,7 @@ export async function verifyAuthentication(authenticationRaw: AuthenticationEnco
throw new Error(`Unexpected ClientData challenge: ${authentication.client.challenge}`)

// this only works because we consider `rp.origin` and `rp.id` to be the same during authentication/registration
const rpId = new URL(authentication.client.origin).hostname
const rpId = expected.domain ?? new URL(authentication.client.origin).hostname
const expectedRpIdHash = utils.toBase64url(await utils.sha256(utils.toBuffer(rpId)))
if (authentication.authenticator.rpIdHash !== expectedRpIdHash)
throw new Error(`Unexpected RpIdHash: ${authentication.authenticator.rpIdHash} vs ${expectedRpIdHash}`)
Expand Down
5 changes: 1 addition & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type NamedAlgo = 'RS256' | 'ES256'


export interface CommonOptions {
domain ?:string // used for parent/subdomain auth and other exotic use cases
userVerification ?:UserVerificationRequirement
authenticatorType ?:AuthType
timeout ?:number
Expand Down Expand Up @@ -42,10 +43,6 @@ export interface RegisterOptions extends CommonOptions {
userHandle?: string
attestation?: boolean
discoverable?: ResidentKeyRequirement
rp?: {
id :string,
name :string
}
}


Expand Down

0 comments on commit 78612fd

Please sign in to comment.