accounts
Version:
Tempo Accounts SDK
152 lines (131 loc) • 5.31 kB
text/typescript
import { describe, expect, test } from 'vp/test'
import * as WebAuthnCeremony from './WebAuthnCeremony.js'
describe('from', () => {
test('default: returns the ceremony', () => {
const ceremony = WebAuthnCeremony.from({
getRegistrationOptions: async () => ({ options: {} as any }),
verifyRegistration: async () => ({ credentialId: 'cred-1', publicKey: '0x1234' }),
getAuthenticationOptions: async () => ({ options: {} as any }),
verifyAuthentication: async () => ({ credentialId: 'cred-1', publicKey: '0x1234' }),
})
expect(ceremony).toBeDefined()
expect(typeof ceremony.getRegistrationOptions).toMatchInlineSnapshot(`"function"`)
expect(typeof ceremony.verifyRegistration).toMatchInlineSnapshot(`"function"`)
expect(typeof ceremony.getAuthenticationOptions).toMatchInlineSnapshot(`"function"`)
expect(typeof ceremony.verifyAuthentication).toMatchInlineSnapshot(`"function"`)
})
})
describe('local', () => {
test('default: creates a ceremony', () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
expect(ceremony).toBeDefined()
})
test('behavior: getRegistrationOptions returns serialized options', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
const { options } = await ceremony.getRegistrationOptions({ name: 'Test' })
expect(options.publicKey).toBeDefined()
expect(options.publicKey!.rp.id).toMatchInlineSnapshot(`"example.com"`)
expect(options.publicKey!.rp.name).toMatchInlineSnapshot(`"example.com"`)
expect(typeof options.publicKey!.challenge).toMatchInlineSnapshot(`"string"`)
expect(options.publicKey!.authenticatorSelection).toBeDefined()
})
test('behavior: getAuthenticationOptions returns serialized options', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
const { options } = await ceremony.getAuthenticationOptions()
expect(options.publicKey).toBeDefined()
expect(options.publicKey!.rpId).toMatchInlineSnapshot(`"example.com"`)
expect(typeof options.publicKey!.challenge).toMatchInlineSnapshot(`"string"`)
})
test('behavior: verifyRegistration stores credential and returns publicKey', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
const result = await ceremony.verifyRegistration({
attestationObject: 'mock',
clientDataJSON: 'mock',
id: 'cred-1',
publicKey: '0xabcd',
raw: {
id: 'cred-1',
type: 'public-key',
authenticatorAttachment: null,
rawId: 'cred-1',
response: { clientDataJSON: 'mock' },
},
})
expect(result).toMatchInlineSnapshot(`
{
"credentialId": "cred-1",
"publicKey": "0xabcd",
}
`)
})
test('behavior: verifyAuthentication returns stored publicKey', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
// Register first to store the credential
await ceremony.verifyRegistration({
attestationObject: 'mock',
clientDataJSON: 'mock',
id: 'cred-1',
publicKey: '0xabcd',
raw: {
id: 'cred-1',
type: 'public-key',
authenticatorAttachment: null,
rawId: 'cred-1',
response: { clientDataJSON: 'mock' },
},
})
const result = await ceremony.verifyAuthentication({
id: 'cred-1',
metadata: {
authenticatorData: '0x00',
clientDataJSON: '{}',
},
raw: {
id: 'cred-1',
type: 'public-key',
authenticatorAttachment: null,
rawId: 'cred-1',
response: { clientDataJSON: 'mock' },
},
signature: '0x00',
})
expect(result).toMatchInlineSnapshot(`
{
"credentialId": "cred-1",
"publicKey": "0xabcd",
}
`)
})
test('error: verifyAuthentication throws for unknown credential', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
await expect(
ceremony.verifyAuthentication({
id: 'unknown',
metadata: {
authenticatorData: '0x00',
clientDataJSON: '{}',
},
raw: {
id: 'unknown',
type: 'public-key',
authenticatorAttachment: null,
rawId: 'unknown',
response: { clientDataJSON: 'mock' },
},
signature: '0x00',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Unknown credential: unknown]`)
})
test('behavior: each call to getRegistrationOptions generates a unique challenge', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
const { options: a } = await ceremony.getRegistrationOptions({ name: 'Test' })
const { options: b } = await ceremony.getRegistrationOptions({ name: 'Test' })
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
})
test('behavior: each call to getAuthenticationOptions generates a unique challenge', async () => {
const ceremony = WebAuthnCeremony.local({ rpId: 'example.com' })
const { options: a } = await ceremony.getAuthenticationOptions()
const { options: b } = await ceremony.getAuthenticationOptions()
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
})
})