@better-auth/passkey 1.7.0-rc.2 → 1.7.0-rc.4

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-BKGmXRsk.mjs";
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: Passkey;
116
+ data: AddPasskeyResponse;
109
117
  error: null;
110
118
  } | {
111
119
  webauthn: {
112
120
  response: RegistrationResponseJSON;
113
121
  clientExtensionResults: AuthenticationExtensionsClientOutputs;
114
122
  };
115
- data: Passkey;
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: Passkey;
246
+ data: AddPasskeyResponse;
235
247
  error: null;
236
248
  } | {
237
249
  webauthn: {
238
250
  response: RegistrationResponseJSON;
239
251
  clientExtensionResults: AuthenticationExtensionsClientOutputs;
240
252
  };
241
- data: Passkey;
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-Dv6dkT5m.mjs";
1
+ import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-CT6Oulhl.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
- $ref: string;
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-BKGmXRsk.mjs";
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-Dv6dkT5m.mjs";
1
+ import { n as PASSKEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-CT6Oulhl.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: { $ref: "#/components/schemas/Passkey" } } }
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 resolvedUser = {
349
- id: userData.id,
350
- name: userData.name || userData.id,
351
- displayName: userData.displayName
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
- let targetUserId = resolvedUser.id;
354
- let resolvedName = ctx.body.name || void 0;
355
- if (options.registration?.afterVerification) {
356
- const result = await options.registration.afterVerification({
357
- ctx,
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
- if (result?.userId) {
364
- if (typeof result.userId !== "string" || !result.userId) throw APIError.from("BAD_REQUEST", PASSKEY_ERROR_CODES.RESOLVED_USER_INVALID);
365
- if (session?.user?.id && result.userId !== session.user.id) throw APIError.from("UNAUTHORIZED", PASSKEY_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY);
366
- targetUserId = result.userId;
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
- if (!targetUserId) throw APIError.from("BAD_REQUEST", PASSKEY_ERROR_CODES.RESOLVED_USER_INVALID);
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.2";
22
+ const PACKAGE_VERSION = "1.7.0-rc.4";
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.2",
3
+ "version": "1.7.0-rc.4",
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.2",
60
- "better-auth": "1.7.0-rc.2"
59
+ "@better-auth/core": "1.7.0-rc.4",
60
+ "better-auth": "1.7.0-rc.4"
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.2",
68
- "better-auth": "^1.7.0-rc.2"
67
+ "@better-auth/core": "^1.7.0-rc.4",
68
+ "better-auth": "^1.7.0-rc.4"
69
69
  },
70
70
  "scripts": {
71
71
  "build": "tsdown",