@better-auth/passkey 1.7.0-rc.2 → 1.7.0-rc.3
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/dist/client.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Passkey, c as PasskeyOptions, d as WebAuthnChallengeValue, l as PasskeyRegistrationOptions, n as PASSKEY_ERROR_CODES, o as PasskeyAuthenticationOptions, s as PasskeyExtensionsResolver, t as passkey, u as PasskeyRegistrationUser } from "./index-
|
|
1
|
+
import { a as Passkey, c as PasskeyOptions, d as WebAuthnChallengeValue, l as PasskeyRegistrationOptions, n as PASSKEY_ERROR_CODES, o as PasskeyAuthenticationOptions, s as PasskeyExtensionsResolver, t as passkey, u as PasskeyRegistrationUser } from "./index-CatEiPwC.mjs";
|
|
2
2
|
import { AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, AuthenticationResponseJSON, RegistrationResponseJSON } from "@simplewebauthn/server";
|
|
3
3
|
import { atom } from "nanostores";
|
|
4
4
|
import { ClientFetchOption, ClientStore } from "@better-auth/core";
|
|
@@ -6,6 +6,10 @@ import { BetterFetch } from "@better-fetch/fetch";
|
|
|
6
6
|
import { Session, User } from "better-auth/types";
|
|
7
7
|
export type * from "@simplewebauthn/server";
|
|
8
8
|
//#region src/client.d.ts
|
|
9
|
+
type AddPasskeyResponse = Passkey & {
|
|
10
|
+
session?: Session;
|
|
11
|
+
user?: User;
|
|
12
|
+
};
|
|
9
13
|
declare const getPasskeyActions: ($fetch: BetterFetch, { $listPasskeys, $store }: {
|
|
10
14
|
$listPasskeys: ReturnType<typeof atom<any>>;
|
|
11
15
|
$store: ClientStore;
|
|
@@ -83,6 +87,10 @@ declare const getPasskeyActions: ($fetch: BetterFetch, { $listPasskeys, $store }
|
|
|
83
87
|
* Optional context for passkey-first registration flows.
|
|
84
88
|
*/
|
|
85
89
|
context?: string | null;
|
|
90
|
+
/**
|
|
91
|
+
* Create a session after successfully registering the passkey.
|
|
92
|
+
*/
|
|
93
|
+
createSession?: boolean;
|
|
86
94
|
/**
|
|
87
95
|
* Optional WebAuthn extensions to include during registration.
|
|
88
96
|
*/
|
|
@@ -105,14 +113,14 @@ declare const getPasskeyActions: ($fetch: BetterFetch, { $listPasskeys, $store }
|
|
|
105
113
|
statusText: string;
|
|
106
114
|
};
|
|
107
115
|
} | {
|
|
108
|
-
data:
|
|
116
|
+
data: AddPasskeyResponse;
|
|
109
117
|
error: null;
|
|
110
118
|
} | {
|
|
111
119
|
webauthn: {
|
|
112
120
|
response: RegistrationResponseJSON;
|
|
113
121
|
clientExtensionResults: AuthenticationExtensionsClientOutputs;
|
|
114
122
|
};
|
|
115
|
-
data:
|
|
123
|
+
data: AddPasskeyResponse;
|
|
116
124
|
error: null;
|
|
117
125
|
} | {
|
|
118
126
|
data: null;
|
|
@@ -209,6 +217,10 @@ declare const passkeyClient: () => {
|
|
|
209
217
|
* Optional context for passkey-first registration flows.
|
|
210
218
|
*/
|
|
211
219
|
context?: string | null;
|
|
220
|
+
/**
|
|
221
|
+
* Create a session after successfully registering the passkey.
|
|
222
|
+
*/
|
|
223
|
+
createSession?: boolean;
|
|
212
224
|
/**
|
|
213
225
|
* Optional WebAuthn extensions to include during registration.
|
|
214
226
|
*/
|
|
@@ -231,14 +243,14 @@ declare const passkeyClient: () => {
|
|
|
231
243
|
statusText: string;
|
|
232
244
|
};
|
|
233
245
|
} | {
|
|
234
|
-
data:
|
|
246
|
+
data: AddPasskeyResponse;
|
|
235
247
|
error: null;
|
|
236
248
|
} | {
|
|
237
249
|
webauthn: {
|
|
238
250
|
response: RegistrationResponseJSON;
|
|
239
251
|
clientExtensionResults: AuthenticationExtensionsClientOutputs;
|
|
240
252
|
};
|
|
241
|
-
data:
|
|
253
|
+
data: AddPasskeyResponse;
|
|
242
254
|
error: null;
|
|
243
255
|
} | {
|
|
244
256
|
data: null;
|
|
@@ -279,6 +291,7 @@ declare const passkeyClient: () => {
|
|
|
279
291
|
PASSKEY_NOT_FOUND: import("better-auth").RawError<"PASSKEY_NOT_FOUND">;
|
|
280
292
|
AUTHENTICATION_FAILED: import("better-auth").RawError<"AUTHENTICATION_FAILED">;
|
|
281
293
|
UNABLE_TO_CREATE_SESSION: import("better-auth").RawError<"UNABLE_TO_CREATE_SESSION">;
|
|
294
|
+
USER_NOT_FOUND: import("better-auth").RawError<"USER_NOT_FOUND">;
|
|
282
295
|
FAILED_TO_UPDATE_PASSKEY: import("better-auth").RawError<"FAILED_TO_UPDATE_PASSKEY">;
|
|
283
296
|
PREVIOUSLY_REGISTERED: import("better-auth").RawError<"PREVIOUSLY_REGISTERED">;
|
|
284
297
|
REGISTRATION_CANCELLED: import("better-auth").RawError<"REGISTRATION_CANCELLED">;
|
package/dist/client.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-02cV8Fcg.mjs";
|
|
2
2
|
import { WebAuthnError, startAuthentication, startRegistration } from "@simplewebauthn/browser";
|
|
3
3
|
import { useAuthQuery } from "better-auth/client";
|
|
4
4
|
import { atom } from "nanostores";
|
|
@@ -95,13 +95,15 @@ const getPasskeyActions = ($fetch, { $listPasskeys, $store }) => {
|
|
|
95
95
|
...fetchOpts,
|
|
96
96
|
body: {
|
|
97
97
|
response: responseBody,
|
|
98
|
-
name: opts?.name
|
|
98
|
+
name: opts?.name,
|
|
99
|
+
...opts?.createSession && { createSession: true }
|
|
99
100
|
},
|
|
100
101
|
method: "POST",
|
|
101
102
|
throw: false
|
|
102
103
|
});
|
|
103
104
|
if (!verified.data) return verified;
|
|
104
105
|
$listPasskeys.set(Math.random());
|
|
106
|
+
if (verified.data.session) $store.notify("$sessionSignal");
|
|
105
107
|
if (opts?.returnWebAuthnResponse) return {
|
|
106
108
|
...verified,
|
|
107
109
|
webauthn: {
|
|
@@ -229,6 +229,7 @@ declare const PASSKEY_ERROR_CODES: {
|
|
|
229
229
|
PASSKEY_NOT_FOUND: import("better-auth").RawError<"PASSKEY_NOT_FOUND">;
|
|
230
230
|
AUTHENTICATION_FAILED: import("better-auth").RawError<"AUTHENTICATION_FAILED">;
|
|
231
231
|
UNABLE_TO_CREATE_SESSION: import("better-auth").RawError<"UNABLE_TO_CREATE_SESSION">;
|
|
232
|
+
USER_NOT_FOUND: import("better-auth").RawError<"USER_NOT_FOUND">;
|
|
232
233
|
FAILED_TO_UPDATE_PASSKEY: import("better-auth").RawError<"FAILED_TO_UPDATE_PASSKEY">;
|
|
233
234
|
PREVIOUSLY_REGISTERED: import("better-auth").RawError<"PREVIOUSLY_REGISTERED">;
|
|
234
235
|
REGISTRATION_CANCELLED: import("better-auth").RawError<"REGISTRATION_CANCELLED">;
|
|
@@ -490,6 +491,7 @@ declare const passkey: (options?: PasskeyOptions | undefined) => {
|
|
|
490
491
|
body: import("zod").ZodObject<{
|
|
491
492
|
response: import("zod").ZodAny;
|
|
492
493
|
name: import("zod").ZodOptional<import("zod").ZodString>;
|
|
494
|
+
createSession: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
493
495
|
}, import("zod/v4/core").$strip>;
|
|
494
496
|
use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
495
497
|
session: {
|
|
@@ -524,7 +526,20 @@ declare const passkey: (options?: PasskeyOptions | undefined) => {
|
|
|
524
526
|
content: {
|
|
525
527
|
"application/json": {
|
|
526
528
|
schema: {
|
|
527
|
-
|
|
529
|
+
readonly type: "object";
|
|
530
|
+
readonly allOf: readonly [{
|
|
531
|
+
readonly $ref: "#/components/schemas/Passkey";
|
|
532
|
+
}, {
|
|
533
|
+
readonly type: "object";
|
|
534
|
+
readonly properties: {
|
|
535
|
+
readonly session: {
|
|
536
|
+
readonly $ref: "#/components/schemas/Session";
|
|
537
|
+
};
|
|
538
|
+
readonly user: {
|
|
539
|
+
readonly $ref: "#/components/schemas/User";
|
|
540
|
+
};
|
|
541
|
+
};
|
|
542
|
+
}];
|
|
528
543
|
};
|
|
529
544
|
};
|
|
530
545
|
};
|
|
@@ -815,6 +830,7 @@ declare const passkey: (options?: PasskeyOptions | undefined) => {
|
|
|
815
830
|
PASSKEY_NOT_FOUND: import("better-auth").RawError<"PASSKEY_NOT_FOUND">;
|
|
816
831
|
AUTHENTICATION_FAILED: import("better-auth").RawError<"AUTHENTICATION_FAILED">;
|
|
817
832
|
UNABLE_TO_CREATE_SESSION: import("better-auth").RawError<"UNABLE_TO_CREATE_SESSION">;
|
|
833
|
+
USER_NOT_FOUND: import("better-auth").RawError<"USER_NOT_FOUND">;
|
|
818
834
|
FAILED_TO_UPDATE_PASSKEY: import("better-auth").RawError<"FAILED_TO_UPDATE_PASSKEY">;
|
|
819
835
|
PREVIOUSLY_REGISTERED: import("better-auth").RawError<"PREVIOUSLY_REGISTERED">;
|
|
820
836
|
REGISTRATION_CANCELLED: import("better-auth").RawError<"REGISTRATION_CANCELLED">;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Passkey, c as PasskeyOptions, i as getAuthenticatorName, n as PASSKEY_ERROR_CODES, r as commonAuthenticatorNames, t as passkey } from "./index-
|
|
1
|
+
import { a as Passkey, c as PasskeyOptions, i as getAuthenticatorName, n as PASSKEY_ERROR_CODES, r as commonAuthenticatorNames, t as passkey } from "./index-CatEiPwC.mjs";
|
|
2
2
|
export { PASSKEY_ERROR_CODES, type Passkey, type PasskeyOptions, commonAuthenticatorNames, getAuthenticatorName, passkey };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-02cV8Fcg.mjs";
|
|
2
2
|
import { mergeSchema } from "better-auth/db";
|
|
3
3
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
4
|
+
import { getCurrentAdapter, runWithTransaction } from "@better-auth/core/context";
|
|
4
5
|
import { APIError } from "@better-auth/core/error";
|
|
5
6
|
import { base64 } from "@better-auth/utils/base64";
|
|
6
7
|
import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse } from "@simplewebauthn/server";
|
|
@@ -302,8 +303,19 @@ const generatePasskeyAuthenticationOptions = (opts, { maxAgeInSeconds }) => crea
|
|
|
302
303
|
});
|
|
303
304
|
const verifyPasskeyRegistrationBodySchema = z.object({
|
|
304
305
|
response: z.any(),
|
|
305
|
-
name: z.string().trim().meta({ description: "Name of the passkey" }).optional()
|
|
306
|
+
name: z.string().trim().meta({ description: "Name of the passkey" }).optional(),
|
|
307
|
+
createSession: z.boolean().meta({ description: "Create a session after registering the passkey" }).optional()
|
|
306
308
|
});
|
|
309
|
+
const passkeyRegistrationResponseSchema = {
|
|
310
|
+
type: "object",
|
|
311
|
+
allOf: [{ $ref: "#/components/schemas/Passkey" }, {
|
|
312
|
+
type: "object",
|
|
313
|
+
properties: {
|
|
314
|
+
session: { $ref: "#/components/schemas/Session" },
|
|
315
|
+
user: { $ref: "#/components/schemas/User" }
|
|
316
|
+
}
|
|
317
|
+
}]
|
|
318
|
+
};
|
|
307
319
|
const verifyPasskeyRegistration = (options) => {
|
|
308
320
|
const requireSession = options.registration?.requireSession ?? true;
|
|
309
321
|
return createAuthEndpoint("/passkey/verify-registration", {
|
|
@@ -316,7 +328,7 @@ const verifyPasskeyRegistration = (options) => {
|
|
|
316
328
|
responses: {
|
|
317
329
|
200: {
|
|
318
330
|
description: "Success",
|
|
319
|
-
content: { "application/json": { schema:
|
|
331
|
+
content: { "application/json": { schema: passkeyRegistrationResponseSchema } }
|
|
320
332
|
},
|
|
321
333
|
400: { description: "Bad request" }
|
|
322
334
|
}
|
|
@@ -345,47 +357,70 @@ const verifyPasskeyRegistration = (options) => {
|
|
|
345
357
|
const { verified, registrationInfo } = verification;
|
|
346
358
|
if (!verified || !registrationInfo) throw APIError.from("BAD_REQUEST", PASSKEY_ERROR_CODES.FAILED_TO_VERIFY_REGISTRATION);
|
|
347
359
|
const { aaguid, credentialDeviceType, credentialBackedUp, credential } = registrationInfo;
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
360
|
+
const persistRegistration = async () => {
|
|
361
|
+
const resolvedUser = {
|
|
362
|
+
id: userData.id,
|
|
363
|
+
name: userData.name || userData.id,
|
|
364
|
+
displayName: userData.displayName
|
|
365
|
+
};
|
|
366
|
+
let targetUserId = resolvedUser.id;
|
|
367
|
+
let resolvedName = ctx.body.name || void 0;
|
|
368
|
+
if (options.registration?.afterVerification) {
|
|
369
|
+
const result = await options.registration.afterVerification({
|
|
370
|
+
ctx,
|
|
371
|
+
verification,
|
|
372
|
+
user: resolvedUser,
|
|
373
|
+
clientData: resp,
|
|
374
|
+
context
|
|
375
|
+
});
|
|
376
|
+
if (result?.userId) {
|
|
377
|
+
if (typeof result.userId !== "string" || !result.userId) throw APIError.from("BAD_REQUEST", PASSKEY_ERROR_CODES.RESOLVED_USER_INVALID);
|
|
378
|
+
if (session?.user?.id && result.userId !== session.user.id) throw APIError.from("UNAUTHORIZED", PASSKEY_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY);
|
|
379
|
+
targetUserId = result.userId;
|
|
380
|
+
}
|
|
381
|
+
if (!resolvedName) resolvedName = result?.name?.trim() || void 0;
|
|
382
|
+
}
|
|
383
|
+
if (!targetUserId) throw APIError.from("BAD_REQUEST", PASSKEY_ERROR_CODES.RESOLVED_USER_INVALID);
|
|
384
|
+
const user = ctx.body.createSession ? await ctx.context.internalAdapter.findUserById(targetUserId) : null;
|
|
385
|
+
if (ctx.body.createSession && !user) throw APIError.from("INTERNAL_SERVER_ERROR", PASSKEY_ERROR_CODES.USER_NOT_FOUND);
|
|
386
|
+
const pubKey = base64.encode(credential.publicKey);
|
|
387
|
+
const passkey = await (await getCurrentAdapter(ctx.context.adapter)).create({
|
|
388
|
+
model: "passkey",
|
|
389
|
+
data: {
|
|
390
|
+
name: resolvedName,
|
|
391
|
+
userId: targetUserId,
|
|
392
|
+
credentialID: credential.id,
|
|
393
|
+
publicKey: pubKey,
|
|
394
|
+
counter: credential.counter,
|
|
395
|
+
deviceType: credentialDeviceType,
|
|
396
|
+
transports: resp.response.transports?.join(",") ?? "",
|
|
397
|
+
backedUp: credentialBackedUp,
|
|
398
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
399
|
+
aaguid
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
if (!user) return { passkey };
|
|
403
|
+
const createdSession = await ctx.context.internalAdapter.createSession(targetUserId, void 0, void 0, void 0, { deferSecondaryStorageWrites: true });
|
|
404
|
+
if (!createdSession) throw APIError.from("INTERNAL_SERVER_ERROR", PASSKEY_ERROR_CODES.UNABLE_TO_CREATE_SESSION);
|
|
405
|
+
return {
|
|
406
|
+
passkey,
|
|
407
|
+
session: createdSession,
|
|
408
|
+
user
|
|
409
|
+
};
|
|
352
410
|
};
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
verification,
|
|
359
|
-
user: resolvedUser,
|
|
360
|
-
clientData: resp,
|
|
361
|
-
context
|
|
411
|
+
const registration = ctx.body.createSession ? await runWithTransaction(ctx.context.adapter, persistRegistration) : await persistRegistration();
|
|
412
|
+
if (registration.session && registration.user) {
|
|
413
|
+
await setSessionCookie(ctx, {
|
|
414
|
+
session: registration.session,
|
|
415
|
+
user: registration.user
|
|
362
416
|
});
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
if (!resolvedName) resolvedName = result?.name?.trim() || void 0;
|
|
417
|
+
return ctx.json({
|
|
418
|
+
...registration.passkey,
|
|
419
|
+
session: registration.session,
|
|
420
|
+
user: registration.user
|
|
421
|
+
}, { status: 200 });
|
|
369
422
|
}
|
|
370
|
-
|
|
371
|
-
const pubKey = base64.encode(credential.publicKey);
|
|
372
|
-
const newPasskey = {
|
|
373
|
-
name: resolvedName,
|
|
374
|
-
userId: targetUserId,
|
|
375
|
-
credentialID: credential.id,
|
|
376
|
-
publicKey: pubKey,
|
|
377
|
-
counter: credential.counter,
|
|
378
|
-
deviceType: credentialDeviceType,
|
|
379
|
-
transports: resp.response.transports?.join(",") ?? "",
|
|
380
|
-
backedUp: credentialBackedUp,
|
|
381
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
382
|
-
aaguid
|
|
383
|
-
};
|
|
384
|
-
const newPasskeyRes = await ctx.context.adapter.create({
|
|
385
|
-
model: "passkey",
|
|
386
|
-
data: newPasskey
|
|
387
|
-
});
|
|
388
|
-
return ctx.json(newPasskeyRes, { status: 200 });
|
|
423
|
+
return ctx.json(registration.passkey, { status: 200 });
|
|
389
424
|
} catch (e) {
|
|
390
425
|
if (e instanceof APIError) throw e;
|
|
391
426
|
ctx.context.logger.error("Failed to verify registration", e);
|
|
@@ -7,6 +7,7 @@ const PASSKEY_ERROR_CODES = defineErrorCodes({
|
|
|
7
7
|
PASSKEY_NOT_FOUND: "Passkey not found",
|
|
8
8
|
AUTHENTICATION_FAILED: "Authentication failed",
|
|
9
9
|
UNABLE_TO_CREATE_SESSION: "Unable to create session",
|
|
10
|
+
USER_NOT_FOUND: "User not found",
|
|
10
11
|
FAILED_TO_UPDATE_PASSKEY: "Failed to update passkey",
|
|
11
12
|
PREVIOUSLY_REGISTERED: "Previously registered",
|
|
12
13
|
REGISTRATION_CANCELLED: "Registration cancelled",
|
|
@@ -18,6 +19,6 @@ const PASSKEY_ERROR_CODES = defineErrorCodes({
|
|
|
18
19
|
});
|
|
19
20
|
//#endregion
|
|
20
21
|
//#region src/version.ts
|
|
21
|
-
const PACKAGE_VERSION = "1.7.0-rc.
|
|
22
|
+
const PACKAGE_VERSION = "1.7.0-rc.3";
|
|
22
23
|
//#endregion
|
|
23
24
|
export { PASSKEY_ERROR_CODES as n, PACKAGE_VERSION as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/passkey",
|
|
3
|
-
"version": "1.7.0-rc.
|
|
3
|
+
"version": "1.7.0-rc.3",
|
|
4
4
|
"description": "Passkey plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,16 +56,16 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"nanostores": "^1.3.0",
|
|
58
58
|
"tsdown": "0.22.7",
|
|
59
|
-
"@better-auth/core": "1.7.0-rc.
|
|
60
|
-
"better-auth": "1.7.0-rc.
|
|
59
|
+
"@better-auth/core": "1.7.0-rc.3",
|
|
60
|
+
"better-auth": "1.7.0-rc.3"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"@better-auth/utils": "0.4.2",
|
|
64
64
|
"@better-fetch/fetch": "1.3.1",
|
|
65
65
|
"better-call": "1.3.7",
|
|
66
66
|
"nanostores": "^1.0.1",
|
|
67
|
-
"@better-auth/core": "^1.7.0-rc.
|
|
68
|
-
"better-auth": "^1.7.0-rc.
|
|
67
|
+
"@better-auth/core": "^1.7.0-rc.3",
|
|
68
|
+
"better-auth": "^1.7.0-rc.3"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "tsdown",
|