@robelest/convex-auth 0.0.4-preview.27 → 0.0.4-preview.28

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 (88) hide show
  1. package/README.md +3 -5
  2. package/dist/bin.js +6488 -1571
  3. package/dist/browser/index.js +10 -7
  4. package/dist/browser/locks.js +3 -5
  5. package/dist/browser/navigation.js +7 -10
  6. package/dist/browser/runtime.js +35 -33
  7. package/dist/client/core/types.js +17 -0
  8. package/dist/client/factors/device.js +26 -19
  9. package/dist/client/index.js +151 -163
  10. package/dist/client/runtime/proxy.js +6 -6
  11. package/dist/client/services/adapters.js +3 -7
  12. package/dist/client/services/http.js +2 -5
  13. package/dist/client/services/resolve.js +5 -11
  14. package/dist/client/services/runtime.js +2 -5
  15. package/dist/component/_generated/component.d.ts +46 -0
  16. package/dist/component/index.d.ts +3 -3
  17. package/dist/component/model.d.ts +25 -25
  18. package/dist/component/public/identity/sessions.js +38 -1
  19. package/dist/component/public/identity/tokens.js +81 -3
  20. package/dist/component/public/identity/verifiers.js +9 -3
  21. package/dist/component/public.js +3 -3
  22. package/dist/component/schema.d.ts +320 -320
  23. package/dist/core/index.d.ts +380 -0
  24. package/dist/core/index.js +83 -0
  25. package/dist/otel.d.ts +13 -17
  26. package/dist/otel.js +39 -49
  27. package/dist/providers/email.d.ts +2 -2
  28. package/dist/providers/password.js +8 -16
  29. package/dist/providers/phone.js +2 -9
  30. package/dist/server/auth-context.d.ts +204 -0
  31. package/dist/server/auth-context.js +76 -0
  32. package/dist/server/auth.d.ts +25 -187
  33. package/dist/server/auth.js +5 -96
  34. package/dist/server/componentContext.d.ts +12 -0
  35. package/dist/server/componentContext.js +1 -0
  36. package/dist/server/config.js +1 -12
  37. package/dist/server/constants.js +6 -0
  38. package/dist/server/contract.d.ts +1 -1
  39. package/dist/server/core.js +5 -14
  40. package/dist/server/crypto.js +26 -18
  41. package/dist/server/db.js +6 -1
  42. package/dist/server/device.js +88 -78
  43. package/dist/server/http.d.ts +4 -3
  44. package/dist/server/http.js +74 -86
  45. package/dist/server/index.d.ts +2 -1
  46. package/dist/server/limits.js +22 -15
  47. package/dist/server/mounts.d.ts +103 -103
  48. package/dist/server/mutations/account.js +6 -4
  49. package/dist/server/mutations/invalidate.js +3 -6
  50. package/dist/server/mutations/oauth.js +86 -88
  51. package/dist/server/mutations/refresh.js +45 -87
  52. package/dist/server/mutations/register.js +19 -19
  53. package/dist/server/mutations/retrieve.js +17 -15
  54. package/dist/server/mutations/signature.js +9 -13
  55. package/dist/server/mutations/signin.js +7 -3
  56. package/dist/server/mutations/signout.js +10 -15
  57. package/dist/server/mutations/store.js +22 -12
  58. package/dist/server/mutations/verifier.js +11 -6
  59. package/dist/server/mutations/verify.js +55 -46
  60. package/dist/server/oauth/runtime.js +27 -25
  61. package/dist/server/passkey.js +299 -250
  62. package/dist/server/prefetch.js +283 -281
  63. package/dist/server/refresh.js +7 -60
  64. package/dist/server/runtime.d.ts +82 -206
  65. package/dist/server/runtime.js +63 -56
  66. package/dist/server/services/config.js +5 -3
  67. package/dist/server/services/logger.js +2 -4
  68. package/dist/server/services/providers.js +2 -4
  69. package/dist/server/services/refresh.js +2 -4
  70. package/dist/server/services/resolve.js +15 -14
  71. package/dist/server/services/signin.js +2 -4
  72. package/dist/server/sessions.js +32 -33
  73. package/dist/server/signin.js +177 -142
  74. package/dist/server/sso/domain.d.ts +20 -68
  75. package/dist/server/sso/domain.js +444 -413
  76. package/dist/server/sso/http.js +53 -59
  77. package/dist/server/sso/oidc.js +94 -80
  78. package/dist/server/tokens.js +13 -3
  79. package/dist/server/totp.js +153 -116
  80. package/dist/server/types.d.ts +2 -2
  81. package/dist/server/users.js +18 -23
  82. package/dist/server/utils/cache.js +51 -0
  83. package/dist/server/utils/dispatch.js +36 -0
  84. package/dist/server/utils/retry.js +24 -0
  85. package/dist/server/utils/span.js +32 -0
  86. package/dist/shared/errors.js +9 -3
  87. package/dist/shared/log.js +20 -22
  88. package/package.json +41 -33
@@ -1,24 +1,19 @@
1
1
  import { authDb } from "../db.js";
2
2
  import { AUTH_STORE_REF } from "./store/refs.js";
3
3
  import { deleteSession, getAuthSessionId } from "../sessions.js";
4
- import { Effect, Option, pipe } from "effect";
5
4
 
6
5
  //#region src/server/mutations/signout.ts
7
- function signOutImpl(ctx, config) {
6
+ async function signOutImpl(ctx, config) {
8
7
  const db = authDb(ctx, config);
9
- return Effect.gen(function* () {
10
- const sessionId = yield* Effect.promise(() => getAuthSessionId(ctx));
11
- return yield* pipe(Option.fromNullishOr(sessionId), Option.match({
12
- onNone: () => Effect.succeed(null),
13
- onSome: (sessionId$1) => Effect.flatMap(Effect.promise(() => db.sessions.getById(sessionId$1)), (session) => pipe(Option.fromNullishOr(session), Option.match({
14
- onNone: () => Effect.succeed(null),
15
- onSome: (session$1) => Effect.as(Effect.promise(() => deleteSession(ctx, session$1, config)), {
16
- userId: session$1.userId,
17
- sessionId: session$1._id
18
- })
19
- })))
20
- }));
21
- });
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
+ };
22
17
  }
23
18
  const callSignOut = async (ctx) => {
24
19
  return ctx.runMutation(AUTH_STORE_REF, { args: { type: "signOut" } });
@@ -10,10 +10,9 @@ import { retrieveAccountWithCredentialsArgs, retrieveAccountWithCredentialsImpl
10
10
  import { verifierSignatureArgs, verifierSignatureImpl } from "./signature.js";
11
11
  import { signInArgs, signInImpl } from "./signin.js";
12
12
  import { signOutImpl } from "./signout.js";
13
- import { verifierImpl } from "./verifier.js";
13
+ import { verifierArgs, verifierImpl } from "./verifier.js";
14
14
  import { verifyCodeAndSignInArgs, verifyCodeAndSignInImpl } from "./verify.js";
15
15
  import { v } from "convex/values";
16
- import { Cause, Effect, Exit, Match } from "effect";
17
16
 
18
17
  //#region src/server/mutations/store.ts
19
18
  const storeArgs = v.object({ args: v.union(v.object({
@@ -25,7 +24,10 @@ const storeArgs = v.object({ args: v.union(v.object({
25
24
  }), v.object({
26
25
  type: v.literal("verifyCodeAndSignIn"),
27
26
  ...verifyCodeAndSignInArgs.fields
28
- }), v.object({ type: v.literal("verifier") }), v.object({
27
+ }), v.object({
28
+ type: v.literal("verifier"),
29
+ ...verifierArgs.fields
30
+ }), v.object({
29
31
  type: v.literal("verifierSignature"),
30
32
  ...verifierSignatureArgs.fields
31
33
  }), v.object({
@@ -51,15 +53,23 @@ const storeImpl = async (ctx, fnArgs, services) => {
51
53
  const args = fnArgs.args;
52
54
  const config = services.config;
53
55
  const getProviderOrThrow = services.providerRegistry.getProviderOrThrow;
54
- log(LOG_LEVELS.INFO, `\`auth:store\` type: ${args.type}`);
55
- const program = Match.value(args).pipe(Match.when({ type: "signIn" }, (args$1) => Effect.promise(() => signInImpl(ctx, args$1, config))), Match.when({ type: "signOut" }, () => signOutImpl(ctx, config)), Match.when({ type: "refreshSession" }, (args$1) => services.refresh.refresh(ctx, args$1, getProviderOrThrow)), Match.when({ type: "verifyCodeAndSignIn" }, (args$1) => verifyCodeAndSignInImpl(ctx, args$1, getProviderOrThrow, config)), Match.when({ type: "verifier" }, () => verifierImpl(ctx, config)), Match.when({ type: "verifierSignature" }, (args$1) => verifierSignatureImpl(ctx, args$1, config)), Match.when({ type: "userOAuth" }, (args$1) => userOAuthImpl(ctx, args$1, getProviderOrThrow, config)), Match.when({ type: "createVerificationCode" }, (args$1) => Effect.promise(() => createVerificationCodeImpl(ctx, args$1, getProviderOrThrow, config))), Match.when({ type: "createAccountFromCredentials" }, (args$1) => createAccountFromCredentialsImpl(ctx, args$1, getProviderOrThrow, config)), Match.when({ type: "retrieveAccountWithCredentials" }, (args$1) => retrieveAccountWithCredentialsImpl(ctx, args$1, getProviderOrThrow, config)), Match.when({ type: "modifyAccount" }, (args$1) => modifyAccountImpl(ctx, args$1, getProviderOrThrow, config)), Match.when({ type: "invalidateSessions" }, (args$1) => invalidateSessionsImpl(ctx, args$1, config)), Match.exhaustive);
56
- const exit = await Effect.runPromiseExit(program);
57
- return Exit.match(exit, {
58
- onSuccess: (value) => value,
59
- onFailure: (cause) => {
60
- throw Cause.squash(cause);
61
- }
62
- });
56
+ log(LOG_LEVELS.DEBUG, `\`auth:store\` type: ${args.type}`);
57
+ const handler = {
58
+ signIn: (a) => signInImpl(ctx, a, config),
59
+ signOut: () => signOutImpl(ctx, config),
60
+ refreshSession: (a) => services.refresh.refresh(ctx, a),
61
+ verifyCodeAndSignIn: (a) => verifyCodeAndSignInImpl(ctx, a, getProviderOrThrow, config),
62
+ verifier: (a) => verifierImpl(ctx, a, config),
63
+ verifierSignature: (a) => verifierSignatureImpl(ctx, a, config),
64
+ userOAuth: (a) => userOAuthImpl(ctx, a, getProviderOrThrow, config),
65
+ createVerificationCode: (a) => createVerificationCodeImpl(ctx, a, getProviderOrThrow, config),
66
+ createAccountFromCredentials: (a) => createAccountFromCredentialsImpl(ctx, a, getProviderOrThrow, config),
67
+ retrieveAccountWithCredentials: (a) => retrieveAccountWithCredentialsImpl(ctx, a, getProviderOrThrow, config),
68
+ modifyAccount: (a) => modifyAccountImpl(ctx, a, getProviderOrThrow, config),
69
+ invalidateSessions: (a) => invalidateSessionsImpl(ctx, a, config)
70
+ }[args.type];
71
+ if (!handler) throw new Error(`Unknown store type: "${args.type}"`);
72
+ return await handler(args);
63
73
  };
64
74
 
65
75
  //#endregion
@@ -1,16 +1,21 @@
1
1
  import { authDb } from "../db.js";
2
2
  import { AUTH_STORE_REF } from "./store/refs.js";
3
3
  import { getAuthSessionId } from "../sessions.js";
4
- import { Effect } from "effect";
4
+ import { v } from "convex/values";
5
5
 
6
6
  //#region src/server/mutations/verifier.ts
7
- function verifierImpl(ctx, config) {
8
- return Effect.flatMap(Effect.promise(() => getAuthSessionId(ctx)), (sessionId) => Effect.promise(() => authDb(ctx, config).verifiers.create(sessionId ?? void 0)).pipe(Effect.map((verifierId) => verifierId)));
7
+ const verifierArgs = v.object({ signature: v.optional(v.string()) });
8
+ async function verifierImpl(ctx, args, config) {
9
+ const sessionId = await getAuthSessionId(ctx);
10
+ return await authDb(ctx, config).verifiers.create(sessionId ?? void 0, args.signature);
9
11
  }
10
- const callVerifier = async (ctx) => {
11
- return ctx.runMutation(AUTH_STORE_REF, { args: { type: "verifier" } });
12
+ const callVerifier = async (ctx, signature) => {
13
+ return ctx.runMutation(AUTH_STORE_REF, { args: {
14
+ type: "verifier",
15
+ ...signature === void 0 ? {} : { signature }
16
+ } });
12
17
  };
13
18
 
14
19
  //#endregion
15
- export { callVerifier, verifierImpl };
20
+ export { callVerifier, verifierArgs, verifierImpl };
16
21
  //# sourceMappingURL=verifier.js.map
@@ -1,17 +1,16 @@
1
1
  import { LOG_LEVELS } from "../../shared/log.js";
2
- import { requireEnv } from "../env.js";
3
2
  import { sha256 } from "../random.js";
4
3
  import { authDb } from "../db.js";
4
+ import { requireEnv } from "../env.js";
5
5
  import { log } from "../log.js";
6
6
  import { AUTH_STORE_REF } from "./store/refs.js";
7
- import { createNewAndDeleteExistingSession, getAuthSessionId, maybeGenerateTokensForSession } from "../sessions.js";
7
+ import { getAuthSessionId, issueSession } from "../sessions.js";
8
8
  import { upsertUserAndAccount } from "../users.js";
9
9
  import { payloadRecordValidator } from "../payloads.js";
10
10
  import { isGroupProviderId } from "../sso/shared.js";
11
11
  import { createSyntheticOAuthMaterializedConfig } from "../sso/oidc.js";
12
12
  import { isSignInRateLimited, recordFailedSignIn, resetSignInRateLimit } from "../limits.js";
13
13
  import { v } from "convex/values";
14
- import { Data, Effect } from "effect";
15
14
 
16
15
  //#region src/server/mutations/verify.ts
17
16
  const verifyCodeAndSignInArgs = v.object({
@@ -21,50 +20,46 @@ const verifyCodeAndSignInArgs = v.object({
21
20
  generateTokens: v.boolean(),
22
21
  allowExtraProviders: v.boolean()
23
22
  });
24
- /** A soft verification failure logged and collapsed to null at the boundary. */
25
- var VerifyFailure = class extends Data.TaggedError("VerifyFailure") {};
26
- function verifyCodeAndSignInImpl(ctx, args, getProviderOrThrow, config) {
23
+ async function verifyCodeAndSignInImpl(ctx, args, getProviderOrThrow, config) {
27
24
  const params = args.params;
28
25
  const { generateTokens, provider, allowExtraProviders } = args;
29
26
  const identifier = typeof params.email === "string" ? params.email : typeof params.phone === "string" ? params.phone : void 0;
30
- return Effect.gen(function* () {
31
- yield* Effect.sync(() => {
32
- log(LOG_LEVELS.DEBUG, "verifyCodeAndSignInImpl args:", {
33
- params: {
34
- email: params.email,
35
- phone: params.phone
36
- },
37
- provider: args.provider,
38
- verifier: args.verifier,
39
- generateTokens: args.generateTokens,
40
- allowExtraProviders: args.allowExtraProviders
41
- });
42
- if (generateTokens) {
43
- requireEnv("JWT_PRIVATE_KEY");
44
- requireEnv("JWKS");
45
- requireEnv("CONVEX_SITE_URL");
46
- }
27
+ try {
28
+ log(LOG_LEVELS.DEBUG, "verifyCodeAndSignInImpl args:", {
29
+ params: {
30
+ email: params.email,
31
+ phone: params.phone
32
+ },
33
+ provider: args.provider,
34
+ verifier: args.verifier,
35
+ generateTokens: args.generateTokens,
36
+ allowExtraProviders: args.allowExtraProviders
47
37
  });
38
+ if (generateTokens) {
39
+ requireEnv("JWT_PRIVATE_KEY");
40
+ requireEnv("JWKS");
41
+ requireEnv("CONVEX_SITE_URL");
42
+ }
48
43
  if (identifier !== void 0) {
49
- if (yield* isSignInRateLimited(ctx, identifier, config)) return yield* Effect.fail(new VerifyFailure({ reason: "Too many failed attempts to verify code for this email" }));
44
+ if (await isSignInRateLimited(ctx, identifier, config)) throw new VerifyFailure("Too many failed attempts to verify code for this email");
50
45
  }
51
46
  const db = authDb(ctx, config);
52
47
  const verifier = args.verifier;
53
48
  const codeValue = params.code;
54
- if (typeof codeValue !== "string") return yield* Effect.fail(new VerifyFailure({ reason: "Invalid verification code" }));
55
- const hash = yield* Effect.promise(() => sha256(codeValue));
56
- const code = yield* Effect.promise(() => db.verificationCodes.getByCode(hash));
57
- if (code === null) return yield* Effect.fail(new VerifyFailure({ reason: "Invalid verification code" }));
58
- yield* Effect.promise(() => db.verificationCodes.delete(code._id));
59
- if (code.verifier !== verifier) return yield* Effect.fail(new VerifyFailure({ reason: "Invalid verifier" }));
60
- if (code.expirationTime < Date.now()) return yield* Effect.fail(new VerifyFailure({ reason: "Expired verification code" }));
61
- if (provider !== void 0 && code.provider !== provider) return yield* Effect.fail(new VerifyFailure({ reason: `Invalid provider "${provider}" for given \`code\`` }));
62
- const account = yield* Effect.promise(() => db.accounts.getById(code.accountId));
63
- if (account === null) return yield* Effect.fail(new VerifyFailure({ reason: "Account associated with this email has been deleted" }));
49
+ if (typeof codeValue !== "string") throw new VerifyFailure("Invalid verification code");
50
+ const hash = await sha256(codeValue);
51
+ const code = await db.verificationCodes.getByCode(hash);
52
+ if (code === null) throw new VerifyFailure("Invalid verification code");
53
+ await db.verificationCodes.delete(code._id);
54
+ if (code.verifier !== verifier) throw new VerifyFailure("Invalid verifier");
55
+ if (code.expirationTime < Date.now()) throw new VerifyFailure("Expired verification code");
56
+ if (provider !== void 0 && code.provider !== provider) throw new VerifyFailure(`Invalid provider "${provider}" for given \`code\``);
57
+ const account = await db.accounts.getById(code.accountId);
58
+ if (account === null) throw new VerifyFailure("Account associated with this email has been deleted");
64
59
  const codeProvider = isGroupProviderId(code.provider) ? createSyntheticOAuthMaterializedConfig(code.provider) : getProviderOrThrow(code.provider, allowExtraProviders);
65
- if (codeProvider !== null && (codeProvider.type === "email" || codeProvider.type === "phone") && codeProvider.authorize !== void 0) yield* Effect.promise(() => codeProvider.authorize(params, account));
60
+ if (codeProvider !== null && (codeProvider.type === "email" || codeProvider.type === "phone") && codeProvider.authorize !== void 0) await codeProvider.authorize(params, account);
66
61
  const methodProvider = isGroupProviderId(account.provider) ? createSyntheticOAuthMaterializedConfig(account.provider) : getProviderOrThrow(account.provider);
67
- const userId = methodProvider.type === "oauth" ? account.userId : (yield* Effect.promise(async () => upsertUserAndAccount(ctx, await getAuthSessionId(ctx), { existingAccount: account }, {
62
+ const userId = methodProvider.type === "oauth" ? account.userId : (await upsertUserAndAccount(ctx, await getAuthSessionId(ctx), { existingAccount: account }, {
68
63
  type: "verification",
69
64
  provider: methodProvider,
70
65
  profile: {
@@ -77,18 +72,32 @@ function verifyCodeAndSignInImpl(ctx, args, getProviderOrThrow, config) {
77
72
  phoneVerified: true
78
73
  } : {}
79
74
  }
80
- }, config))).userId;
81
- if (identifier !== void 0) yield* resetSignInRateLimit(ctx, identifier, config);
82
- const sessionId = yield* Effect.promise(() => createNewAndDeleteExistingSession(ctx, config, userId));
83
- return yield* Effect.promise(() => maybeGenerateTokensForSession(ctx, config, userId, sessionId, generateTokens));
84
- }).pipe(Effect.catchTag("VerifyFailure", (error) => Effect.gen(function* () {
85
- yield* Effect.sync(() => {
86
- log(LOG_LEVELS.ERROR, error.reason);
75
+ }, config)).userId;
76
+ if (identifier !== void 0) await resetSignInRateLimit(ctx, identifier, config);
77
+ return await issueSession(ctx, config, {
78
+ userId,
79
+ replaceSessionId: await getAuthSessionId(ctx) ?? void 0,
80
+ generateTokens
87
81
  });
88
- if (identifier !== void 0) yield* recordFailedSignIn(ctx, identifier, config);
89
- return null;
90
- })));
82
+ } catch (error) {
83
+ if (error instanceof VerifyFailure) {
84
+ log(LOG_LEVELS.ERROR, error.reason);
85
+ if (identifier !== void 0) await recordFailedSignIn(ctx, identifier, config);
86
+ return null;
87
+ }
88
+ throw error;
89
+ }
91
90
  }
91
+ /** A soft verification failure -- logged and collapsed to null at the boundary. */
92
+ var VerifyFailure = class extends Error {
93
+ _tag = "VerifyFailure";
94
+ reason;
95
+ constructor(reason) {
96
+ super(reason);
97
+ this.reason = reason;
98
+ this.name = "VerifyFailure";
99
+ }
100
+ };
92
101
  const callVerifyCodeAndSignIn = async (ctx, args) => {
93
102
  return ctx.runMutation(AUTH_STORE_REF, { args: {
94
103
  type: "verifyCodeAndSignIn",
@@ -1,9 +1,9 @@
1
1
  import { envOptionalString, readConfigSync } from "../env.js";
2
+ import { log } from "../log.js";
3
+ import { withSpan } from "../utils/span.js";
2
4
  import { isLocalHost } from "../url.js";
3
5
  import { SHARED_COOKIE_OPTIONS } from "../cookies.js";
4
- import { log } from "../log.js";
5
6
  import { ConvexError } from "convex/values";
6
- import { Effect } from "effect";
7
7
  import * as arctic from "arctic";
8
8
 
9
9
  //#region src/server/oauth/runtime.ts
@@ -16,13 +16,14 @@ import * as arctic from "arctic";
16
16
  * @module
17
17
  */
18
18
  function failConvex(data) {
19
- return Effect.fail(new ConvexError(data));
19
+ throw new ConvexError(data);
20
20
  }
21
- function tryConvex(options) {
22
- return Effect.tryPromise({
23
- try: async () => options.try(),
24
- catch: (error) => new ConvexError(options.catch(error))
25
- });
21
+ async function tryConvex(options) {
22
+ try {
23
+ return await options.try();
24
+ } catch (error) {
25
+ throw new ConvexError(options.catch(error));
26
+ }
26
27
  }
27
28
  const COOKIE_TTL = 900;
28
29
  const oauthCookiePrefix = !isLocalHost(readConfigSync(envOptionalString("CONVEX_SITE_URL")) ?? void 0) ? "__Host-" : "";
@@ -67,7 +68,7 @@ function getAuthorizationSignature({ codeVerifier, state }) {
67
68
  function requiresPKCE(provider) {
68
69
  return provider.pkce === "required" || provider.pkce === "optional";
69
70
  }
70
- function exchangeCode(provider, code, codeVerifier) {
71
+ async function exchangeCode(provider, code, codeVerifier) {
71
72
  return tryConvex({
72
73
  try: () => provider.validateAuthorizationCode({
73
74
  code,
@@ -89,7 +90,7 @@ function exchangeCode(provider, code, codeVerifier) {
89
90
  }
90
91
  });
91
92
  }
92
- function extractProfile(providerId, oauthConfig, tokens) {
93
+ async function extractProfile(providerId, oauthConfig, tokens) {
93
94
  if (oauthConfig.profile) return tryConvex({
94
95
  try: () => oauthConfig.profile(tokens),
95
96
  catch: (error) => ({
@@ -99,12 +100,12 @@ function extractProfile(providerId, oauthConfig, tokens) {
99
100
  });
100
101
  if (typeof tokens.idToken === "string") {
101
102
  const claims = arctic.decodeIdToken(tokens.idToken);
102
- return Effect.succeed({
103
+ return {
103
104
  id: claims.sub ?? crypto.randomUUID(),
104
105
  name: claims.name ?? void 0,
105
106
  email: claims.email ?? void 0,
106
107
  image: claims.picture ?? void 0
107
- });
108
+ };
108
109
  }
109
110
  return failConvex({
110
111
  code: "OAUTH_INVALID_PROFILE",
@@ -112,7 +113,8 @@ function extractProfile(providerId, oauthConfig, tokens) {
112
113
  });
113
114
  }
114
115
  function validateProfileId(providerId, profile) {
115
- return typeof profile.id === "string" && profile.id ? Effect.succeed(profile) : failConvex({
116
+ if (typeof profile.id === "string" && profile.id) return profile;
117
+ return failConvex({
116
118
  code: "OAUTH_INVALID_PROFILE",
117
119
  message: `The profile callback for "${providerId}" must return an object with a string \`id\` field.`
118
120
  });
@@ -163,16 +165,16 @@ async function createOAuthAuthorizationURL(providerId, oauthConfig, options) {
163
165
  * Handle the OAuth callback: validate state, exchange code for tokens,
164
166
  * extract profile.
165
167
  */
166
- function handleOAuthCallback(providerId, oauthConfig, params, cookies) {
167
- return Effect.gen(function* () {
168
- if (oauthConfig.provider === null) return yield* failConvex({
168
+ async function handleOAuthCallback(providerId, oauthConfig, params, cookies) {
169
+ return withSpan("convex-auth.oauth.callback", { providerId }, async () => {
170
+ if (oauthConfig.provider === null) return failConvex({
169
171
  code: "OAUTH_PROVIDER_ERROR",
170
172
  message: `OAuth provider "${providerId}" is missing a runtime client.`
171
173
  });
172
174
  const responseCookies = [];
173
175
  const storedState = cookies[oauthCookieName("state", providerId)];
174
176
  const returnedState = params.state;
175
- if (!storedState || !returnedState || !constantTimeEqual(storedState, returnedState)) return yield* failConvex({
177
+ if (!storedState || !returnedState || !constantTimeEqual(storedState, returnedState)) return failConvex({
176
178
  code: "OAUTH_INVALID_STATE",
177
179
  message: "Invalid OAuth state. Please try signing in again."
178
180
  });
@@ -184,21 +186,21 @@ function handleOAuthCallback(providerId, oauthConfig, params, cookies) {
184
186
  error_description: params.error_description
185
187
  };
186
188
  log("DEBUG", "OAuthCallbackError", cause);
187
- return yield* failConvex({
189
+ return failConvex({
188
190
  code: "OAUTH_PROVIDER_ERROR",
189
191
  message: "OAuth provider returned an error",
190
192
  cause: JSON.stringify(cause)
191
193
  });
192
194
  }
193
195
  const code = params.code;
194
- if (code == null) return yield* failConvex({
196
+ if (code == null) return failConvex({
195
197
  code: "OAUTH_PROVIDER_ERROR",
196
198
  message: "Missing authorization code in callback"
197
199
  });
198
200
  let codeVerifier;
199
201
  if (requiresPKCE(oauthConfig.provider)) {
200
202
  const storedVerifier = cookies[oauthCookieName("pkce", providerId)];
201
- if (storedVerifier == null) return yield* failConvex({
203
+ if (storedVerifier == null) return failConvex({
202
204
  code: "OAUTH_MISSING_VERIFIER",
203
205
  message: "Missing PKCE verifier cookie for OAuth callback"
204
206
  });
@@ -208,22 +210,22 @@ function handleOAuthCallback(providerId, oauthConfig, params, cookies) {
208
210
  let nonce;
209
211
  if (oauthConfig.nonce === true) {
210
212
  const storedNonce = cookies[oauthCookieName("nonce", providerId)];
211
- if (storedNonce == null) return yield* failConvex({
213
+ if (storedNonce == null) return failConvex({
212
214
  code: "OAUTH_PROVIDER_ERROR",
213
215
  message: "Missing nonce cookie for OAuth callback"
214
216
  });
215
217
  nonce = storedNonce;
216
218
  responseCookies.push(clearCookie("nonce", providerId));
217
219
  }
218
- const tokens = yield* exchangeCode(oauthConfig.provider, code, codeVerifier);
219
- if (oauthConfig.validateTokens !== void 0) yield* tryConvex({
220
+ const tokens = await exchangeCode(oauthConfig.provider, code, codeVerifier);
221
+ if (oauthConfig.validateTokens !== void 0) await tryConvex({
220
222
  try: () => oauthConfig.validateTokens(tokens, { nonce }),
221
223
  catch: (error) => ({
222
224
  code: "OAUTH_PROVIDER_ERROR",
223
225
  message: `Token validation failed: ${error instanceof Error ? error.message : String(error)}`
224
226
  })
225
227
  });
226
- const profile = yield* validateProfileId(providerId, yield* extractProfile(providerId, oauthConfig, tokens));
228
+ const profile = validateProfileId(providerId, await extractProfile(providerId, oauthConfig, tokens));
227
229
  log("DEBUG", "OAuth callback profile extracted", {
228
230
  providerId,
229
231
  profileId: profile.id
@@ -238,7 +240,7 @@ function handleOAuthCallback(providerId, oauthConfig, params, cookies) {
238
240
  cookies: responseCookies,
239
241
  signature
240
242
  };
241
- }).pipe(Effect.withSpan("convex-auth.oauth.callback", { attributes: { providerId } }));
243
+ });
242
244
  }
243
245
 
244
246
  //#endregion