@robelest/convex-auth 0.0.4-preview.29 → 0.0.4-preview.30

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.
Files changed (49) hide show
  1. package/dist/bin.js +34 -1
  2. package/dist/browser/passkey.js +2 -2
  3. package/dist/client/core/types.d.ts +4 -15
  4. package/dist/client/factors/device.js +3 -3
  5. package/dist/client/factors/totp.js +8 -8
  6. package/dist/client/index.js +7 -7
  7. package/dist/component/convex.config.d.ts +2 -2
  8. package/dist/component/model.d.ts +25 -25
  9. package/dist/component/schema.d.ts +289 -289
  10. package/dist/core/index.d.ts +5 -24
  11. package/dist/core/index.js +3 -16
  12. package/dist/server/auth-context.d.ts +1 -1
  13. package/dist/server/auth-context.js +14 -1
  14. package/dist/server/auth.d.ts +3 -17
  15. package/dist/server/auth.js +2 -7
  16. package/dist/server/config.js +10 -0
  17. package/dist/server/context.js +2 -4
  18. package/dist/server/contract.d.ts +1 -1
  19. package/dist/server/convexIdentity.d.ts +15 -0
  20. package/dist/server/convexIdentity.js +1 -0
  21. package/dist/server/core.js +0 -13
  22. package/dist/server/device.js +13 -12
  23. package/dist/server/env.js +10 -2
  24. package/dist/server/http.d.ts +1 -1
  25. package/dist/server/identity.js +30 -4
  26. package/dist/server/index.d.ts +1 -0
  27. package/dist/server/index.js +1 -0
  28. package/dist/server/mounts.d.ts +79 -79
  29. package/dist/server/mutations/refresh.js +38 -7
  30. package/dist/server/mutations/signin.js +12 -2
  31. package/dist/server/mutations/signout.js +27 -10
  32. package/dist/server/mutations/store.js +1 -1
  33. package/dist/server/oauth/factory.js +2 -1
  34. package/dist/server/passkey.js +12 -13
  35. package/dist/server/prefetch.js +8 -8
  36. package/dist/server/runtime.d.ts +19 -25
  37. package/dist/server/runtime.js +7 -40
  38. package/dist/server/sessions.d.ts +2 -1
  39. package/dist/server/sessions.js +21 -11
  40. package/dist/server/signin.js +17 -16
  41. package/dist/server/sso/domain.d.ts +1 -1
  42. package/dist/server/telemetry.js +58 -0
  43. package/dist/server/tokens.js +24 -4
  44. package/dist/server/totp.js +13 -11
  45. package/dist/server/types.d.ts +58 -2
  46. package/dist/server/utils/span.js +10 -1
  47. package/dist/shared/authResults.d.ts +16 -0
  48. package/package.json +6 -3
  49. package/dist/server/constants.js +0 -6
@@ -2,8 +2,9 @@ import { authDb } from "../db.js";
2
2
  import { log, maybeRedact } from "../log.js";
3
3
  import { AUTH_STORE_REF } from "./store/refs.js";
4
4
  import { REFRESH_TOKEN_DIVIDER, REFRESH_TOKEN_REUSE_WINDOW_MS, parseRefreshToken, refreshTokenExpirationTime } from "../refresh.js";
5
- import { withSpan } from "../utils/span.js";
5
+ import { setActiveSpanAttributes, withSpan } from "../utils/span.js";
6
6
  import { finalizeSessionIssuance } from "../sessions.js";
7
+ import { buildRefreshIdentityAttributes } from "../telemetry.js";
7
8
  import { v } from "convex/values";
8
9
 
9
10
  //#region src/server/mutations/refresh.ts
@@ -18,14 +19,17 @@ const refreshSessionArgs = v.object({ refreshToken: v.string() });
18
19
  */
19
20
  async function refreshSessionImpl(ctx, args, config) {
20
21
  const db = authDb(ctx, config);
21
- return withSpan("convex-auth.refresh.session", { hasRefreshToken: true }, async () => {
22
+ return withSpan("convex-auth.refresh.session", {
23
+ hasRefreshToken: true,
24
+ "auth.flow": "refresh"
25
+ }, async () => {
22
26
  try {
23
27
  let refreshTokenId;
24
28
  let sessionId;
25
29
  try {
26
30
  ({refreshTokenId, sessionId} = parseRefreshToken(args.refreshToken));
27
31
  } catch {
28
- throw new RefreshFailure("Failed to parse refresh token");
32
+ throw new RefreshFailure("parse_failure", "Failed to parse refresh token");
29
33
  }
30
34
  log("DEBUG", `refreshSessionImpl args: Token ID: ${maybeRedact(refreshTokenId)} Session ID: ${maybeRedact(sessionId)}`);
31
35
  let exchanged;
@@ -38,16 +42,42 @@ async function refreshSessionImpl(ctx, args, config) {
38
42
  reuseWindowMs: REFRESH_TOKEN_REUSE_WINDOW_MS
39
43
  });
40
44
  } catch {
41
- throw new RefreshFailure("Failed to exchange refresh token");
45
+ throw new RefreshFailure("exchange_failure", "Failed to exchange refresh token");
42
46
  }
43
- if (exchanged === null) return null;
47
+ if (exchanged === null) {
48
+ setActiveSpanAttributes({ "auth.refresh.result": "null" });
49
+ return null;
50
+ }
51
+ setActiveSpanAttributes({
52
+ "auth.refresh.result": "success",
53
+ ...await buildRefreshIdentityAttributes(ctx, config, {
54
+ userId: exchanged.userId,
55
+ sessionId: exchanged.sessionId,
56
+ refreshTokenId: exchanged.refreshTokenId
57
+ })
58
+ });
59
+ const user = await db.users.getById(exchanged.userId);
44
60
  return {
45
61
  userId: exchanged.userId,
46
62
  sessionId: exchanged.sessionId,
63
+ identity: {
64
+ subject: exchanged.userId,
65
+ sessionId: exchanged.sessionId,
66
+ ...typeof user?.name === "string" ? { name: user.name } : null,
67
+ ...typeof user?.email === "string" ? { email: user.email } : null,
68
+ ...user?.emailVerificationTime !== void 0 ? { emailVerified: true } : user?.email !== void 0 ? { emailVerified: false } : null,
69
+ ...typeof user?.image === "string" ? { picture: user.image } : null,
70
+ ...typeof user?.phone === "string" ? { phoneNumber: user.phone } : null,
71
+ ...user?.phoneVerificationTime !== void 0 ? { phoneNumberVerified: true } : user?.phone !== void 0 ? { phoneNumberVerified: false } : null
72
+ },
47
73
  refreshToken: `${exchanged.refreshTokenId}${REFRESH_TOKEN_DIVIDER}${exchanged.sessionId}`
48
74
  };
49
75
  } catch (e) {
50
76
  if (e instanceof RefreshFailure) {
77
+ setActiveSpanAttributes({
78
+ "auth.refresh.result": e.code,
79
+ "auth.refresh.failure_reason": e.reason
80
+ });
51
81
  log("DEBUG", e.reason);
52
82
  return null;
53
83
  }
@@ -56,8 +86,9 @@ async function refreshSessionImpl(ctx, args, config) {
56
86
  });
57
87
  }
58
88
  var RefreshFailure = class extends Error {
59
- constructor(reason) {
89
+ constructor(code, reason) {
60
90
  super(reason);
91
+ this.code = code;
61
92
  this.reason = reason;
62
93
  }
63
94
  };
@@ -74,7 +105,7 @@ const callRefreshSession = async (ctx, args) => {
74
105
  ...args
75
106
  } });
76
107
  if (issuance === null || issuance.refreshToken === null) return null;
77
- return (await finalizeSessionIssuance(ctx.auth.config, issuance)).tokens;
108
+ return await finalizeSessionIssuance(ctx.auth.config, issuance);
78
109
  };
79
110
 
80
111
  //#endregion
@@ -1,8 +1,9 @@
1
1
  import { LOG_LEVELS } from "../../shared/log.js";
2
2
  import { log } from "../log.js";
3
3
  import { AUTH_STORE_REF } from "./store/refs.js";
4
- import { withSpan } from "../utils/span.js";
4
+ import { setActiveSpanAttributes, withSpan } from "../utils/span.js";
5
5
  import { finalizeSessionIssuance, getAuthSessionId, issueSession } from "../sessions.js";
6
+ import { buildSignInIdentityAttributes } from "../telemetry.js";
6
7
  import { v } from "convex/values";
7
8
 
8
9
  //#region src/server/mutations/signin.ts
@@ -13,17 +14,26 @@ const signInArgs = v.object({
13
14
  });
14
15
  async function signInImpl(ctx, args, config) {
15
16
  return withSpan("convex-auth.mutations.signIn", {
17
+ "auth.flow": "signIn",
16
18
  hasExistingSession: args.sessionId !== void 0,
17
19
  generateTokens: args.generateTokens
18
20
  }, async () => {
19
21
  log(LOG_LEVELS.DEBUG, "signInImpl args:", args);
20
22
  const { userId, sessionId: existingSessionId, generateTokens } = args;
21
- return await issueSession(ctx, config, {
23
+ const issuance = await issueSession(ctx, config, {
22
24
  userId,
23
25
  existingSessionId,
24
26
  replaceSessionId: existingSessionId === void 0 ? await getAuthSessionId(ctx) ?? void 0 : void 0,
25
27
  generateTokens
26
28
  });
29
+ setActiveSpanAttributes({
30
+ "auth.signin.result": "success",
31
+ ...await buildSignInIdentityAttributes(ctx, config, {
32
+ userId: issuance.userId,
33
+ sessionId: issuance.sessionId
34
+ })
35
+ });
36
+ return issuance;
27
37
  });
28
38
  }
29
39
  /**
@@ -1,19 +1,36 @@
1
1
  import { authDb } from "../db.js";
2
2
  import { AUTH_STORE_REF } from "./store/refs.js";
3
+ import { setActiveSpanAttributes, withSpan } from "../utils/span.js";
3
4
  import { deleteSession, getAuthSessionId } from "../sessions.js";
5
+ import { buildSignOutIdentityAttributes } from "../telemetry.js";
4
6
 
5
7
  //#region src/server/mutations/signout.ts
6
8
  async function signOutImpl(ctx, config) {
7
- const db = authDb(ctx, config);
8
- const sessionId = await getAuthSessionId(ctx);
9
- if (sessionId == null) return null;
10
- const session = await db.sessions.getById(sessionId);
11
- if (session == null) return null;
12
- await deleteSession(ctx, session, config);
13
- return {
14
- userId: session.userId,
15
- sessionId: session._id
16
- };
9
+ return withSpan("convex-auth.mutations.signOut", { "auth.flow": "signOut" }, async () => {
10
+ const db = authDb(ctx, config);
11
+ const sessionId = await getAuthSessionId(ctx);
12
+ if (sessionId == null) {
13
+ setActiveSpanAttributes({ "auth.signout.result": "no_session" });
14
+ return null;
15
+ }
16
+ const session = await db.sessions.getById(sessionId);
17
+ if (session == null) {
18
+ setActiveSpanAttributes({ "auth.signout.result": "session_missing" });
19
+ return null;
20
+ }
21
+ await deleteSession(ctx, session, config);
22
+ setActiveSpanAttributes({
23
+ "auth.signout.result": "success",
24
+ ...await buildSignOutIdentityAttributes(ctx, config, {
25
+ userId: session.userId,
26
+ sessionId: session._id
27
+ })
28
+ });
29
+ return {
30
+ userId: session.userId,
31
+ sessionId: session._id
32
+ };
33
+ });
17
34
  }
18
35
  const callSignOut = async (ctx) => {
19
36
  return ctx.runMutation(AUTH_STORE_REF, { args: { type: "signOut" } });
@@ -57,7 +57,7 @@ const storeImpl = async (ctx, fnArgs, services) => {
57
57
  const args = fnArgs.args;
58
58
  const config = services.config;
59
59
  const getProviderOrThrow = services.providerRegistry.getProviderOrThrow;
60
- log(LOG_LEVELS.DEBUG, `\`auth:store\` type: ${args.type}`);
60
+ if (args.type !== "refreshSession") log(LOG_LEVELS.DEBUG, `\`auth:store\` type: ${args.type}`);
61
61
  const handler = {
62
62
  signIn: (a) => signInImpl(ctx, a, config),
63
63
  signOut: () => signOutImpl(ctx, config),
@@ -2,11 +2,12 @@
2
2
  function normalizeTokens(tokens) {
3
3
  const raw = tokens.data;
4
4
  const rawScopes = typeof raw.scope === "string" ? raw.scope : void 0;
5
+ const expiresInSeconds = typeof raw.expires_in === "number" ? raw.expires_in : void 0;
5
6
  return {
6
7
  accessToken: typeof raw.access_token === "string" ? raw.access_token : void 0,
7
8
  refreshToken: typeof raw.refresh_token === "string" ? raw.refresh_token : void 0,
8
9
  idToken: typeof raw.id_token === "string" ? raw.id_token : void 0,
9
- accessTokenExpiresAt: typeof tokens.accessTokenExpiresAt === "function" ? tokens.accessTokenExpiresAt() : typeof raw.expires_in === "number" ? new Date(Date.now() + raw.expires_in * 1e3) : void 0,
10
+ accessTokenExpiresAt: expiresInSeconds === void 0 ? void 0 : new Date(Date.now() + expiresInSeconds * 1e3),
10
11
  scopes: rawScopes ? rawScopes.split(/[,\s]+/).map((scope) => scope.trim()).filter((scope) => scope.length > 0) : void 0,
11
12
  raw: tokens.data
12
13
  };
@@ -1,4 +1,4 @@
1
- import { userIdFromIdentitySubject } from "./identity.js";
1
+ import { getAuthenticatedUserIdOrNull } from "./identity.js";
2
2
  import { authDb } from "./db.js";
3
3
  import { callSignIn } from "./mutations/signin.js";
4
4
  import { callVerifier } from "./mutations/verifier.js";
@@ -114,14 +114,13 @@ function requirePasskeyVerifier(verifier) {
114
114
  throw convexError("PASSKEY_MISSING_VERIFIER", "Missing verifier for passkey operation.");
115
115
  }
116
116
  async function requireAuthenticatedUserId(ctx) {
117
- let identity;
118
117
  try {
119
- identity = await ctx.auth.getUserIdentity();
118
+ const userId = await getAuthenticatedUserIdOrNull(ctx);
119
+ if (userId === null) throw convexError("PASSKEY_AUTH_REQUIRED", "Sign in first, then add a passkey to your account.");
120
+ return userId;
120
121
  } catch {
121
122
  throw convexError("PASSKEY_AUTH_REQUIRED", "Sign in first, then add a passkey to your account.");
122
123
  }
123
- if (identity === null) throw convexError("PASSKEY_AUTH_REQUIRED", "Sign in first, then add a passkey to your account.");
124
- return userIdFromIdentitySubject(identity.subject);
125
124
  }
126
125
  function resolveRegistrationPublicKeyBytes(publicKey, algorithm) {
127
126
  if (algorithm === coseAlgorithmES256) {
@@ -272,12 +271,12 @@ async function handlePasskeyFx(ctx, provider, args) {
272
271
  userId,
273
272
  generateTokens: true
274
273
  });
275
- } catch {
276
- throw convexError("INTERNAL_ERROR", "An unexpected error occurred.");
274
+ } catch (error) {
275
+ throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey registration.");
277
276
  }
278
277
  return {
279
278
  kind: "signedIn",
280
- signedIn: signInResult
279
+ session: signInResult
281
280
  };
282
281
  },
283
282
  authOptions: async () => {
@@ -354,8 +353,8 @@ async function handlePasskeyFx(ctx, provider, args) {
354
353
  if (passkey.counter !== 0 && authenticatorData.signatureCounter !== 0 && authenticatorData.signatureCounter <= passkey.counter) throw convexError("PASSKEY_COUNTER_ERROR", "Authenticator counter did not increase — possible credential cloning detected.");
355
354
  try {
356
355
  await mutatePasskeyUpdateCounter(ctx, passkey._id, authenticatorData.signatureCounter, Date.now());
357
- } catch {
358
- throw convexError("INTERNAL_ERROR", "An unexpected error occurred.");
356
+ } catch (error) {
357
+ throw asConvexError(error, "INTERNAL_ERROR", "Failed to update passkey counter.");
359
358
  }
360
359
  let signInResult;
361
360
  try {
@@ -363,12 +362,12 @@ async function handlePasskeyFx(ctx, provider, args) {
363
362
  userId: passkey.userId,
364
363
  generateTokens: true
365
364
  });
366
- } catch {
367
- throw convexError("INTERNAL_ERROR", "An unexpected error occurred.");
365
+ } catch (error) {
366
+ throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey sign-in.");
368
367
  }
369
368
  return {
370
369
  kind: "signedIn",
371
- signedIn: signInResult
370
+ session: signInResult
372
371
  };
373
372
  }
374
373
  }[dispatch.flow];
@@ -185,7 +185,7 @@ function getProxyErrorBody(error) {
185
185
  } : { error: error instanceof Error ? error.message : String(error) };
186
186
  }
187
187
  function extractSignedInTokens(result, context) {
188
- if (result.kind === "signedIn") return result.tokens;
188
+ if (result.kind === "signedIn") return result.session;
189
189
  throw new Error(`Invalid \`auth:signIn\` result for ${context}`);
190
190
  }
191
191
  function normalizeCookieNamespace(cookieNamespace) {
@@ -360,19 +360,19 @@ function server(options) {
360
360
  verifier: result.verifier
361
361
  }, host$1, cookieConfig, cookieNamespace));
362
362
  if (result.kind === "signedIn") {
363
- const nextCookies = result.tokens === null ? {
363
+ const nextCookies = result.session === null ? {
364
364
  token: currentCookies$1.token,
365
365
  refreshToken: currentCookies$1.refreshToken,
366
366
  verifier: null
367
367
  } : {
368
- token: result.tokens.token,
369
- refreshToken: result.tokens.refreshToken,
368
+ token: result.session.token,
369
+ refreshToken: result.session.refreshToken,
370
370
  verifier: null
371
371
  };
372
372
  return appendCookieHeaders(jsonResponse({
373
373
  kind: "signedIn",
374
- tokens: result.tokens === null ? null : {
375
- token: result.tokens.token,
374
+ session: result.session === null ? null : {
375
+ token: result.session.token,
376
376
  refreshToken: "dummy"
377
377
  }
378
378
  }), serializeAuthCookies(nextCookies, host$1, cookieConfig, cookieNamespace));
@@ -416,11 +416,11 @@ function server(options) {
416
416
  const action = body.action;
417
417
  const args = typeof body.args === "object" && body.args !== null ? { ...body.args } : {};
418
418
  if (args.refreshToken === null) args.refreshToken = void 0;
419
- const actionDispatch = action === "auth:signIn" ? { action: "sessionStart" } : action === "auth:signOut" ? { action: "sessionStop" } : null;
419
+ const actionDispatch = action === "auth:signIn" ? { action: "signInStart" } : action === "auth:signOut" ? { action: "sessionStop" } : null;
420
420
  if (actionDispatch === null) return new Response("Invalid action", { status: 400 });
421
421
  const host = request.headers.get("host") ?? new URL(request.url).host;
422
422
  const currentCookies = parseAuthCookies(request.headers.get("cookie"), host, cookieNamespace);
423
- if (actionDispatch.action === "sessionStart") {
423
+ if (actionDispatch.action === "signInStart") {
424
424
  let refreshResponse = null;
425
425
  if (args.refreshToken === void 0) refreshResponse = null;
426
426
  else if (currentCookies.refreshToken === null) {
@@ -1,10 +1,10 @@
1
- import { ComponentCtx, ComponentReadCtx } from "./componentContext.js";
2
1
  import { AuthProviderConfig, ConvexAuthConfig, CorsConfig, Doc, HttpKeyContext, KeyDoc, KeyScope, ScopeChecker, UserOrderBy, UserWhere } from "./types.js";
2
+ import { ComponentCtx, ComponentReadCtx } from "./componentContext.js";
3
3
  import { AuthProfile, SignInParams } from "./payloads.js";
4
4
  import { HttpAuthContext, HttpAuthContextConfig, OptionalHttpAuthContext } from "./http.js";
5
5
  import { createGroupConnectionDomain } from "./sso/domain.js";
6
- import * as convex_values1137 from "convex/values";
7
- import * as convex_server108 from "convex/server";
6
+ import * as convex_values86 from "convex/values";
7
+ import * as convex_server80 from "convex/server";
8
8
  import { GenericActionCtx, GenericDataModel, HttpRouter } from "convex/server";
9
9
 
10
10
  //#region src/server/runtime.d.ts
@@ -44,7 +44,7 @@ declare function Auth(config_: ConvexAuthConfig): {
44
44
  order?: "asc" | "desc";
45
45
  }) => Promise<any>;
46
46
  viewer: (ctx: ComponentReadCtx & {
47
- auth: convex_server108.Auth;
47
+ auth: convex_server80.Auth;
48
48
  }) => Promise<Doc<"User"> | null>;
49
49
  update: (ctx: ComponentCtx, userId: string, data: Record<string, unknown>) => Promise<{
50
50
  userId: string;
@@ -69,18 +69,12 @@ declare function Auth(config_: ConvexAuthConfig): {
69
69
  }>;
70
70
  };
71
71
  session: {
72
- current: (ctx: {
73
- auth: convex_server108.Auth;
74
- }) => Promise<convex_values1137.GenericId<"Session"> | null>;
75
- userId: (ctx: {
76
- auth: convex_server108.Auth;
77
- }) => Promise<convex_values1137.GenericId<"User"> | null>;
78
72
  invalidate: <DataModel extends GenericDataModel>(ctx: GenericActionCtx<DataModel>, args: {
79
- userId: convex_values1137.GenericId<"User">;
80
- except?: convex_values1137.GenericId<"Session">[];
73
+ userId: convex_values86.GenericId<"User">;
74
+ except?: convex_values86.GenericId<"Session">[];
81
75
  }) => Promise<{
82
- userId: convex_values1137.GenericId<"User">;
83
- except: convex_values1137.GenericId<"Session">[];
76
+ userId: convex_values86.GenericId<"User">;
77
+ except: convex_values86.GenericId<"Session">[];
84
78
  }>;
85
79
  get: (ctx: ComponentReadCtx, sessionId: string) => Promise<any>;
86
80
  list: (ctx: ComponentReadCtx, opts: {
@@ -149,7 +143,7 @@ declare function Auth(config_: ConvexAuthConfig): {
149
143
  };
150
144
  provider: {
151
145
  signIn: (<DataModel extends GenericDataModel>(ctx: GenericActionCtx<DataModel>, providerConfig: AuthProviderConfig, args: {
152
- accountId?: convex_values1137.GenericId<"Account">;
146
+ accountId?: convex_values86.GenericId<"Account">;
153
147
  params?: SignInParams;
154
148
  }) => Promise<{
155
149
  userId: string;
@@ -457,22 +451,22 @@ declare function Auth(config_: ConvexAuthConfig): {
457
451
  context: {
458
452
  <TResolve extends Record<string, unknown> = Record<string, never>, TCtx extends {
459
453
  auth: {
460
- getUserIdentity: () => Promise<convex_server108.UserIdentity | null>;
454
+ getUserIdentity: () => Promise<convex_server80.UserIdentity | null>;
461
455
  };
462
456
  } & ComponentReadCtx = {
463
457
  auth: {
464
- getUserIdentity: () => Promise<convex_server108.UserIdentity | null>;
458
+ getUserIdentity: () => Promise<convex_server80.UserIdentity | null>;
465
459
  };
466
460
  } & ComponentReadCtx>(ctx: TCtx, request: Request, config: HttpAuthContextConfig<TResolve, TCtx> & {
467
461
  optional: true;
468
462
  }): Promise<OptionalHttpAuthContext & TResolve>;
469
463
  <TResolve extends Record<string, unknown> = Record<string, never>, TCtx extends {
470
464
  auth: {
471
- getUserIdentity: () => Promise<convex_server108.UserIdentity | null>;
465
+ getUserIdentity: () => Promise<convex_server80.UserIdentity | null>;
472
466
  };
473
467
  } & ComponentReadCtx = {
474
468
  auth: {
475
- getUserIdentity: () => Promise<convex_server108.UserIdentity | null>;
469
+ getUserIdentity: () => Promise<convex_server80.UserIdentity | null>;
476
470
  };
477
471
  } & ComponentReadCtx>(ctx: TCtx, request: Request, config?: HttpAuthContextConfig<TResolve, TCtx>): Promise<HttpAuthContext & TResolve>;
478
472
  };
@@ -506,7 +500,7 @@ declare function Auth(config_: ConvexAuthConfig): {
506
500
  action: string;
507
501
  };
508
502
  cors?: CorsConfig;
509
- }) => convex_server108.PublicHttpAction;
503
+ }) => convex_server80.PublicHttpAction;
510
504
  /**
511
505
  * Register a Bearer-authenticated route **and** its OPTIONS preflight
512
506
  * in a single call.
@@ -552,7 +546,7 @@ declare function Auth(config_: ConvexAuthConfig): {
552
546
  *
553
547
  * Also used for refreshing the session.
554
548
  */
555
- signIn: convex_server108.RegisteredAction<"public", {
549
+ signIn: convex_server80.RegisteredAction<"public", {
556
550
  provider?: string | undefined;
557
551
  verifier?: string | undefined;
558
552
  params?: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null> | undefined;
@@ -562,12 +556,12 @@ declare function Auth(config_: ConvexAuthConfig): {
562
556
  /**
563
557
  * Action called by the client to invalidate the current session.
564
558
  */
565
- signOut: convex_server108.RegisteredAction<"public", {}, Promise<void>>;
559
+ signOut: convex_server80.RegisteredAction<"public", {}, Promise<void>>;
566
560
  /**
567
561
  * Internal mutation used by the library to read and write
568
562
  * to the database during signin and signout.
569
563
  */
570
- store: convex_server108.RegisteredMutation<"internal", {
564
+ store: convex_server80.RegisteredMutation<"internal", {
571
565
  args: {
572
566
  sessionId?: string | undefined;
573
567
  type: "signIn";
@@ -610,11 +604,11 @@ declare function Auth(config_: ConvexAuthConfig): {
610
604
  sessionIndex?: string | undefined;
611
605
  } | undefined;
612
606
  } | undefined;
607
+ profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
613
608
  type: "userOAuth";
614
609
  provider: string;
615
610
  providerAccountId: string;
616
611
  signature: string;
617
- profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
618
612
  } | {
619
613
  email?: string | undefined;
620
614
  phone?: string | undefined;
@@ -627,9 +621,9 @@ declare function Auth(config_: ConvexAuthConfig): {
627
621
  } | {
628
622
  shouldLinkViaEmail?: boolean | undefined;
629
623
  shouldLinkViaPhone?: boolean | undefined;
624
+ profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
630
625
  type: "createAccountFromCredentials";
631
626
  provider: string;
632
- profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
633
627
  account: {
634
628
  secret?: string | undefined;
635
629
  id: string;
@@ -227,49 +227,16 @@ function Auth(config_) {
227
227
  resolveSsoProtocol: group.resolveGroupConnectionSsoProtocolOrThrow
228
228
  });
229
229
  const handler = {
230
- redirect: (r) => ({
231
- kind: "redirect",
232
- redirect: r.redirect,
233
- verifier: r.verifier
234
- }),
230
+ redirect: (r) => r,
235
231
  signedIn: (r) => ({
236
232
  kind: "signedIn",
237
- tokens: r.signedIn?.tokens ?? null
238
- }),
239
- refreshTokens: (r) => ({
240
- kind: "signedIn",
241
- tokens: r.signedIn?.tokens ?? null
242
- }),
243
- started: () => ({ kind: "started" }),
244
- passkeyOptions: (r) => ({
245
- kind: "passkeyOptions",
246
- options: r.options,
247
- verifier: r.verifier
248
- }),
249
- totpRequired: (r) => ({
250
- kind: "totpRequired",
251
- verifier: r.verifier
252
- }),
253
- totpSetup: (r) => ({
254
- kind: "totpSetup",
255
- totpSetup: {
256
- uri: r.uri,
257
- secret: r.secret,
258
- totpId: r.totpId
259
- },
260
- verifier: r.verifier
233
+ session: r.session?.tokens ?? null
261
234
  }),
262
- deviceCode: (r) => ({
263
- kind: "deviceCode",
264
- deviceCode: {
265
- deviceCode: r.deviceCode,
266
- userCode: r.userCode,
267
- verificationUri: r.verificationUri,
268
- verificationUriComplete: r.verificationUriComplete,
269
- expiresIn: r.expiresIn,
270
- interval: r.interval
271
- }
272
- })
235
+ started: (r) => r,
236
+ passkeyOptions: (r) => r,
237
+ totpRequired: (r) => r,
238
+ totpSetup: (r) => r,
239
+ deviceCode: (r) => r
273
240
  }[result.kind];
274
241
  if (!handler) throw new Error(`Unexpected sign-in result kind: ${result.kind}`);
275
242
  return handler(result);
@@ -1,4 +1,4 @@
1
- import "./types.js";
1
+ import { SessionTokenIdentityClaims } from "./types.js";
2
2
  import { GenericId } from "convex/values";
3
3
 
4
4
  //#region src/server/sessions.d.ts
@@ -10,6 +10,7 @@ import { GenericId } from "convex/values";
10
10
  type SessionIssuance = {
11
11
  userId: GenericId<"User">;
12
12
  sessionId: GenericId<"Session">;
13
+ identity: SessionTokenIdentityClaims;
13
14
  /**
14
15
  * Encoded refresh token (`${refreshTokenId}|${sessionId}`), or `null` when
15
16
  * the caller opted out of refresh-token issuance (e.g. TOTP step-up).
@@ -1,15 +1,28 @@
1
1
  import { LOG_LEVELS } from "../shared/log.js";
2
+ import { getAuthenticatedSessionIdOrNull } from "./identity.js";
2
3
  import { authDb } from "./db.js";
3
4
  import { envOptionalNumber, readConfigSync } from "./env.js";
4
5
  import { log, maybeRedact } from "./log.js";
5
6
  import { REFRESH_TOKEN_DIVIDER, refreshTokenExpirationTime } from "./refresh.js";
6
7
  import { withSpan } from "./utils/span.js";
7
- import { TOKEN_SUB_CLAIM_DIVIDER, generateToken } from "./tokens.js";
8
+ import { generateToken } from "./tokens.js";
8
9
 
9
10
  //#region src/server/sessions.ts
10
11
  const DEFAULT_SESSION_TOTAL_DURATION_MS = 1e3 * 60 * 60 * 24 * 30;
11
12
  const sessionExpirationTime = (config, now = Date.now()) => now + (config.session?.totalDurationMs ?? readConfigSync(envOptionalNumber("AUTH_SESSION_TOTAL_DURATION_MS")) ?? DEFAULT_SESSION_TOTAL_DURATION_MS);
12
13
  const encodeRefreshToken = (refreshTokenId, sessionId) => `${refreshTokenId}${REFRESH_TOKEN_DIVIDER}${sessionId}`;
14
+ function buildSessionIdentity(userId, sessionId, user) {
15
+ return {
16
+ subject: userId,
17
+ sessionId,
18
+ ...typeof user?.name === "string" ? { name: user.name } : null,
19
+ ...typeof user?.email === "string" ? { email: user.email } : null,
20
+ ...user?.emailVerificationTime !== void 0 ? { emailVerified: true } : user?.email !== void 0 ? { emailVerified: false } : null,
21
+ ...typeof user?.image === "string" ? { picture: user.image } : null,
22
+ ...typeof user?.phone === "string" ? { phoneNumber: user.phone } : null,
23
+ ...user?.phoneVerificationTime !== void 0 ? { phoneNumberVerified: true } : user?.phone !== void 0 ? { phoneNumberVerified: false } : null
24
+ };
25
+ }
13
26
  /**
14
27
  * Convert a {@link SessionIssuance} returned from a mutation into the
15
28
  * external `SessionInfo` shape by signing the JWT on the action side.
@@ -26,10 +39,7 @@ async function finalizeSessionIssuance(config, issuance) {
26
39
  sessionId: issuance.sessionId,
27
40
  tokens: null
28
41
  };
29
- const token = await generateToken({
30
- userId: issuance.userId,
31
- sessionId: issuance.sessionId
32
- }, config);
42
+ const token = await generateToken({ identity: issuance.identity }, config);
33
43
  log(LOG_LEVELS.DEBUG, `Generated token ${maybeRedact(token)} and refresh token ${maybeRedact(issuance.refreshToken)} for session ${maybeRedact(issuance.sessionId)}`);
34
44
  return {
35
45
  userId: issuance.userId,
@@ -43,7 +53,8 @@ async function finalizeSessionIssuance(config, issuance) {
43
53
  }
44
54
  /** @internal */
45
55
  async function issueSession(ctx, config, args) {
46
- const issued = await authDb(ctx, config).sessions.issue({
56
+ const db = authDb(ctx, config);
57
+ const issued = await db.sessions.issue({
47
58
  userId: args.userId,
48
59
  sessionId: args.existingSessionId,
49
60
  replaceSessionId: args.replaceSessionId,
@@ -52,9 +63,11 @@ async function issueSession(ctx, config, args) {
52
63
  });
53
64
  const sessionId = issued.sessionId;
54
65
  const refreshTokenId = issued.refreshTokenId;
66
+ const user = await db.users.getById(issued.userId);
55
67
  return {
56
68
  userId: issued.userId,
57
69
  sessionId,
70
+ identity: buildSessionIdentity(issued.userId, sessionId, user),
58
71
  refreshToken: args.generateTokens && refreshTokenId !== void 0 ? encodeRefreshToken(refreshTokenId, sessionId) : null
59
72
  };
60
73
  }
@@ -67,14 +80,11 @@ async function deleteSession(ctx, session, config) {
67
80
  /**
68
81
  * Return the current session ID from the auth identity subject.
69
82
  *
70
- * Internal helper used by auth runtime internals and `auth.session.current`.
83
+ * Internal helper used by auth runtime internals.
71
84
  */
72
85
  /** @internal */
73
86
  async function getAuthSessionId(ctx) {
74
- const identity = await ctx.auth.getUserIdentity();
75
- if (identity === null) return null;
76
- const [, sessionId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
77
- return sessionId;
87
+ return await getAuthenticatedSessionIdOrNull(ctx);
78
88
  }
79
89
 
80
90
  //#endregion