@organigram/passkey-wallet 0.1.6
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/.organigram-docs-cache/passkey-wallet/api-extractor.json +35 -0
- package/.organigram-docs-cache/passkey-wallet/api-model/passkey-wallet.api.json +3166 -0
- package/.organigram-docs-cache/passkey-wallet/entry/index.d.ts +9 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/node-compile-cache/v24.12.0-arm64-cf738c9d-501/05732508 +0 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/node-compile-cache/v24.12.0-arm64-cf738c9d-501/166b6dbf +0 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/node-compile-cache/v24.12.0-arm64-cf738c9d-501/41c20e23 +0 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/node-compile-cache/v24.12.0-arm64-cf738c9d-501/7111ea58 +0 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/tsx-501/17844-1822dfbab9819e9f6a93485fe3da5dbf3745311c +1 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/tsx-501/17844-45a65cfdc7a50c53817c1dad3954ae3d2d2c35fb +1 -0
- package/.organigram-docs-cache/passkey-wallet/tmp/tsx-501/17844-fee5290b05398fe9e3b878deefd22383744f1526 +1 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/createorganigrampasskeywalletinput.yml +17 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/identitypasskeycapabilities.yml +16 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/organigrampasskeycapabilities.yml +18 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/organigrampasskeyprovider.yml +16 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/organigrampasskeyprovideractions.yml +20 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyprfunavailableerror.yml +28 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyproviderevent.yml +11 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyproviderlistener.yml +11 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyregistrationresult.yml +15 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyvaultdecrypterror.yml +28 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyvaultenvelopedata.yml +18 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeyvaultenvelopeinput.yml +19 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeywalletapiclient.yml +33 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeywalletcapabilities.yml +11 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeywalletlogincapabilities.yml +14 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeywalletregistrationcapabilities.yml +18 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/passkeywalletvaultpayload.yml +18 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet/unlockedpasskeywallet.yml +21 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/passkey-wallet.yml +870 -0
- package/.organigram-docs-cache/passkey-wallet/yaml/toc.yml +43 -0
- package/.turbo/turbo-build.log +6 -0
- package/.turbo/turbo-docs$colon$generate.log +118 -0
- package/.turbo/turbo-lint.log +13 -0
- package/.turbo/turbo-test.log +152 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE.md +21 -0
- package/README.md +427 -0
- package/__mocks__/organigram-js.ts +19 -0
- package/__mocks__/wagmi.ts +1 -0
- package/dist/crypto.d.ts +39 -0
- package/dist/crypto.js +95 -0
- package/dist/eip1193.d.ts +24 -0
- package/dist/eip1193.js +103 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +13 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/rainbowkit.d.ts +14 -0
- package/dist/rainbowkit.js +128 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.js +17 -0
- package/dist/vault.d.ts +10 -0
- package/dist/vault.js +41 -0
- package/dist/wallet.d.ts +15 -0
- package/dist/wallet.js +30 -0
- package/dist/webauthn-client.d.ts +73 -0
- package/dist/webauthn-client.js +234 -0
- package/dist/webauthn-server.d.ts +62 -0
- package/dist/webauthn-server.js +133 -0
- package/jest.config.ts +32 -0
- package/package.json +61 -0
- package/src/crypto.test.ts +69 -0
- package/src/crypto.ts +193 -0
- package/src/eip1193.ts +169 -0
- package/src/errors.ts +16 -0
- package/src/index.ts +4 -0
- package/src/rainbowkit.test.ts +42 -0
- package/src/rainbowkit.ts +171 -0
- package/src/types.ts +82 -0
- package/src/vault.test.ts +23 -0
- package/src/vault.ts +77 -0
- package/src/wallet.ts +61 -0
- package/src/webauthn-client.test.ts +103 -0
- package/src/webauthn-client.ts +430 -0
- package/src/webauthn-server.test.ts +43 -0
- package/src/webauthn-server.ts +258 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateAuthenticationOptions,
|
|
3
|
+
generateRegistrationOptions,
|
|
4
|
+
verifyAuthenticationResponse,
|
|
5
|
+
verifyRegistrationResponse
|
|
6
|
+
} from '@simplewebauthn/server'
|
|
7
|
+
import { getAddress, isAddress } from 'viem'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
base64UrlToBytes,
|
|
11
|
+
passkeyVaultAlgorithm,
|
|
12
|
+
type PasskeyVaultEnvelopeData,
|
|
13
|
+
passkeyVaultKeyVersion
|
|
14
|
+
} from './crypto'
|
|
15
|
+
|
|
16
|
+
export const passkeyChallengeTtlMs = 5 * 60 * 1000
|
|
17
|
+
|
|
18
|
+
export type PasskeyVaultEnvelopeInput = {
|
|
19
|
+
address: string
|
|
20
|
+
encryptedMnemonic: string
|
|
21
|
+
salt: string
|
|
22
|
+
nonce: string
|
|
23
|
+
algorithm: string
|
|
24
|
+
keyVersion: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const validatePasskeyVaultEnvelopeInput = (
|
|
28
|
+
input: PasskeyVaultEnvelopeInput
|
|
29
|
+
): PasskeyVaultEnvelopeInput & { address: `0x${string}` } => {
|
|
30
|
+
if (!isAddress(input.address)) {
|
|
31
|
+
throw new Error('Passkey vault envelope requires a valid wallet address.')
|
|
32
|
+
}
|
|
33
|
+
if (input.encryptedMnemonic.trim() === '') {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'Passkey vault envelope requires encrypted recovery phrase data.'
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
if (input.salt.trim() === '') {
|
|
39
|
+
throw new Error('Passkey vault envelope requires a salt.')
|
|
40
|
+
}
|
|
41
|
+
if (input.nonce.trim() === '') {
|
|
42
|
+
throw new Error('Passkey vault envelope requires a nonce.')
|
|
43
|
+
}
|
|
44
|
+
if (input.algorithm !== passkeyVaultAlgorithm) {
|
|
45
|
+
throw new Error('Passkey vault envelope algorithm is not supported.')
|
|
46
|
+
}
|
|
47
|
+
if (input.keyVersion !== passkeyVaultKeyVersion) {
|
|
48
|
+
throw new Error('Passkey vault envelope key version is not supported.')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
...input,
|
|
53
|
+
address: getAddress(input.address as `0x${string}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const createPasskeyChallengeExpiry = (): Date =>
|
|
58
|
+
new Date(Date.now() + passkeyChallengeTtlMs)
|
|
59
|
+
|
|
60
|
+
export const getWebAuthnClientDataChallenge = (response: unknown): string => {
|
|
61
|
+
const clientDataJSON = (response as {
|
|
62
|
+
response?: {
|
|
63
|
+
clientDataJSON?: string
|
|
64
|
+
}
|
|
65
|
+
})?.response?.clientDataJSON
|
|
66
|
+
if (typeof clientDataJSON !== 'string' || clientDataJSON === '') {
|
|
67
|
+
throw new Error('WebAuthn response is missing client data.')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const clientData = JSON.parse(
|
|
71
|
+
new TextDecoder().decode(base64UrlToBytes(clientDataJSON))
|
|
72
|
+
) as {
|
|
73
|
+
challenge?: unknown
|
|
74
|
+
}
|
|
75
|
+
if (typeof clientData.challenge !== 'string' || clientData.challenge === '') {
|
|
76
|
+
throw new Error('WebAuthn response is missing its challenge.')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return clientData.challenge
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const createPasskeyRegistrationOptions = async ({
|
|
83
|
+
rpId,
|
|
84
|
+
userAddress,
|
|
85
|
+
email
|
|
86
|
+
}: {
|
|
87
|
+
rpId: string
|
|
88
|
+
userAddress?: string | null
|
|
89
|
+
email?: string | null
|
|
90
|
+
}) =>
|
|
91
|
+
await generateRegistrationOptions({
|
|
92
|
+
rpName: 'Organigram',
|
|
93
|
+
rpID: rpId,
|
|
94
|
+
userName: email ?? userAddress ?? 'Organigram passkey wallet',
|
|
95
|
+
userDisplayName: email ?? userAddress ?? 'Organigram passkey wallet',
|
|
96
|
+
attestationType: 'none',
|
|
97
|
+
authenticatorSelection: {
|
|
98
|
+
residentKey: 'required',
|
|
99
|
+
userVerification: 'required'
|
|
100
|
+
},
|
|
101
|
+
extensions: {
|
|
102
|
+
prf: {
|
|
103
|
+
eval: {
|
|
104
|
+
first: new Uint8Array(32)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} as Parameters<typeof generateRegistrationOptions>[0])
|
|
109
|
+
|
|
110
|
+
type VerifiedPasskeyRegistrationResponse = Awaited<
|
|
111
|
+
ReturnType<typeof verifyRegistrationResponse>
|
|
112
|
+
> & {
|
|
113
|
+
registrationInfo: NonNullable<
|
|
114
|
+
Awaited<ReturnType<typeof verifyRegistrationResponse>>['registrationInfo']
|
|
115
|
+
>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const verifyPasskeyRegistration = async ({
|
|
119
|
+
response,
|
|
120
|
+
expectedChallenge,
|
|
121
|
+
expectedOrigin,
|
|
122
|
+
expectedRpId,
|
|
123
|
+
envelope
|
|
124
|
+
}: {
|
|
125
|
+
response: unknown
|
|
126
|
+
expectedChallenge: string
|
|
127
|
+
expectedOrigin: string
|
|
128
|
+
expectedRpId: string
|
|
129
|
+
envelope: PasskeyVaultEnvelopeInput
|
|
130
|
+
}): Promise<{
|
|
131
|
+
verification: VerifiedPasskeyRegistrationResponse
|
|
132
|
+
vaultEnvelope: PasskeyVaultEnvelopeInput & { address: `0x${string}` }
|
|
133
|
+
}> => {
|
|
134
|
+
const vaultEnvelope = validatePasskeyVaultEnvelopeInput(envelope)
|
|
135
|
+
const verification = await verifyRegistrationResponse({
|
|
136
|
+
response: response as Parameters<
|
|
137
|
+
typeof verifyRegistrationResponse
|
|
138
|
+
>[0]['response'],
|
|
139
|
+
expectedChallenge,
|
|
140
|
+
expectedOrigin,
|
|
141
|
+
expectedRPID: expectedRpId,
|
|
142
|
+
requireUserVerification: true
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
if (!verification.verified || verification.registrationInfo == null) {
|
|
146
|
+
throw new Error('Passkey registration was not verified.')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
verification: verification as VerifiedPasskeyRegistrationResponse,
|
|
151
|
+
vaultEnvelope
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const createPasskeyAuthenticationOptions = async ({
|
|
156
|
+
rpId,
|
|
157
|
+
credentials = []
|
|
158
|
+
}: {
|
|
159
|
+
rpId: string
|
|
160
|
+
credentials?: Array<{
|
|
161
|
+
credentialId: string
|
|
162
|
+
transports: string[]
|
|
163
|
+
}>
|
|
164
|
+
}) => {
|
|
165
|
+
const isDiscoverableAuthentication = credentials.length === 0
|
|
166
|
+
|
|
167
|
+
return await generateAuthenticationOptions({
|
|
168
|
+
rpID: rpId,
|
|
169
|
+
userVerification: 'required',
|
|
170
|
+
...(!isDiscoverableAuthentication
|
|
171
|
+
? {
|
|
172
|
+
allowCredentials: credentials.map(credential => ({
|
|
173
|
+
id: credential.credentialId,
|
|
174
|
+
transports: credential.transports as AuthenticatorTransport[]
|
|
175
|
+
}))
|
|
176
|
+
}
|
|
177
|
+
: {}),
|
|
178
|
+
extensions: {
|
|
179
|
+
prf: isDiscoverableAuthentication
|
|
180
|
+
? {
|
|
181
|
+
eval: {
|
|
182
|
+
first: new Uint8Array(32)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
: {
|
|
186
|
+
evalByCredential: Object.fromEntries(
|
|
187
|
+
credentials.map(credential => [
|
|
188
|
+
credential.credentialId,
|
|
189
|
+
{
|
|
190
|
+
first: new Uint8Array(32)
|
|
191
|
+
}
|
|
192
|
+
])
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} as Parameters<typeof generateAuthenticationOptions>[0])
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const verifyPasskeyAuthentication = async ({
|
|
200
|
+
response,
|
|
201
|
+
expectedChallenge,
|
|
202
|
+
expectedOrigin,
|
|
203
|
+
expectedRpId,
|
|
204
|
+
credential
|
|
205
|
+
}: {
|
|
206
|
+
response: unknown
|
|
207
|
+
expectedChallenge: string
|
|
208
|
+
expectedOrigin: string
|
|
209
|
+
expectedRpId: string
|
|
210
|
+
credential: {
|
|
211
|
+
credentialId: string
|
|
212
|
+
publicKey: string
|
|
213
|
+
signCount: number
|
|
214
|
+
}
|
|
215
|
+
}): Promise<Awaited<ReturnType<typeof verifyAuthenticationResponse>>> =>
|
|
216
|
+
await verifyAuthenticationResponse({
|
|
217
|
+
response: response as Parameters<
|
|
218
|
+
typeof verifyAuthenticationResponse
|
|
219
|
+
>[0]['response'],
|
|
220
|
+
expectedChallenge,
|
|
221
|
+
expectedOrigin,
|
|
222
|
+
expectedRPID: expectedRpId,
|
|
223
|
+
credential: {
|
|
224
|
+
id: credential.credentialId,
|
|
225
|
+
publicKey: Buffer.from(credential.publicKey, 'base64url'),
|
|
226
|
+
counter: credential.signCount
|
|
227
|
+
},
|
|
228
|
+
requireUserVerification: true
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
export const toPasskeyVaultEnvelopeData = ({
|
|
232
|
+
encryptedMnemonic,
|
|
233
|
+
salt,
|
|
234
|
+
nonce,
|
|
235
|
+
algorithm,
|
|
236
|
+
keyVersion
|
|
237
|
+
}: {
|
|
238
|
+
encryptedMnemonic: string
|
|
239
|
+
salt: string
|
|
240
|
+
nonce: string
|
|
241
|
+
algorithm: string
|
|
242
|
+
keyVersion: number
|
|
243
|
+
}): PasskeyVaultEnvelopeData => {
|
|
244
|
+
if (algorithm !== passkeyVaultAlgorithm) {
|
|
245
|
+
throw new Error('Passkey vault envelope algorithm is not supported.')
|
|
246
|
+
}
|
|
247
|
+
if (keyVersion !== passkeyVaultKeyVersion) {
|
|
248
|
+
throw new Error('Passkey vault envelope key version is not supported.')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
algorithm,
|
|
253
|
+
keyVersion,
|
|
254
|
+
ciphertext: encryptedMnemonic,
|
|
255
|
+
salt,
|
|
256
|
+
nonce
|
|
257
|
+
}
|
|
258
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@tsconfig/recommended/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"removeComments": false,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"preserveConstEnums": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"rootDir": ".",
|
|
13
|
+
"sourceMap": false,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"types": ["node", "jest"]
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"src/**/*.ts",
|
|
20
|
+
"__mocks__/**/*.ts",
|
|
21
|
+
"jest.config.ts"
|
|
22
|
+
],
|
|
23
|
+
"exclude": [
|
|
24
|
+
"node_modules",
|
|
25
|
+
"dist"
|
|
26
|
+
]
|
|
27
|
+
}
|