mirror of
https://github.com/documenso/documenso.git
synced 2026-08-26 08:12:27 +10:00
The v9 packages are deprecated. This updates to v13 which includes breaking API changes: optionsJSON wrapper for auth functions, renamed properties (authenticator→credential), and base64 encoding for credential IDs via isoBase64URL helper.
186 lines
7.8 KiB
Markdown
186 lines
7.8 KiB
Markdown
---
|
|
date: 2026-01-14
|
|
title: Simplewebauthn V13 Upgrade
|
|
---
|
|
|
|
## Overview
|
|
|
|
Upgrade SimpleWebAuthn packages from v9.x to v13.x to address the deprecation of `@simplewebauthn/types` and take advantage of new features and improvements.
|
|
|
|
## Current State
|
|
|
|
The codebase currently uses:
|
|
- `@simplewebauthn/browser@9.x`
|
|
- `@simplewebauthn/server@9.x`
|
|
- `@simplewebauthn/types@9.x`
|
|
|
|
## Breaking Changes Summary (v9 → v13)
|
|
|
|
### v10.0.0 Breaking Changes
|
|
1. **Minimum Node version raised to Node v20**
|
|
2. **`generateRegistrationOptions()` now expects `Base64URLString` for `excludeCredentials` IDs** (no more `type: 'public-key'` needed)
|
|
3. **`generateAuthenticationOptions()` now expects `Base64URLString` for `allowCredentials` IDs**
|
|
4. **`credentialID` returned from verification methods is now `Base64URLString`** instead of `Uint8Array`
|
|
5. **`AuthenticatorDevice.credentialID` is now `Base64URLString`**
|
|
6. **`rpID` is now required when calling `generateAuthenticationOptions()`**
|
|
7. **`generateRegistrationOptions()` will generate random user IDs** if not provided
|
|
8. **`user.id` is treated as base64url string in `startRegistration()`**
|
|
9. **`userHandle` is treated as base64url string in `startAuthentication()`**
|
|
|
|
### v11.0.0 Breaking Changes
|
|
1. **Positional arguments in `startRegistration()` and `startAuthentication()` replaced by object**
|
|
- Before: `startRegistration(options)`
|
|
- After: `startRegistration({ optionsJSON: options })`
|
|
- Before: `startAuthentication(options)`
|
|
- After: `startAuthentication({ optionsJSON: options })`
|
|
2. **`AuthenticatorDevice` type renamed to `WebAuthnCredential`**
|
|
- `credentialID` → `credential.id`
|
|
- `credentialPublicKey` → `credential.publicKey`
|
|
3. **`verifyRegistrationResponse()` returns `registrationInfo.credential` instead of individual properties**
|
|
- `credentialID` → `credential.id`
|
|
- `credentialPublicKey` → `credential.publicKey`
|
|
- `counter` → `credential.counter`
|
|
- `transports` are now in `credential.transports`
|
|
4. **`verifyAuthenticationResponse()` uses `credential` argument instead of `authenticator`**
|
|
|
|
### v13.0.0 Breaking Changes
|
|
1. **`@simplewebauthn/types` package is retired**
|
|
- Types are now exported from `@simplewebauthn/browser` and `@simplewebauthn/server`
|
|
- Import types from `@simplewebauthn/server` instead
|
|
|
|
## Files to Update
|
|
|
|
### Package Changes
|
|
1. Remove `@simplewebauthn/types` dependency
|
|
2. Update `@simplewebauthn/browser` to `^13.2.2`
|
|
3. Update `@simplewebauthn/server` to `^13.2.2`
|
|
|
|
### Server-side Files
|
|
|
|
#### 1. `packages/lib/server-only/auth/create-passkey-registration-options.ts`
|
|
- Change import from `@simplewebauthn/types` to `@simplewebauthn/server`
|
|
- Remove `type: 'public-key'` from `excludeCredentials` items
|
|
- Update `userID` to use `isoUint8Array.fromUTF8String()` for proper encoding
|
|
|
|
#### 2. `packages/lib/server-only/auth/create-passkey-authentication-options.ts`
|
|
- Change import from `@simplewebauthn/types` to `@simplewebauthn/server`
|
|
- Remove `type: 'public-key'` from `allowCredentials` items
|
|
|
|
#### 3. `packages/lib/server-only/auth/create-passkey-signin-options.ts`
|
|
- No changes needed (already using correct options)
|
|
|
|
#### 4. `packages/lib/server-only/auth/create-passkey.ts`
|
|
- Change import from `@simplewebauthn/types` to `@simplewebauthn/server`
|
|
- Update to use new `registrationInfo.credential` structure:
|
|
- `credentialID` → `credential.id`
|
|
- `credentialPublicKey` → `credential.publicKey`
|
|
- `counter` → `credential.counter`
|
|
- Note: `credential.id` is now a `Base64URLString`, so `Buffer.from(credentialID)` needs updating
|
|
|
|
#### 5. `packages/lib/server-only/document/is-recipient-authorized.ts`
|
|
- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator`:
|
|
- Change `authenticator: { credentialID, credentialPublicKey, counter }` to `credential: { id, publicKey, counter }`
|
|
- Since `credential.id` is now base64url string, convert stored `credentialId` buffer to base64url
|
|
|
|
#### 6. `packages/auth/server/routes/passkey.ts`
|
|
- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator`
|
|
- Same changes as `is-recipient-authorized.ts`
|
|
|
|
#### 7. `packages/trpc/server/auth-router/create-passkey.ts`
|
|
- Change import from `@simplewebauthn/types` to `@simplewebauthn/server`
|
|
|
|
### Browser-side Files
|
|
|
|
#### 8. `apps/remix/app/components/dialogs/passkey-create-dialog.tsx`
|
|
- Update `startRegistration()` call:
|
|
- Before: `startRegistration(passkeyRegistrationOptions)`
|
|
- After: `startRegistration({ optionsJSON: passkeyRegistrationOptions })`
|
|
|
|
#### 9. `apps/remix/app/components/forms/signin.tsx`
|
|
- Update `startAuthentication()` call:
|
|
- Before: `startAuthentication(options)`
|
|
- After: `startAuthentication({ optionsJSON: options })`
|
|
|
|
#### 10. `apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx`
|
|
- Update `startAuthentication()` call:
|
|
- Before: `startAuthentication(options)`
|
|
- After: `startAuthentication({ optionsJSON: options })`
|
|
|
|
### Database/Schema Considerations
|
|
|
|
The database stores `credentialId` as `Bytes`. The new API returns `credential.id` as `Base64URLString`. We need to:
|
|
1. When **storing** a new passkey: Convert from `Base64URLString` to `Buffer`
|
|
2. When **passing to verification**: Convert from `Buffer` to `Base64URLString`
|
|
|
|
Use `isoBase64URL` helper from `@simplewebauthn/server/helpers` for these conversions.
|
|
|
|
## Implementation Steps
|
|
|
|
### Step 1: Update package.json dependencies
|
|
```bash
|
|
npm uninstall @simplewebauthn/types
|
|
npm install @simplewebauthn/browser@^13.2.2 @simplewebauthn/server@^13.2.2
|
|
```
|
|
|
|
### Step 2: Update type imports
|
|
Replace all `@simplewebauthn/types` imports with `@simplewebauthn/server`
|
|
|
|
### Step 3: Update browser-side API calls
|
|
- `startRegistration(options)` → `startRegistration({ optionsJSON: options })`
|
|
- `startAuthentication(options)` → `startAuthentication({ optionsJSON: options })`
|
|
|
|
### Step 4: Update server-side registration
|
|
- Update `excludeCredentials` format (remove `type: 'public-key'`)
|
|
- Update `userID` encoding if needed
|
|
- Update `verifyRegistrationResponse()` result handling for new `credential` structure
|
|
|
|
### Step 5: Update server-side authentication
|
|
- Update `allowCredentials` format (remove `type: 'public-key'`)
|
|
- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator`
|
|
- Handle `Base64URLString` for `credential.id`
|
|
|
|
### Step 6: Update credential storage/retrieval
|
|
- When storing: Convert `Base64URLString` to `Buffer`
|
|
- When reading: Convert `Buffer` to `Base64URLString`
|
|
|
|
### Step 7: Test passkey flows
|
|
1. Test passkey creation
|
|
2. Test passkey sign-in
|
|
3. Test passkey authentication for document signing
|
|
4. Test passkey deletion
|
|
|
|
## Code Examples
|
|
|
|
### Converting stored Buffer to Base64URLString for verification
|
|
```typescript
|
|
import { isoBase64URL } from '@simplewebauthn/server/helpers';
|
|
|
|
// When reading from database (Buffer) and passing to verification
|
|
const credential = {
|
|
id: isoBase64URL.fromBuffer(passkey.credentialId),
|
|
publicKey: new Uint8Array(passkey.credentialPublicKey),
|
|
counter: Number(passkey.counter),
|
|
transports: passkey.transports,
|
|
};
|
|
```
|
|
|
|
### Converting Base64URLString to Buffer for storage
|
|
```typescript
|
|
import { isoBase64URL } from '@simplewebauthn/server/helpers';
|
|
|
|
// When storing from registration response
|
|
const credentialIdBuffer = Buffer.from(
|
|
isoBase64URL.toBuffer(registrationInfo.credential.id)
|
|
);
|
|
```
|
|
|
|
## Risks and Mitigations
|
|
|
|
1. **Database compatibility**: The `credentialId` is stored as `Bytes` in the database. The new API uses `Base64URLString`. We need proper conversion functions.
|
|
- **Mitigation**: Use `isoBase64URL.fromBuffer()` and `isoBase64URL.toBuffer()` for conversions
|
|
|
|
2. **Existing passkeys**: Existing passkeys should continue to work as long as conversion is done correctly.
|
|
- **Mitigation**: Test with existing passkeys after upgrade
|
|
|
|
3. **Browser compatibility**: v10+ requires newer browser APIs.
|
|
- **Mitigation**: `browserSupportsWebAuthn()` already handles this check |