supakeys 0.1.0
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/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/cli.js +771 -0
- package/dist/index.cjs +394 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +181 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.js +381 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var browser = require('@simplewebauthn/browser');
|
|
4
|
+
|
|
5
|
+
// src/lib/passkey-auth.ts
|
|
6
|
+
|
|
7
|
+
// src/lib/errors.ts
|
|
8
|
+
function createError(code, message, cause) {
|
|
9
|
+
return { code, message, cause };
|
|
10
|
+
}
|
|
11
|
+
function mapWebAuthnError(error) {
|
|
12
|
+
if (error instanceof Error) {
|
|
13
|
+
const name = error.name;
|
|
14
|
+
const message = error.message;
|
|
15
|
+
switch (name) {
|
|
16
|
+
case "NotSupportedError":
|
|
17
|
+
return createError(
|
|
18
|
+
"NOT_SUPPORTED",
|
|
19
|
+
"WebAuthn is not supported in this browser or context.",
|
|
20
|
+
error
|
|
21
|
+
);
|
|
22
|
+
case "NotAllowedError":
|
|
23
|
+
if (message.toLowerCase().includes("timeout")) {
|
|
24
|
+
return createError("TIMEOUT", "The operation timed out. Please try again.", error);
|
|
25
|
+
}
|
|
26
|
+
return createError("CANCELLED", "The operation was cancelled.", error);
|
|
27
|
+
case "InvalidStateError":
|
|
28
|
+
return createError(
|
|
29
|
+
"INVALID_STATE",
|
|
30
|
+
"A passkey for this account already exists on this device.",
|
|
31
|
+
error
|
|
32
|
+
);
|
|
33
|
+
case "SecurityError":
|
|
34
|
+
return createError(
|
|
35
|
+
"SECURITY_ERROR",
|
|
36
|
+
"The operation was blocked due to security restrictions.",
|
|
37
|
+
error
|
|
38
|
+
);
|
|
39
|
+
case "AbortError":
|
|
40
|
+
return createError("CANCELLED", "The operation was aborted.", error);
|
|
41
|
+
case "TypeError":
|
|
42
|
+
if (message.toLowerCase().includes("fetch")) {
|
|
43
|
+
return createError(
|
|
44
|
+
"NETWORK_ERROR",
|
|
45
|
+
"Network request failed. Please check your connection.",
|
|
46
|
+
error
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return createError(
|
|
53
|
+
"UNKNOWN_ERROR",
|
|
54
|
+
error instanceof Error ? error.message : "An unknown error occurred.",
|
|
55
|
+
error
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function getErrorMessage(code) {
|
|
59
|
+
const messages = {
|
|
60
|
+
NOT_SUPPORTED: "Passkeys are not supported on this device or browser.",
|
|
61
|
+
CANCELLED: "The operation was cancelled.",
|
|
62
|
+
TIMEOUT: "The operation timed out. Please try again.",
|
|
63
|
+
INVALID_STATE: "A passkey for this account already exists on this device.",
|
|
64
|
+
SECURITY_ERROR: "The operation was blocked due to security restrictions.",
|
|
65
|
+
CHALLENGE_EXPIRED: "Your session has expired. Please try again.",
|
|
66
|
+
CHALLENGE_MISMATCH: "Verification failed. Please try again.",
|
|
67
|
+
VERIFICATION_FAILED: "Authentication failed. Please try again.",
|
|
68
|
+
CREDENTIAL_NOT_FOUND: "Passkey not found. It may have been removed.",
|
|
69
|
+
USER_NOT_FOUND: "Account not found.",
|
|
70
|
+
CREDENTIAL_EXISTS: "This passkey is already registered.",
|
|
71
|
+
RATE_LIMITED: "Too many attempts. Please wait a moment and try again.",
|
|
72
|
+
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
73
|
+
UNKNOWN_ERROR: "An unexpected error occurred. Please try again."
|
|
74
|
+
};
|
|
75
|
+
return messages[code] || messages.UNKNOWN_ERROR;
|
|
76
|
+
}
|
|
77
|
+
function isPasskeyError(error) {
|
|
78
|
+
return typeof error === "object" && error !== null && "code" in error && "message" in error && typeof error.code === "string" && typeof error.message === "string";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/lib/support.ts
|
|
82
|
+
function isWebAuthnSupported() {
|
|
83
|
+
return typeof window !== "undefined" && typeof window.PublicKeyCredential !== "undefined" && typeof window.navigator !== "undefined" && typeof window.navigator.credentials !== "undefined";
|
|
84
|
+
}
|
|
85
|
+
async function isPlatformAuthenticatorAvailable() {
|
|
86
|
+
if (!isWebAuthnSupported()) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function isConditionalUISupported() {
|
|
96
|
+
if (!isWebAuthnSupported()) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const pubKeyCred = PublicKeyCredential;
|
|
101
|
+
if (typeof pubKeyCred.isConditionalMediationAvailable === "function") {
|
|
102
|
+
return await pubKeyCred.isConditionalMediationAvailable();
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function getPasskeySupport() {
|
|
110
|
+
const webauthn = isWebAuthnSupported();
|
|
111
|
+
if (!webauthn) {
|
|
112
|
+
return {
|
|
113
|
+
webauthn: false,
|
|
114
|
+
platformAuthenticator: false,
|
|
115
|
+
conditionalUI: false
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const [platformAuthenticator, conditionalUI] = await Promise.all([
|
|
119
|
+
isPlatformAuthenticatorAvailable(),
|
|
120
|
+
isConditionalUISupported()
|
|
121
|
+
]);
|
|
122
|
+
return {
|
|
123
|
+
webauthn,
|
|
124
|
+
platformAuthenticator,
|
|
125
|
+
conditionalUI
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function isSecureContext() {
|
|
129
|
+
if (typeof window === "undefined") {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return window.isSecureContext === true;
|
|
133
|
+
}
|
|
134
|
+
function getUnsupportedReason() {
|
|
135
|
+
if (typeof window === "undefined") {
|
|
136
|
+
return "Passkeys are only available in browser environments.";
|
|
137
|
+
}
|
|
138
|
+
if (!isSecureContext()) {
|
|
139
|
+
return "Passkeys require a secure connection (HTTPS).";
|
|
140
|
+
}
|
|
141
|
+
if (!isWebAuthnSupported()) {
|
|
142
|
+
return "Your browser does not support passkeys. Please update to a modern browser.";
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/lib/passkey-auth.ts
|
|
148
|
+
var DEFAULT_CONFIG = {
|
|
149
|
+
functionName: "passkey-auth",
|
|
150
|
+
rpId: typeof window !== "undefined" ? window.location.hostname : "localhost",
|
|
151
|
+
rpName: "My App",
|
|
152
|
+
timeout: 6e4
|
|
153
|
+
};
|
|
154
|
+
var PasskeyAuth = class {
|
|
155
|
+
supabase;
|
|
156
|
+
config;
|
|
157
|
+
constructor(supabase, config = {}) {
|
|
158
|
+
this.supabase = supabase;
|
|
159
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
160
|
+
}
|
|
161
|
+
async isSupported() {
|
|
162
|
+
return getPasskeySupport();
|
|
163
|
+
}
|
|
164
|
+
async register(options) {
|
|
165
|
+
const unsupportedReason = getUnsupportedReason();
|
|
166
|
+
if (unsupportedReason) {
|
|
167
|
+
return { success: false, error: createError("NOT_SUPPORTED", unsupportedReason) };
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const startResponse = await this.callEdgeFunction(
|
|
171
|
+
"/register/start",
|
|
172
|
+
{
|
|
173
|
+
email: options.email,
|
|
174
|
+
displayName: options.displayName || options.email,
|
|
175
|
+
authenticatorName: options.authenticatorName
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
if (!startResponse.success || !startResponse.data) {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
error: startResponse.error ? createError(startResponse.error.code, startResponse.error.message) : createError("UNKNOWN_ERROR", "Failed to start registration")
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
let registrationResponse;
|
|
185
|
+
try {
|
|
186
|
+
registrationResponse = await browser.startRegistration({
|
|
187
|
+
optionsJSON: startResponse.data.options
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
191
|
+
}
|
|
192
|
+
const finishResponse = await this.callEdgeFunction(
|
|
193
|
+
"/register/finish",
|
|
194
|
+
{
|
|
195
|
+
challengeId: startResponse.data.challengeId,
|
|
196
|
+
response: registrationResponse,
|
|
197
|
+
authenticatorName: options.authenticatorName
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
if (!finishResponse.success || !finishResponse.data?.verified) {
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
error: finishResponse.error ? createError(finishResponse.error.code, finishResponse.error.message) : createError("VERIFICATION_FAILED", "Failed to verify registration")
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return { success: true, passkey: finishResponse.data.passkey };
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (isPasskeyError(error)) return { success: false, error };
|
|
209
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async signIn(options = {}) {
|
|
213
|
+
const unsupportedReason = getUnsupportedReason();
|
|
214
|
+
if (unsupportedReason) {
|
|
215
|
+
return { success: false, error: createError("NOT_SUPPORTED", unsupportedReason) };
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const startResponse = await this.callEdgeFunction(
|
|
219
|
+
"/login/start",
|
|
220
|
+
{ email: options.email }
|
|
221
|
+
);
|
|
222
|
+
if (!startResponse.success || !startResponse.data) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
error: startResponse.error ? createError(startResponse.error.code, startResponse.error.message) : createError("UNKNOWN_ERROR", "Failed to start authentication")
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
let authenticationResponse;
|
|
229
|
+
try {
|
|
230
|
+
authenticationResponse = await browser.startAuthentication({
|
|
231
|
+
optionsJSON: startResponse.data.options
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
235
|
+
}
|
|
236
|
+
const finishResponse = await this.callEdgeFunction(
|
|
237
|
+
"/login/finish",
|
|
238
|
+
{
|
|
239
|
+
challengeId: startResponse.data.challengeId,
|
|
240
|
+
response: authenticationResponse
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
if (!finishResponse.success || !finishResponse.data?.verified) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
error: finishResponse.error ? createError(finishResponse.error.code, finishResponse.error.message) : createError("VERIFICATION_FAILED", "Authentication failed")
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const { tokenHash, email } = finishResponse.data;
|
|
250
|
+
if (!tokenHash || !email) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
error: createError("VERIFICATION_FAILED", "Missing session token")
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const { data: sessionData, error: sessionError } = await this.supabase.auth.verifyOtp({
|
|
257
|
+
token_hash: tokenHash,
|
|
258
|
+
type: "email"
|
|
259
|
+
});
|
|
260
|
+
if (sessionError || !sessionData.session) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
error: createError(
|
|
264
|
+
"VERIFICATION_FAILED",
|
|
265
|
+
sessionError?.message || "Failed to create session"
|
|
266
|
+
)
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return { success: true, session: sessionData.session };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (isPasskeyError(error)) return { success: false, error };
|
|
272
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async linkPasskey(options = {}) {
|
|
276
|
+
const unsupportedReason = getUnsupportedReason();
|
|
277
|
+
if (unsupportedReason) {
|
|
278
|
+
return { success: false, error: createError("NOT_SUPPORTED", unsupportedReason) };
|
|
279
|
+
}
|
|
280
|
+
const {
|
|
281
|
+
data: { user }
|
|
282
|
+
} = await this.supabase.auth.getUser();
|
|
283
|
+
if (!user?.email) {
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
error: createError("USER_NOT_FOUND", "You must be logged in to link a passkey")
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return this.register({
|
|
290
|
+
email: user.email,
|
|
291
|
+
authenticatorName: options.authenticatorName
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
async listPasskeys() {
|
|
295
|
+
try {
|
|
296
|
+
const response = await this.callEdgeFunction("/passkeys/list", {});
|
|
297
|
+
if (!response.success || !response.data) {
|
|
298
|
+
return {
|
|
299
|
+
success: false,
|
|
300
|
+
error: response.error ? createError(response.error.code, response.error.message) : createError("UNKNOWN_ERROR", "Failed to list passkeys")
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return { success: true, passkeys: response.data.passkeys };
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async removePasskey(options) {
|
|
309
|
+
try {
|
|
310
|
+
const response = await this.callEdgeFunction("/passkeys/remove", {
|
|
311
|
+
credentialId: options.credentialId
|
|
312
|
+
});
|
|
313
|
+
if (!response.success) {
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
error: response.error ? createError(response.error.code, response.error.message) : createError("UNKNOWN_ERROR", "Failed to remove passkey")
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return { success: true };
|
|
320
|
+
} catch (error) {
|
|
321
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async updatePasskey(options) {
|
|
325
|
+
try {
|
|
326
|
+
const response = await this.callEdgeFunction("/passkeys/update", {
|
|
327
|
+
credentialId: options.credentialId,
|
|
328
|
+
authenticatorName: options.authenticatorName
|
|
329
|
+
});
|
|
330
|
+
if (!response.success || !response.data) {
|
|
331
|
+
return {
|
|
332
|
+
success: false,
|
|
333
|
+
error: response.error ? createError(response.error.code, response.error.message) : createError("UNKNOWN_ERROR", "Failed to update passkey")
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return { success: true, passkey: response.data.passkey };
|
|
337
|
+
} catch (error) {
|
|
338
|
+
return { success: false, error: mapWebAuthnError(error) };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async callEdgeFunction(endpoint, data) {
|
|
342
|
+
try {
|
|
343
|
+
const { data: responseData, error } = await this.supabase.functions.invoke(
|
|
344
|
+
this.config.functionName,
|
|
345
|
+
{
|
|
346
|
+
body: {
|
|
347
|
+
endpoint,
|
|
348
|
+
data: {
|
|
349
|
+
...data,
|
|
350
|
+
rpId: this.config.rpId,
|
|
351
|
+
rpName: this.config.rpName
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
if (error) {
|
|
357
|
+
return {
|
|
358
|
+
success: false,
|
|
359
|
+
error: {
|
|
360
|
+
code: "NETWORK_ERROR",
|
|
361
|
+
message: error.message || "Edge function request failed"
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
return responseData;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
return {
|
|
368
|
+
success: false,
|
|
369
|
+
error: {
|
|
370
|
+
code: "NETWORK_ERROR",
|
|
371
|
+
message: error instanceof Error ? error.message : "Network request failed"
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
function createPasskeyAuth(supabase, config = {}) {
|
|
378
|
+
return new PasskeyAuth(supabase, config);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
exports.PasskeyAuth = PasskeyAuth;
|
|
382
|
+
exports.createError = createError;
|
|
383
|
+
exports.createPasskeyAuth = createPasskeyAuth;
|
|
384
|
+
exports.getErrorMessage = getErrorMessage;
|
|
385
|
+
exports.getPasskeySupport = getPasskeySupport;
|
|
386
|
+
exports.getUnsupportedReason = getUnsupportedReason;
|
|
387
|
+
exports.isConditionalUISupported = isConditionalUISupported;
|
|
388
|
+
exports.isPasskeyError = isPasskeyError;
|
|
389
|
+
exports.isPlatformAuthenticatorAvailable = isPlatformAuthenticatorAvailable;
|
|
390
|
+
exports.isSecureContext = isSecureContext;
|
|
391
|
+
exports.isWebAuthnSupported = isWebAuthnSupported;
|
|
392
|
+
exports.mapWebAuthnError = mapWebAuthnError;
|
|
393
|
+
//# sourceMappingURL=index.cjs.map
|
|
394
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/errors.ts","../src/lib/support.ts","../src/lib/passkey-auth.ts"],"names":["startRegistration","startAuthentication"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { PasskeyError, PasskeyErrorCode } from '../types';\n\nexport function createError(\n code: PasskeyErrorCode,\n message: string,\n cause?: unknown\n): PasskeyError {\n return { code, message, cause };\n}\n\nexport function mapWebAuthnError(error: unknown): PasskeyError {\n if (error instanceof Error) {\n const name = error.name;\n const message = error.message;\n\n switch (name) {\n case 'NotSupportedError':\n return createError(\n 'NOT_SUPPORTED',\n 'WebAuthn is not supported in this browser or context.',\n error\n );\n\n case 'NotAllowedError':\n if (message.toLowerCase().includes('timeout')) {\n return createError('TIMEOUT', 'The operation timed out. Please try again.', error);\n }\n return createError('CANCELLED', 'The operation was cancelled.', error);\n\n case 'InvalidStateError':\n return createError(\n 'INVALID_STATE',\n 'A passkey for this account already exists on this device.',\n error\n );\n\n case 'SecurityError':\n return createError(\n 'SECURITY_ERROR',\n 'The operation was blocked due to security restrictions.',\n error\n );\n\n case 'AbortError':\n return createError('CANCELLED', 'The operation was aborted.', error);\n\n case 'TypeError':\n if (message.toLowerCase().includes('fetch')) {\n return createError(\n 'NETWORK_ERROR',\n 'Network request failed. Please check your connection.',\n error\n );\n }\n break;\n }\n }\n\n return createError(\n 'UNKNOWN_ERROR',\n error instanceof Error ? error.message : 'An unknown error occurred.',\n error\n );\n}\n\nexport function getErrorMessage(code: PasskeyErrorCode): string {\n const messages: Record<PasskeyErrorCode, string> = {\n NOT_SUPPORTED: 'Passkeys are not supported on this device or browser.',\n CANCELLED: 'The operation was cancelled.',\n TIMEOUT: 'The operation timed out. Please try again.',\n INVALID_STATE: 'A passkey for this account already exists on this device.',\n SECURITY_ERROR: 'The operation was blocked due to security restrictions.',\n CHALLENGE_EXPIRED: 'Your session has expired. Please try again.',\n CHALLENGE_MISMATCH: 'Verification failed. Please try again.',\n VERIFICATION_FAILED: 'Authentication failed. Please try again.',\n CREDENTIAL_NOT_FOUND: 'Passkey not found. It may have been removed.',\n USER_NOT_FOUND: 'Account not found.',\n CREDENTIAL_EXISTS: 'This passkey is already registered.',\n RATE_LIMITED: 'Too many attempts. Please wait a moment and try again.',\n NETWORK_ERROR: 'Network error. Please check your connection.',\n UNKNOWN_ERROR: 'An unexpected error occurred. Please try again.',\n };\n\n return messages[code] || messages.UNKNOWN_ERROR;\n}\n\nexport function isPasskeyError(error: unknown): error is PasskeyError {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n 'message' in error &&\n typeof (error as PasskeyError).code === 'string' &&\n typeof (error as PasskeyError).message === 'string'\n );\n}\n","import type { PasskeySupport } from '../types';\n\nexport function isWebAuthnSupported(): boolean {\n return (\n typeof window !== 'undefined' &&\n typeof window.PublicKeyCredential !== 'undefined' &&\n typeof window.navigator !== 'undefined' &&\n typeof window.navigator.credentials !== 'undefined'\n );\n}\n\nexport async function isPlatformAuthenticatorAvailable(): Promise<boolean> {\n if (!isWebAuthnSupported()) {\n return false;\n }\n\n try {\n return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();\n } catch {\n return false;\n }\n}\n\nexport async function isConditionalUISupported(): Promise<boolean> {\n if (!isWebAuthnSupported()) {\n return false;\n }\n\n try {\n const pubKeyCred = PublicKeyCredential as typeof PublicKeyCredential & {\n isConditionalMediationAvailable?: () => Promise<boolean>;\n };\n\n if (typeof pubKeyCred.isConditionalMediationAvailable === 'function') {\n return await pubKeyCred.isConditionalMediationAvailable();\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport async function getPasskeySupport(): Promise<PasskeySupport> {\n const webauthn = isWebAuthnSupported();\n\n if (!webauthn) {\n return {\n webauthn: false,\n platformAuthenticator: false,\n conditionalUI: false,\n };\n }\n\n const [platformAuthenticator, conditionalUI] = await Promise.all([\n isPlatformAuthenticatorAvailable(),\n isConditionalUISupported(),\n ]);\n\n return {\n webauthn,\n platformAuthenticator,\n conditionalUI,\n };\n}\n\nexport function isSecureContext(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n return window.isSecureContext === true;\n}\n\nexport function getUnsupportedReason(): string | null {\n if (typeof window === 'undefined') {\n return 'Passkeys are only available in browser environments.';\n }\n\n if (!isSecureContext()) {\n return 'Passkeys require a secure connection (HTTPS).';\n }\n\n if (!isWebAuthnSupported()) {\n return 'Your browser does not support passkeys. Please update to a modern browser.';\n }\n\n return null;\n}\n","import { startRegistration, startAuthentication } from '@simplewebauthn/browser';\n\nimport type {\n AnySupabaseClient,\n PasskeyAuthConfig,\n ResolvedPasskeyAuthConfig,\n RegisterPasskeyOptions,\n RegisterPasskeyResult,\n SignInWithPasskeyOptions,\n SignInWithPasskeyResult,\n LinkPasskeyOptions,\n LinkPasskeyResult,\n RemovePasskeyOptions,\n RemovePasskeyResult,\n UpdatePasskeyOptions,\n UpdatePasskeyResult,\n ListPasskeysResult,\n PasskeySupport,\n StartRegistrationResponse,\n FinishRegistrationResponse,\n StartAuthenticationResponse,\n FinishAuthenticationResponse,\n PasskeyEndpoint,\n EdgeFunctionResponse,\n Passkey,\n RegistrationResponseJSON,\n AuthenticationResponseJSON,\n} from '../types';\n\nimport { createError, mapWebAuthnError, isPasskeyError } from './errors';\nimport { getPasskeySupport, getUnsupportedReason } from './support';\n\nconst DEFAULT_CONFIG: ResolvedPasskeyAuthConfig = {\n functionName: 'passkey-auth',\n rpId: typeof window !== 'undefined' ? window.location.hostname : 'localhost',\n rpName: 'My App',\n timeout: 60000,\n};\n\nexport class PasskeyAuth {\n private supabase: AnySupabaseClient;\n private config: ResolvedPasskeyAuthConfig;\n\n constructor(supabase: AnySupabaseClient, config: PasskeyAuthConfig = {}) {\n this.supabase = supabase;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n async isSupported(): Promise<PasskeySupport> {\n return getPasskeySupport();\n }\n\n async register(options: RegisterPasskeyOptions): Promise<RegisterPasskeyResult> {\n const unsupportedReason = getUnsupportedReason();\n if (unsupportedReason) {\n return { success: false, error: createError('NOT_SUPPORTED', unsupportedReason) };\n }\n\n try {\n const startResponse = await this.callEdgeFunction<StartRegistrationResponse>(\n '/register/start',\n {\n email: options.email,\n displayName: options.displayName || options.email,\n authenticatorName: options.authenticatorName,\n }\n );\n\n if (!startResponse.success || !startResponse.data) {\n return {\n success: false,\n error: startResponse.error\n ? createError(startResponse.error.code, startResponse.error.message)\n : createError('UNKNOWN_ERROR', 'Failed to start registration'),\n };\n }\n\n let registrationResponse: RegistrationResponseJSON;\n try {\n registrationResponse = (await startRegistration({\n optionsJSON: startResponse.data.options,\n })) as unknown as RegistrationResponseJSON;\n } catch (error) {\n return { success: false, error: mapWebAuthnError(error) };\n }\n\n const finishResponse = await this.callEdgeFunction<FinishRegistrationResponse>(\n '/register/finish',\n {\n challengeId: startResponse.data.challengeId,\n response: registrationResponse,\n authenticatorName: options.authenticatorName,\n }\n );\n\n if (!finishResponse.success || !finishResponse.data?.verified) {\n return {\n success: false,\n error: finishResponse.error\n ? createError(finishResponse.error.code, finishResponse.error.message)\n : createError('VERIFICATION_FAILED', 'Failed to verify registration'),\n };\n }\n\n return { success: true, passkey: finishResponse.data.passkey };\n } catch (error) {\n if (isPasskeyError(error)) return { success: false, error };\n return { success: false, error: mapWebAuthnError(error) };\n }\n }\n\n async signIn(options: SignInWithPasskeyOptions = {}): Promise<SignInWithPasskeyResult> {\n const unsupportedReason = getUnsupportedReason();\n if (unsupportedReason) {\n return { success: false, error: createError('NOT_SUPPORTED', unsupportedReason) };\n }\n\n try {\n const startResponse = await this.callEdgeFunction<StartAuthenticationResponse>(\n '/login/start',\n { email: options.email }\n );\n\n if (!startResponse.success || !startResponse.data) {\n return {\n success: false,\n error: startResponse.error\n ? createError(startResponse.error.code, startResponse.error.message)\n : createError('UNKNOWN_ERROR', 'Failed to start authentication'),\n };\n }\n\n let authenticationResponse: AuthenticationResponseJSON;\n try {\n authenticationResponse = (await startAuthentication({\n optionsJSON: startResponse.data.options,\n })) as unknown as AuthenticationResponseJSON;\n } catch (error) {\n return { success: false, error: mapWebAuthnError(error) };\n }\n\n const finishResponse = await this.callEdgeFunction<FinishAuthenticationResponse>(\n '/login/finish',\n {\n challengeId: startResponse.data.challengeId,\n response: authenticationResponse,\n }\n );\n\n if (!finishResponse.success || !finishResponse.data?.verified) {\n return {\n success: false,\n error: finishResponse.error\n ? createError(finishResponse.error.code, finishResponse.error.message)\n : createError('VERIFICATION_FAILED', 'Authentication failed'),\n };\n }\n\n const { tokenHash, email } = finishResponse.data;\n if (!tokenHash || !email) {\n return {\n success: false,\n error: createError('VERIFICATION_FAILED', 'Missing session token'),\n };\n }\n\n const { data: sessionData, error: sessionError } = await this.supabase.auth.verifyOtp({\n token_hash: tokenHash,\n type: 'email',\n });\n\n if (sessionError || !sessionData.session) {\n return {\n success: false,\n error: createError(\n 'VERIFICATION_FAILED',\n sessionError?.message || 'Failed to create session'\n ),\n };\n }\n\n return { success: true, session: sessionData.session };\n } catch (error) {\n if (isPasskeyError(error)) return { success: false, error };\n return { success: false, error: mapWebAuthnError(error) };\n }\n }\n\n async linkPasskey(options: LinkPasskeyOptions = {}): Promise<LinkPasskeyResult> {\n const unsupportedReason = getUnsupportedReason();\n if (unsupportedReason) {\n return { success: false, error: createError('NOT_SUPPORTED', unsupportedReason) };\n }\n\n const {\n data: { user },\n } = await this.supabase.auth.getUser();\n if (!user?.email) {\n return {\n success: false,\n error: createError('USER_NOT_FOUND', 'You must be logged in to link a passkey'),\n };\n }\n\n return this.register({\n email: user.email,\n authenticatorName: options.authenticatorName,\n });\n }\n\n async listPasskeys(): Promise<ListPasskeysResult> {\n try {\n const response = await this.callEdgeFunction<{ passkeys: Passkey[] }>('/passkeys/list', {});\n\n if (!response.success || !response.data) {\n return {\n success: false,\n error: response.error\n ? createError(response.error.code, response.error.message)\n : createError('UNKNOWN_ERROR', 'Failed to list passkeys'),\n };\n }\n\n return { success: true, passkeys: response.data.passkeys };\n } catch (error) {\n return { success: false, error: mapWebAuthnError(error) };\n }\n }\n\n async removePasskey(options: RemovePasskeyOptions): Promise<RemovePasskeyResult> {\n try {\n const response = await this.callEdgeFunction<{ removed: boolean }>('/passkeys/remove', {\n credentialId: options.credentialId,\n });\n\n if (!response.success) {\n return {\n success: false,\n error: response.error\n ? createError(response.error.code, response.error.message)\n : createError('UNKNOWN_ERROR', 'Failed to remove passkey'),\n };\n }\n\n return { success: true };\n } catch (error) {\n return { success: false, error: mapWebAuthnError(error) };\n }\n }\n\n async updatePasskey(options: UpdatePasskeyOptions): Promise<UpdatePasskeyResult> {\n try {\n const response = await this.callEdgeFunction<{ passkey: Passkey }>('/passkeys/update', {\n credentialId: options.credentialId,\n authenticatorName: options.authenticatorName,\n });\n\n if (!response.success || !response.data) {\n return {\n success: false,\n error: response.error\n ? createError(response.error.code, response.error.message)\n : createError('UNKNOWN_ERROR', 'Failed to update passkey'),\n };\n }\n\n return { success: true, passkey: response.data.passkey };\n } catch (error) {\n return { success: false, error: mapWebAuthnError(error) };\n }\n }\n\n private async callEdgeFunction<T>(\n endpoint: PasskeyEndpoint,\n data: Record<string, unknown>\n ): Promise<EdgeFunctionResponse<T>> {\n try {\n const { data: responseData, error } = await this.supabase.functions.invoke(\n this.config.functionName,\n {\n body: {\n endpoint,\n data: {\n ...data,\n rpId: this.config.rpId,\n rpName: this.config.rpName,\n },\n },\n }\n );\n\n if (error) {\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error.message || 'Edge function request failed',\n },\n };\n }\n\n return responseData as EdgeFunctionResponse<T>;\n } catch (error) {\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n}\n\nexport function createPasskeyAuth(\n supabase: AnySupabaseClient,\n config: PasskeyAuthConfig = {}\n): PasskeyAuth {\n return new PasskeyAuth(supabase, config);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
|
2
|
+
export { Session } from '@supabase/supabase-js';
|
|
3
|
+
|
|
4
|
+
type AuthenticatorTransport = 'ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb';
|
|
5
|
+
interface PublicKeyCredentialCreationOptionsJSON {
|
|
6
|
+
rp: {
|
|
7
|
+
name: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
};
|
|
10
|
+
user: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
};
|
|
15
|
+
challenge: string;
|
|
16
|
+
pubKeyCredParams: Array<{
|
|
17
|
+
type: 'public-key';
|
|
18
|
+
alg: number;
|
|
19
|
+
}>;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
excludeCredentials?: Array<{
|
|
22
|
+
type: 'public-key';
|
|
23
|
+
id: string;
|
|
24
|
+
transports?: AuthenticatorTransport[];
|
|
25
|
+
}>;
|
|
26
|
+
authenticatorSelection?: {
|
|
27
|
+
authenticatorAttachment?: 'platform' | 'cross-platform';
|
|
28
|
+
residentKey?: 'discouraged' | 'preferred' | 'required';
|
|
29
|
+
requireResidentKey?: boolean;
|
|
30
|
+
userVerification?: 'required' | 'preferred' | 'discouraged';
|
|
31
|
+
};
|
|
32
|
+
attestation?: 'none' | 'indirect' | 'direct' | 'enterprise';
|
|
33
|
+
extensions?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
interface PublicKeyCredentialRequestOptionsJSON {
|
|
36
|
+
challenge: string;
|
|
37
|
+
timeout?: number;
|
|
38
|
+
rpId?: string;
|
|
39
|
+
allowCredentials?: Array<{
|
|
40
|
+
type: 'public-key';
|
|
41
|
+
id: string;
|
|
42
|
+
transports?: AuthenticatorTransport[];
|
|
43
|
+
}>;
|
|
44
|
+
userVerification?: 'required' | 'preferred' | 'discouraged';
|
|
45
|
+
extensions?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
interface RegistrationResponseJSON {
|
|
48
|
+
id: string;
|
|
49
|
+
rawId: string;
|
|
50
|
+
response: {
|
|
51
|
+
clientDataJSON: string;
|
|
52
|
+
attestationObject: string;
|
|
53
|
+
transports?: AuthenticatorTransport[];
|
|
54
|
+
publicKeyAlgorithm?: number;
|
|
55
|
+
publicKey?: string;
|
|
56
|
+
authenticatorData?: string;
|
|
57
|
+
};
|
|
58
|
+
authenticatorAttachment?: 'platform' | 'cross-platform';
|
|
59
|
+
clientExtensionResults: Record<string, unknown>;
|
|
60
|
+
type: 'public-key';
|
|
61
|
+
}
|
|
62
|
+
interface AuthenticationResponseJSON {
|
|
63
|
+
id: string;
|
|
64
|
+
rawId: string;
|
|
65
|
+
response: {
|
|
66
|
+
clientDataJSON: string;
|
|
67
|
+
authenticatorData: string;
|
|
68
|
+
signature: string;
|
|
69
|
+
userHandle?: string;
|
|
70
|
+
};
|
|
71
|
+
authenticatorAttachment?: 'platform' | 'cross-platform';
|
|
72
|
+
clientExtensionResults: Record<string, unknown>;
|
|
73
|
+
type: 'public-key';
|
|
74
|
+
}
|
|
75
|
+
interface PasskeyAuthConfig {
|
|
76
|
+
functionName?: string;
|
|
77
|
+
rpId?: string;
|
|
78
|
+
rpName?: string;
|
|
79
|
+
timeout?: number;
|
|
80
|
+
}
|
|
81
|
+
interface Passkey {
|
|
82
|
+
id: string;
|
|
83
|
+
userId: string;
|
|
84
|
+
webauthnUserId: string;
|
|
85
|
+
authenticatorName: string | null;
|
|
86
|
+
deviceType: 'singleDevice' | 'multiDevice';
|
|
87
|
+
backedUp: boolean;
|
|
88
|
+
transports: AuthenticatorTransport[];
|
|
89
|
+
aaguid: string | null;
|
|
90
|
+
createdAt: string;
|
|
91
|
+
lastUsedAt: string | null;
|
|
92
|
+
}
|
|
93
|
+
interface RegisterPasskeyOptions {
|
|
94
|
+
email: string;
|
|
95
|
+
displayName?: string;
|
|
96
|
+
authenticatorName?: string;
|
|
97
|
+
}
|
|
98
|
+
interface RegisterPasskeyResult {
|
|
99
|
+
success: boolean;
|
|
100
|
+
passkey?: Passkey;
|
|
101
|
+
error?: PasskeyError;
|
|
102
|
+
}
|
|
103
|
+
interface SignInWithPasskeyOptions {
|
|
104
|
+
email?: string;
|
|
105
|
+
}
|
|
106
|
+
interface SignInWithPasskeyResult {
|
|
107
|
+
success: boolean;
|
|
108
|
+
session?: Session;
|
|
109
|
+
error?: PasskeyError;
|
|
110
|
+
}
|
|
111
|
+
interface LinkPasskeyOptions {
|
|
112
|
+
authenticatorName?: string;
|
|
113
|
+
}
|
|
114
|
+
interface LinkPasskeyResult {
|
|
115
|
+
success: boolean;
|
|
116
|
+
passkey?: Passkey;
|
|
117
|
+
error?: PasskeyError;
|
|
118
|
+
}
|
|
119
|
+
interface RemovePasskeyOptions {
|
|
120
|
+
credentialId: string;
|
|
121
|
+
}
|
|
122
|
+
interface RemovePasskeyResult {
|
|
123
|
+
success: boolean;
|
|
124
|
+
error?: PasskeyError;
|
|
125
|
+
}
|
|
126
|
+
interface UpdatePasskeyOptions {
|
|
127
|
+
credentialId: string;
|
|
128
|
+
authenticatorName: string;
|
|
129
|
+
}
|
|
130
|
+
interface UpdatePasskeyResult {
|
|
131
|
+
success: boolean;
|
|
132
|
+
passkey?: Passkey;
|
|
133
|
+
error?: PasskeyError;
|
|
134
|
+
}
|
|
135
|
+
interface ListPasskeysResult {
|
|
136
|
+
success: boolean;
|
|
137
|
+
passkeys?: Passkey[];
|
|
138
|
+
error?: PasskeyError;
|
|
139
|
+
}
|
|
140
|
+
type PasskeyErrorCode = 'NOT_SUPPORTED' | 'CANCELLED' | 'TIMEOUT' | 'INVALID_STATE' | 'SECURITY_ERROR' | 'CHALLENGE_EXPIRED' | 'CHALLENGE_MISMATCH' | 'VERIFICATION_FAILED' | 'CREDENTIAL_NOT_FOUND' | 'USER_NOT_FOUND' | 'CREDENTIAL_EXISTS' | 'RATE_LIMITED' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
141
|
+
interface PasskeyError {
|
|
142
|
+
code: PasskeyErrorCode;
|
|
143
|
+
message: string;
|
|
144
|
+
cause?: unknown;
|
|
145
|
+
}
|
|
146
|
+
interface PasskeySupport {
|
|
147
|
+
webauthn: boolean;
|
|
148
|
+
platformAuthenticator: boolean;
|
|
149
|
+
conditionalUI: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
type AnySupabaseClient = SupabaseClient<any, any, any>;
|
|
153
|
+
|
|
154
|
+
declare class PasskeyAuth {
|
|
155
|
+
private supabase;
|
|
156
|
+
private config;
|
|
157
|
+
constructor(supabase: AnySupabaseClient, config?: PasskeyAuthConfig);
|
|
158
|
+
isSupported(): Promise<PasskeySupport>;
|
|
159
|
+
register(options: RegisterPasskeyOptions): Promise<RegisterPasskeyResult>;
|
|
160
|
+
signIn(options?: SignInWithPasskeyOptions): Promise<SignInWithPasskeyResult>;
|
|
161
|
+
linkPasskey(options?: LinkPasskeyOptions): Promise<LinkPasskeyResult>;
|
|
162
|
+
listPasskeys(): Promise<ListPasskeysResult>;
|
|
163
|
+
removePasskey(options: RemovePasskeyOptions): Promise<RemovePasskeyResult>;
|
|
164
|
+
updatePasskey(options: UpdatePasskeyOptions): Promise<UpdatePasskeyResult>;
|
|
165
|
+
private callEdgeFunction;
|
|
166
|
+
}
|
|
167
|
+
declare function createPasskeyAuth(supabase: AnySupabaseClient, config?: PasskeyAuthConfig): PasskeyAuth;
|
|
168
|
+
|
|
169
|
+
declare function isWebAuthnSupported(): boolean;
|
|
170
|
+
declare function isPlatformAuthenticatorAvailable(): Promise<boolean>;
|
|
171
|
+
declare function isConditionalUISupported(): Promise<boolean>;
|
|
172
|
+
declare function getPasskeySupport(): Promise<PasskeySupport>;
|
|
173
|
+
declare function isSecureContext(): boolean;
|
|
174
|
+
declare function getUnsupportedReason(): string | null;
|
|
175
|
+
|
|
176
|
+
declare function createError(code: PasskeyErrorCode, message: string, cause?: unknown): PasskeyError;
|
|
177
|
+
declare function mapWebAuthnError(error: unknown): PasskeyError;
|
|
178
|
+
declare function getErrorMessage(code: PasskeyErrorCode): string;
|
|
179
|
+
declare function isPasskeyError(error: unknown): error is PasskeyError;
|
|
180
|
+
|
|
181
|
+
export { type AuthenticationResponseJSON, type AuthenticatorTransport, type LinkPasskeyOptions, type LinkPasskeyResult, type ListPasskeysResult, type Passkey, PasskeyAuth, type PasskeyAuthConfig, type PasskeyError, type PasskeyErrorCode, type PasskeySupport, type PublicKeyCredentialCreationOptionsJSON, type PublicKeyCredentialRequestOptionsJSON, type RegisterPasskeyOptions, type RegisterPasskeyResult, type RegistrationResponseJSON, type RemovePasskeyOptions, type RemovePasskeyResult, type SignInWithPasskeyOptions, type SignInWithPasskeyResult, type UpdatePasskeyOptions, type UpdatePasskeyResult, createError, createPasskeyAuth, getErrorMessage, getPasskeySupport, getUnsupportedReason, isConditionalUISupported, isPasskeyError, isPlatformAuthenticatorAvailable, isSecureContext, isWebAuthnSupported, mapWebAuthnError };
|