@metamask/passkey-controller 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -2
- package/README.md +30 -13
- package/dist/PasskeyController.cjs +182 -125
- package/dist/PasskeyController.cjs.map +1 -1
- package/dist/PasskeyController.d.cts +61 -60
- package/dist/PasskeyController.d.cts.map +1 -1
- package/dist/PasskeyController.d.mts +61 -60
- package/dist/PasskeyController.d.mts.map +1 -1
- package/dist/PasskeyController.mjs +184 -127
- package/dist/PasskeyController.mjs.map +1 -1
- package/dist/ceremony-manager.cjs +3 -2
- package/dist/ceremony-manager.cjs.map +1 -1
- package/dist/ceremony-manager.d.cts +3 -2
- package/dist/ceremony-manager.d.cts.map +1 -1
- package/dist/ceremony-manager.d.mts +3 -2
- package/dist/ceremony-manager.d.mts.map +1 -1
- package/dist/ceremony-manager.mjs +3 -2
- package/dist/ceremony-manager.mjs.map +1 -1
- package/dist/constants.cjs +2 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +2 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +2 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +2 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/key-derivation.cjs +3 -35
- package/dist/key-derivation.cjs.map +1 -1
- package/dist/key-derivation.d.cts +5 -28
- package/dist/key-derivation.d.cts.map +1 -1
- package/dist/key-derivation.d.mts +5 -28
- package/dist/key-derivation.d.mts.map +1 -1
- package/dist/key-derivation.mjs +2 -33
- package/dist/key-derivation.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/crypto.cjs +12 -1
- package/dist/utils/crypto.cjs.map +1 -1
- package/dist/utils/crypto.d.cts +7 -0
- package/dist/utils/crypto.d.cts.map +1 -1
- package/dist/utils/crypto.d.mts +7 -0
- package/dist/utils/crypto.d.mts.map +1 -1
- package/dist/utils/crypto.mjs +10 -0
- package/dist/utils/crypto.mjs.map +1 -1
- package/dist/webauthn/types.cjs.map +1 -1
- package/dist/webauthn/types.d.cts +1 -1
- package/dist/webauthn/types.d.cts.map +1 -1
- package/dist/webauthn/types.d.mts +1 -1
- package/dist/webauthn/types.d.mts.map +1 -1
- package/dist/webauthn/types.mjs.map +1 -1
- package/dist/webauthn/verify-authentication-response.cjs +3 -4
- package/dist/webauthn/verify-authentication-response.cjs.map +1 -1
- package/dist/webauthn/verify-authentication-response.d.cts +3 -2
- package/dist/webauthn/verify-authentication-response.d.cts.map +1 -1
- package/dist/webauthn/verify-authentication-response.d.mts +3 -2
- package/dist/webauthn/verify-authentication-response.d.mts.map +1 -1
- package/dist/webauthn/verify-authentication-response.mjs +3 -4
- package/dist/webauthn/verify-authentication-response.mjs.map +1 -1
- package/dist/webauthn/verify-registration-response.cjs +8 -6
- package/dist/webauthn/verify-registration-response.cjs.map +1 -1
- package/dist/webauthn/verify-registration-response.d.cts +5 -5
- package/dist/webauthn/verify-registration-response.d.cts.map +1 -1
- package/dist/webauthn/verify-registration-response.d.mts +5 -5
- package/dist/webauthn/verify-registration-response.d.mts.map +1 -1
- package/dist/webauthn/verify-registration-response.mjs +8 -6
- package/dist/webauthn/verify-registration-response.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.1]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `PasskeyController` verifies registration and authentication responses with `requireUserVerification: true`, so the WebAuthn user verification (UV) flag must be set; assertions with user presence only no longer pass verification ([#8696](https://github.com/MetaMask/core/pull/8696))
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `generateAuthenticationOptions` now sets `userVerification: 'required'` so client WebAuthn requests align with server-side verification requirements and do not fail on authenticators that skip UV when set to `'preferred'` ([#8696](https://github.com/MetaMask/core/pull/8696))
|
|
19
|
+
|
|
20
|
+
## [2.0.0]
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `generatePostRegistrationAuthenticationOptions` to issue `navigator.credentials.get()` options after `navigator.credentials.create()`, keyed to the in-flight registration ceremony (including PRF eval when a salt was used) ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
25
|
+
- `already_enrolled` (`PasskeyControllerErrorCode.AlreadyEnrolled`) when calling `protectVaultKeyWithPasskey` while a passkey is already enrolled ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **BREAKING:** Enrollment completes in three steps: `generateRegistrationOptions` → `create()` → `generatePostRegistrationAuthenticationOptions` → `get()` → `protectVaultKeyWithPasskey`; `protectVaultKeyWithPasskey` now **requires** `authenticationResponse`, and the vault wrapping key is derived from that post-registration assertion (same path as unlock: PRF when present, otherwise `userHandle`) ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
30
|
+
- **BREAKING:** `PasskeyController` constructor option `rpID` is replaced with `expectedRPID: string | string[]` (normalized to a string array, which may be empty). Optional `rpId` sets `rp.id` / `rpId` in generated WebAuthn options; when omitted, those fields are omitted. Verification passes that array to `verifyRegistrationResponse` / `verifyAuthenticationResponse` as `expectedRPIDs` ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
31
|
+
- **BREAKING:** `verifyRegistrationResponse` and `verifyAuthenticationResponse` now take `expectedRPIDs: string[]` instead of `expectedRPID: string` ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
32
|
+
- `verifyRegistrationResponse` / `verifyAuthenticationResponse` accept an empty `expectedRPIDs` array to skip RP ID hash allowlist matching; successful authentication then reports `authenticationInfo.rpID` as an empty string ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
33
|
+
- Increase `CEREMONY_TTL_SLACK_MS` to 2 minutes so in-flight ceremony state (`CEREMONY_MAX_AGE_MS`, 3 minutes including WebAuthn timeout) tolerates longer gaps between WebAuthn options and completion (e.g. post-registration authentication) ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
34
|
+
- Bump `@metamask/messenger` from `^1.1.1` to `^1.2.0` ([#8632](https://github.com/MetaMask/core/pull/8632))
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- `protectVaultKeyWithPasskey` rejects post-registration assertions whose `userHandle` is missing or does not match the in-flight registration ceremony when using `userHandle` key derivation (assertion `userHandle` is not signature-bound) ([#8663](https://github.com/MetaMask/core/pull/8663))
|
|
39
|
+
|
|
10
40
|
## [1.0.0]
|
|
11
41
|
|
|
12
42
|
### Added
|
|
13
43
|
|
|
14
|
-
- Initial `@metamask/passkey-controller
|
|
44
|
+
- Initial `@metamask/passkey-controller`: `PasskeyController` for WebAuthn passkey vault key protection (HKDF-derived keys, AES-256-GCM wrap/unwrap), PRF or `userHandle` derivation, challenge-keyed `CeremonyManager`, enrollment/unlock/renewal flows, `verifyPasskeyAuthentication`, selectors, and exported ceremony timing constants. ([#8422](https://github.com/MetaMask/core/pull/8422))
|
|
15
45
|
- `PasskeyControllerError` with stable `code`, optional `cause` / `context`, `toJSON`, and `toString`; `PasskeyControllerErrorCode`, `PasskeyControllerErrorMessage`, and `controllerName`. Replaces `PasskeyAuthenticationRejectedError`—use `PasskeyControllerError` and `code` for auth failures.
|
|
16
46
|
- **BREAKING:** Operational error messages are prefixed with `PasskeyController - `; prefer `code` or `instanceof PasskeyControllerError` over matching raw strings.
|
|
17
47
|
- `renewVaultKeyProtection` uses the same `vault_key_decryption_failed` code as `retrieveVaultKeyWithPasskey` when AES-GCM decrypt fails.
|
|
@@ -23,5 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
23
53
|
- Registration verification requires the credential `id`/`rawId` to match the credential id in authenticator data; vault wrapping key derivation uses that verified credential id so enrollment keys align with the stored credential.
|
|
24
54
|
- Registration options request attestation conveyance `'none'` so clients are not asked for direct attestation formats the verifier does not implement (`none` and self-attested `packed` only).
|
|
25
55
|
|
|
26
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/passkey-controller@
|
|
56
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/passkey-controller@2.0.1...HEAD
|
|
57
|
+
[2.0.1]: https://github.com/MetaMask/core/compare/@metamask/passkey-controller@2.0.0...@metamask/passkey-controller@2.0.1
|
|
58
|
+
[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/passkey-controller@1.0.0...@metamask/passkey-controller@2.0.0
|
|
27
59
|
[1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/passkey-controller@1.0.0
|
package/README.md
CHANGED
|
@@ -12,19 +12,21 @@ or
|
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
|
-
The controller follows a two-phase ceremony pattern for
|
|
15
|
+
The controller follows a two-phase ceremony pattern for unlock (authentication) and a three-step pattern for enrollment: registration options → post-registration authentication options → combined verify and protect.
|
|
16
16
|
|
|
17
17
|
1. **Generate options** — call a synchronous method that returns options JSON and records **in-flight ceremony** state (challenge-keyed; not a user login session).
|
|
18
18
|
2. **Verify response** — pass the authenticator's response back to the controller, which verifies the WebAuthn signature and performs the cryptographic operation (protect or retrieve the vault key).
|
|
19
19
|
|
|
20
|
+
For enrollment, the wrapping key is always derived from the **post-registration** `get()` response (same path as unlock), not from the `create()` response alone.
|
|
21
|
+
|
|
20
22
|
### Key derivation strategies
|
|
21
23
|
|
|
22
24
|
The controller supports two key derivation methods, selected automatically during enrollment:
|
|
23
25
|
|
|
24
|
-
| Strategy | When used
|
|
25
|
-
| -------------- |
|
|
26
|
-
| **PRF** |
|
|
27
|
-
| **userHandle** |
|
|
26
|
+
| Strategy | When used | Input key material |
|
|
27
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
28
|
+
| **PRF** | Post-registration assertion includes non-empty [PRF extension](https://w3c.github.io/webauthn/#prf-extension) output and registration used PRF salt | PRF evaluation output from the assertion (ceremony `prfSalt` is stored on the record) |
|
|
29
|
+
| **userHandle** | Otherwise | Random `userHandle` from registration (asserted on the post-registration `get()`) |
|
|
28
30
|
|
|
29
31
|
Both strategies feed the input key material through **HKDF-SHA256** with the credential ID as salt and a fixed info string to produce the 32-byte AES-256 wrapping key.
|
|
30
32
|
|
|
@@ -40,7 +42,9 @@ const messenger: PasskeyControllerMessenger = /* create via root messenger */;
|
|
|
40
42
|
|
|
41
43
|
const controller = new PasskeyController({
|
|
42
44
|
messenger,
|
|
43
|
-
|
|
45
|
+
rpId: 'example.com',
|
|
46
|
+
// Or multiple verification candidates: expectedRPID: ['a.example', 'b.example']
|
|
47
|
+
expectedRPID: 'example.com',
|
|
44
48
|
rpName: 'My Wallet',
|
|
45
49
|
expectedOrigin: 'chrome-extension://abcdef1234567890',
|
|
46
50
|
// Optional — both default to `rpName` when omitted.
|
|
@@ -49,18 +53,31 @@ const controller = new PasskeyController({
|
|
|
49
53
|
});
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
`expectedRPID` is a string or string array used to verify the authenticator `rpIdHash`. Optional `rpId`, when set, is sent as `rp.id` / `rpId` in generated WebAuthn options; when omitted, those fields are omitted so the client uses its default RP ID behavior.
|
|
57
|
+
|
|
52
58
|
### Passkey enrollment (registration)
|
|
53
59
|
|
|
54
60
|
```typescript
|
|
55
61
|
// 1. Generate registration options (synchronous)
|
|
56
|
-
const
|
|
62
|
+
const regOptions = controller.generateRegistrationOptions();
|
|
57
63
|
|
|
58
|
-
// 2.
|
|
59
|
-
const
|
|
64
|
+
// 2. Create the passkey in the browser
|
|
65
|
+
const regResponse = await navigator.credentials.create({
|
|
66
|
+
publicKey: regOptions,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 3. Post-registration authentication (same wrapping-key path as unlock)
|
|
70
|
+
const authOptions = controller.generatePostRegistrationAuthenticationOptions({
|
|
71
|
+
registrationResponse: regResponse,
|
|
72
|
+
});
|
|
73
|
+
const authResponse = await navigator.credentials.get({
|
|
74
|
+
publicKey: authOptions,
|
|
75
|
+
});
|
|
60
76
|
|
|
61
|
-
//
|
|
77
|
+
// 4. Verify registration + post-registration auth once, then persist
|
|
62
78
|
await controller.protectVaultKeyWithPasskey({
|
|
63
|
-
registrationResponse:
|
|
79
|
+
registrationResponse: regResponse,
|
|
80
|
+
authenticationResponse: authResponse,
|
|
64
81
|
vaultKey: myVaultEncryptionKey,
|
|
65
82
|
});
|
|
66
83
|
```
|
|
@@ -116,8 +133,8 @@ passkeyControllerSelectors.selectIsPasskeyEnrolled(state); // boolean
|
|
|
116
133
|
|
|
117
134
|
`PasskeyControllerError` is thrown for controller failures. Expected operational
|
|
118
135
|
cases use a stable `code` from `PasskeyControllerErrorCode` (for example:
|
|
119
|
-
`not_enrolled`, `
|
|
120
|
-
`missing_key_material`, `vault_key_decryption_failed`). Human-readable strings
|
|
136
|
+
`not_enrolled`, `already_enrolled`, `no_registration_ceremony`,
|
|
137
|
+
`authentication_verification_failed`, `missing_key_material`, `vault_key_decryption_failed`). Human-readable strings
|
|
121
138
|
live on `PasskeyControllerErrorMessage`. Use `instanceof PasskeyControllerError`
|
|
122
139
|
and a defined `error.code` to tell these apart from malformed WebAuthn payloads
|
|
123
140
|
and other `Error` values. Thrown errors from the internal WebAuthn verify helpers
|