@thru/passkey 0.2.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/README.md +165 -0
- package/dist/index.cjs +892 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +850 -0
- package/dist/index.js.map +1 -0
- package/package.json +27 -0
- package/src/capabilities.ts +254 -0
- package/src/index.ts +86 -0
- package/src/popup-service.ts +168 -0
- package/src/popup.ts +192 -0
- package/src/register.ts +228 -0
- package/src/sign.ts +280 -0
- package/src/types.ts +149 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @thru/passkey
|
|
2
|
+
|
|
3
|
+
Browser-only WebAuthn package for passkey registration, signing, and popup-based flows. Built on top of `@thru/passkey-manager` for platform-agnostic crypto and encoding utilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @thru/passkey
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This package requires a browser environment with WebAuthn support (`navigator.credentials`).
|
|
12
|
+
|
|
13
|
+
## Basic Usage
|
|
14
|
+
|
|
15
|
+
### Register a Passkey
|
|
16
|
+
|
|
17
|
+
Create a new P-256 credential bound to the user's platform authenticator:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { registerPasskey } from '@thru/passkey';
|
|
21
|
+
|
|
22
|
+
const result = await registerPasskey('alice', 'user-id-123', 'example.com');
|
|
23
|
+
// result.credentialId - base64url credential ID
|
|
24
|
+
// result.publicKeyX - hex-encoded P-256 X coordinate
|
|
25
|
+
// result.publicKeyY - hex-encoded P-256 Y coordinate
|
|
26
|
+
// result.rpId - relying party ID
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Sign with a Known Credential
|
|
30
|
+
|
|
31
|
+
Sign a challenge using a specific credential ID:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { signWithPasskey } from '@thru/passkey';
|
|
35
|
+
|
|
36
|
+
const challenge = new Uint8Array(32); // your challenge bytes
|
|
37
|
+
const result = await signWithPasskey(credentialId, challenge, 'example.com');
|
|
38
|
+
// result.signature - 64-byte concatenated r||s (low-S normalized)
|
|
39
|
+
// result.signatureR - 32-byte r component
|
|
40
|
+
// result.signatureS - 32-byte s component
|
|
41
|
+
// result.authenticatorData - raw authenticator data
|
|
42
|
+
// result.clientDataJSON - raw client data JSON
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Sign with a Stored Passkey
|
|
46
|
+
|
|
47
|
+
For embedded or iframe contexts where you have stored passkey metadata. Automatically falls back to a popup window when inline WebAuthn is restricted:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { signWithStoredPasskey } from '@thru/passkey';
|
|
51
|
+
import type { PasskeyMetadata } from '@thru/passkey';
|
|
52
|
+
|
|
53
|
+
const result = await signWithStoredPasskey(
|
|
54
|
+
challenge,
|
|
55
|
+
'example.com',
|
|
56
|
+
preferredPasskey, // PasskeyMetadata | null
|
|
57
|
+
allPasskeys, // PasskeyMetadata[]
|
|
58
|
+
{ appName: 'My App', origin: 'https://app.example.com' }
|
|
59
|
+
);
|
|
60
|
+
// result includes .passkey metadata for the credential that signed
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Sign with a Discoverable Passkey
|
|
64
|
+
|
|
65
|
+
Let the browser prompt the user to choose from their available passkeys:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { signWithDiscoverablePasskey } from '@thru/passkey';
|
|
69
|
+
|
|
70
|
+
const result = await signWithDiscoverablePasskey(challenge, 'example.com');
|
|
71
|
+
// result.credentialId - the credential the user selected
|
|
72
|
+
// result.rpId - relying party ID
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Key Capabilities
|
|
76
|
+
|
|
77
|
+
- **P-256 (ES256) credential creation** via `navigator.credentials.create` with platform authenticator selection, resident key, and user verification required
|
|
78
|
+
- **Three signing modes**: known credential, stored passkey with fallback, and discoverable (browser-prompted)
|
|
79
|
+
- **Automatic popup fallback** for iframe/embedded contexts where the Permissions Policy blocks inline WebAuthn
|
|
80
|
+
- **Low-S signature normalization** applied to all signing results for protocol compatibility
|
|
81
|
+
- **Capability detection** to query WebAuthn support, client capabilities, and determine the optimal prompt mode before signing
|
|
82
|
+
- **Re-exports** encoding and crypto utilities from `@thru/passkey-manager` for backward compatibility
|
|
83
|
+
|
|
84
|
+
## Capability Detection
|
|
85
|
+
|
|
86
|
+
Check browser support and determine the best prompt mode ahead of time:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import {
|
|
90
|
+
isWebAuthnSupported,
|
|
91
|
+
preloadPasskeyClientCapabilities,
|
|
92
|
+
getPasskeyClientCapabilities,
|
|
93
|
+
shouldUsePasskeyPopup,
|
|
94
|
+
isInIframe,
|
|
95
|
+
} from '@thru/passkey';
|
|
96
|
+
|
|
97
|
+
// Quick synchronous check
|
|
98
|
+
if (!isWebAuthnSupported()) {
|
|
99
|
+
// WebAuthn not available
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Preload capabilities early (e.g., on app init)
|
|
103
|
+
preloadPasskeyClientCapabilities();
|
|
104
|
+
|
|
105
|
+
// Later, read cached or await capabilities
|
|
106
|
+
const capabilities = await getPasskeyClientCapabilities();
|
|
107
|
+
|
|
108
|
+
// Check if a popup is needed for a given action
|
|
109
|
+
const needsPopup = await shouldUsePasskeyPopup('get');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Popup Bridge
|
|
113
|
+
|
|
114
|
+
For applications that host the passkey popup window (e.g., the wallet app), the package provides both the parent-side and popup-side APIs:
|
|
115
|
+
|
|
116
|
+
### Parent Side
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import {
|
|
120
|
+
openPasskeyPopupWindow,
|
|
121
|
+
requestPasskeyPopup,
|
|
122
|
+
closePopup,
|
|
123
|
+
PASSKEY_POPUP_PATH,
|
|
124
|
+
PASSKEY_POPUP_CHANNEL,
|
|
125
|
+
} from '@thru/passkey';
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Popup Window Side
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import {
|
|
132
|
+
toPopupSigningResult,
|
|
133
|
+
buildSuccessResponse,
|
|
134
|
+
decodeChallenge,
|
|
135
|
+
getPopupDisplayInfo,
|
|
136
|
+
getResponseError,
|
|
137
|
+
signWithPreferredPasskey,
|
|
138
|
+
buildStoredPasskeyResult,
|
|
139
|
+
} from '@thru/passkey';
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Communication between parent and popup uses `postMessage` with `BroadcastChannel` as a fallback. The popup path defaults to `/passkey/popup`.
|
|
143
|
+
|
|
144
|
+
## Re-exported Utilities
|
|
145
|
+
|
|
146
|
+
The following are re-exported from `@thru/passkey-manager` for convenience:
|
|
147
|
+
|
|
148
|
+
**Crypto**: `parseDerSignature`, `normalizeLowS`, `normalizeSignatureComponent`, `P256_N`, `P256_HALF_N`, `bytesToBigIntBE`, `bigIntToBytesBE`
|
|
149
|
+
|
|
150
|
+
**Encoding**: `arrayBufferToBase64Url`, `base64UrlToArrayBuffer`, `bytesToBase64Url`, `base64UrlToBytes`, `bytesToHex`, `hexToBytes`, `bytesEqual`, `compareBytes`, `uniqueAccounts`
|
|
151
|
+
|
|
152
|
+
## Types
|
|
153
|
+
|
|
154
|
+
Key types exported from this package:
|
|
155
|
+
|
|
156
|
+
| Type | Description |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `PasskeyRegistrationResult` | Credential ID and P-256 public key coordinates |
|
|
159
|
+
| `PasskeySigningResult` | Signature bytes, authenticator data, and client data |
|
|
160
|
+
| `PasskeyDiscoverableSigningResult` | Signing result with credential ID and rpId |
|
|
161
|
+
| `PasskeyStoredSigningResult` | Signing result with attached passkey metadata |
|
|
162
|
+
| `PasskeyMetadata` | Stored passkey info (credential ID, public key, rpId, timestamps) |
|
|
163
|
+
| `PasskeyClientCapabilities` | WebAuthn client capability flags |
|
|
164
|
+
| `PasskeyPopupContext` | App context passed to popup for display |
|
|
165
|
+
| `PasskeyPopupAccount` | Account info passed through popup bridge |
|