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

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 (104) hide show
  1. package/dist/bin.js +125 -36
  2. package/dist/browser/index.d.ts +3 -13
  3. package/dist/browser/index.js +47 -12
  4. package/dist/browser/navigation.js +1 -1
  5. package/dist/browser/passkey.js +7 -7
  6. package/dist/browser/runtime.js +13 -15
  7. package/dist/client/core/types.d.ts +179 -63
  8. package/dist/client/core/types.js +6 -0
  9. package/dist/client/factors/totp.js +1 -1
  10. package/dist/client/index.d.ts +5 -4
  11. package/dist/client/index.js +115 -56
  12. package/dist/client/runtime/mutex.js +3 -2
  13. package/dist/component/_generated/component.d.ts +40 -0
  14. package/dist/component/http.js +9 -0
  15. package/dist/component/index.d.ts +1 -1
  16. package/dist/component/model.d.ts +27 -27
  17. package/dist/component/model.js +2 -1
  18. package/dist/component/modules.js +1 -0
  19. package/dist/component/public/factors/passkeys.js +31 -1
  20. package/dist/component/public/identity/codes.js +1 -1
  21. package/dist/component/public/identity/tokens.js +2 -1
  22. package/dist/component/public/identity/verifiers.js +15 -5
  23. package/dist/component/public.js +2 -2
  24. package/dist/component/schema.d.ts +287 -285
  25. package/dist/component/schema.js +2 -1
  26. package/dist/core/index.d.ts +8 -3
  27. package/dist/core/index.js +7 -2
  28. package/dist/expo/index.d.ts +21 -0
  29. package/dist/expo/index.js +148 -0
  30. package/dist/expo/passkey.js +174 -0
  31. package/dist/providers/apple.d.ts +1 -1
  32. package/dist/providers/apple.js +6 -8
  33. package/dist/providers/custom.d.ts +1 -1
  34. package/dist/providers/custom.js +4 -7
  35. package/dist/providers/github.d.ts +1 -1
  36. package/dist/providers/github.js +5 -8
  37. package/dist/providers/google.d.ts +1 -1
  38. package/dist/providers/google.js +5 -8
  39. package/dist/providers/microsoft.d.ts +1 -1
  40. package/dist/providers/microsoft.js +5 -9
  41. package/dist/providers/password.d.ts +18 -37
  42. package/dist/providers/password.js +170 -115
  43. package/dist/providers/redirect.d.ts +1 -0
  44. package/dist/providers/redirect.js +20 -0
  45. package/dist/server/auth.d.ts +6 -7
  46. package/dist/server/auth.js +3 -2
  47. package/dist/server/{ctxCache.js → cache/context.js} +2 -2
  48. package/dist/server/{componentContext.d.ts → component/context.d.ts} +2 -2
  49. package/dist/server/context.js +3 -10
  50. package/dist/server/contract.d.ts +2 -87
  51. package/dist/server/contract.js +1 -1
  52. package/dist/server/cookies.js +25 -1
  53. package/dist/server/core.js +1 -1
  54. package/dist/server/errors.js +24 -1
  55. package/dist/server/{auth-context.d.ts → facade.d.ts} +3 -45
  56. package/dist/server/{auth-context.js → facade.js} +11 -12
  57. package/dist/server/http.d.ts +7 -7
  58. package/dist/server/http.js +47 -7
  59. package/dist/server/{convexIdentity.d.ts → identity/convex.d.ts} +3 -3
  60. package/dist/server/index.d.ts +5 -3
  61. package/dist/server/index.js +3 -2
  62. package/dist/server/mounts.d.ts +171 -18
  63. package/dist/server/mutations/code.js +7 -1
  64. package/dist/server/mutations/{credentialsSignIn.js → credentials/signin.js} +10 -10
  65. package/dist/server/mutations/index.js +1 -1
  66. package/dist/server/mutations/invalidate.js +11 -1
  67. package/dist/server/mutations/oauth.js +25 -27
  68. package/dist/server/mutations/signin.js +6 -0
  69. package/dist/server/mutations/signout.js +5 -0
  70. package/dist/server/mutations/store.js +1 -1
  71. package/dist/server/oauth/factory.js +11 -3
  72. package/dist/server/passkey.js +126 -110
  73. package/dist/server/prefetch.js +8 -1
  74. package/dist/server/redirects.js +11 -3
  75. package/dist/server/refresh.js +6 -1
  76. package/dist/server/runtime.d.ts +68 -37
  77. package/dist/server/runtime.js +318 -36
  78. package/dist/server/services/group.js +4 -0
  79. package/dist/server/sessions.js +1 -0
  80. package/dist/server/signin.js +8 -6
  81. package/dist/server/sso/domain.d.ts +159 -16
  82. package/dist/server/sso/domain.js +1 -1
  83. package/dist/server/sso/http.js +144 -60
  84. package/dist/server/sso/oidc.js +28 -12
  85. package/dist/server/sso/policy.js +30 -14
  86. package/dist/server/sso/provision.js +1 -1
  87. package/dist/server/sso/saml.js +18 -9
  88. package/dist/server/sso/scim.js +12 -4
  89. package/dist/server/sso/shared.js +5 -5
  90. package/dist/server/telemetry.js +3 -0
  91. package/dist/server/tokens.js +10 -2
  92. package/dist/server/totp.js +127 -100
  93. package/dist/server/types.d.ts +224 -151
  94. package/dist/server/url.js +1 -1
  95. package/dist/server/users.js +93 -53
  96. package/dist/server/wellknown.d.ts +75 -0
  97. package/dist/server/wellknown.js +198 -0
  98. package/dist/shared/errors.js +0 -1
  99. package/package.json +36 -4
  100. package/dist/server/oauth/index.js +0 -12
  101. package/dist/server/utils/dispatch.js +0 -36
  102. package/dist/shared/authResults.d.ts +0 -16
  103. /package/dist/server/{componentContext.js → component/context.js} +0 -0
  104. /package/dist/server/{convexIdentity.js → identity/convex.js} +0 -0
@@ -7,54 +7,39 @@ import { DocumentByName, GenericDataModel, WithoutSystemFields } from "convex/se
7
7
  /** Configuration for the {@link password} provider. */
8
8
  interface PasswordConfig<DataModel extends GenericDataModel> {
9
9
  /**
10
- * Uniquely identifies the provider, allowing to use
11
- * multiple different password providers.
10
+ * Uniquely identifies the provider, allowing multiple password providers.
12
11
  */
13
12
  id?: string;
14
13
  /**
15
- * Perform checks on provided params and customize the user
16
- * information stored after sign up, including email normalization.
14
+ * Perform checks on provided params and customize the user information
15
+ * stored after sign up, including email normalization.
17
16
  *
18
- * Called for every flow ("signUp", "signIn", "reset",
19
- * "reset-verification" and "email-verification").
17
+ * Called for every flow.
20
18
  */
21
- profile?: (
22
- /**
23
- * The values passed to the `signIn` function.
24
- */
25
- params: Record<string, Value | undefined>,
26
- /**
27
- * Convex ActionCtx in case you want to read from or write to
28
- * the database.
29
- */
30
- ctx: GenericActionCtxWithAuthConfig<DataModel>) => WithoutSystemFields<DocumentByName<DataModel, "User">> & {
19
+ profile?: (params: Record<string, Value | undefined>, ctx: GenericActionCtxWithAuthConfig<DataModel>) => WithoutSystemFields<DocumentByName<DataModel, "User">> & {
31
20
  email: string;
32
21
  };
33
22
  /**
34
- * Performs custom validation on password provided during sign up or reset.
35
- *
36
- * Otherwise the default validation is used (password is not empty and
37
- * at least 8 characters in length).
23
+ * Performs custom validation on a password during `signUp`, `verify`
24
+ * (when `newPassword` is set), and `change`.
38
25
  *
39
- * If the provided password is invalid, implementations must throw an Error.
26
+ * Default: non-empty, length >= 8.
40
27
  *
41
- * @param password the password supplied during "signUp" or
42
- * "reset-verification" flows.
28
+ * Throw an `Error` to reject the password.
43
29
  */
44
30
  validatePasswordRequirements?: (password: string) => void;
45
31
  /**
46
- * Provide hashing and verification functions if you want to control
47
- * how passwords are hashed.
32
+ * Hashing and verification functions. Defaults to scrypt.
48
33
  */
49
34
  crypto?: CredentialsConfig["crypto"];
50
35
  /**
51
- * An email provider used to require verification
52
- * before password reset.
36
+ * Email provider for the `reset` flow. Issues OTPs that the `verify` flow
37
+ * accepts when `newPassword` is included.
53
38
  */
54
39
  reset?: EmailConfig | PasswordEmailProviderFactory;
55
40
  /**
56
- * An email provider used to require verification
57
- * before sign up / sign in.
41
+ * Email provider for post-signup email confirmation. Issues OTPs that the
42
+ * `verify` flow accepts when `newPassword` is omitted.
58
43
  */
59
44
  verify?: EmailConfig | PasswordEmailProviderFactory;
60
45
  }
@@ -62,24 +47,20 @@ type PasswordEmailProviderFactory = () => EmailConfig;
62
47
  /**
63
48
  * Email and password authentication provider.
64
49
  *
65
- * Passwords are by default hashed using scrypt.
66
- * You can customize the hashing via the `crypto` option.
50
+ * Passwords are hashed with scrypt by default. Customize via `crypto`.
67
51
  *
68
- * Email verification is not required unless you pass
69
- * an email provider to the `verify` option.
52
+ * Email verification is opt-in via the `verify` option. Password reset is
53
+ * opt-in via the `reset` option (typically the same email provider).
70
54
  *
71
55
  * @example
72
56
  * ```ts
73
- * import { password } from "@robelest/convex-auth/providers";
74
- *
75
57
  * password()
76
- * password({ verify: myEmailProvider })
58
+ * password({ verify: myEmailProvider, reset: myEmailProvider })
77
59
  * ```
78
60
  *
79
61
  * @typeParam DataModel - The Convex data model used by the auth context.
80
62
  * @param config - Password flow hooks and optional verification providers.
81
63
  * @returns A configured password provider for `createAuth`.
82
- * @throws {Error} During sign-in flows when required password params are missing or reset is not enabled.
83
64
  */
84
65
  declare function password<DataModel extends GenericDataModel = GenericDataModel>(config?: PasswordConfig<DataModel>): ConvexCredentialsConfig;
85
66
  //#endregion
@@ -1,4 +1,5 @@
1
- import { callCredentialsSignIn } from "../server/mutations/credentialsSignIn.js";
1
+ import { getAuthenticatedUserIdOrNull } from "../server/identity.js";
2
+ import { callCredentialsSignIn } from "../server/mutations/credentials/signin.js";
2
3
  import { credentials } from "./credentials.js";
3
4
  import { ConvexError } from "convex/values";
4
5
  import { scryptAsync } from "@noble/hashes/scrypt.js";
@@ -8,63 +9,57 @@ import { bytesToHex } from "@noble/hashes/utils.js";
8
9
  /**
9
10
  * Configure the password provider for email/password authentication.
10
11
  *
11
- * The password provider supports the following flows, determined
12
- * by the `flow` parameter:
12
+ * Five flows, all single-word camelCase:
13
13
  *
14
- * - `"signUp"`: Create a new account with a password.
15
- * - `"signIn"`: Sign in with an existing account and password.
16
- * - `"reset"`: Request a password reset.
17
- * - `"reset-verification"`: Verify a password reset code and change password.
18
- * - `"email-verification"`: If email verification is enabled and `code` is
19
- * included in params, verify an OTP.
14
+ * - `signUp` Create a new account.
15
+ * - `signIn` Sign in with email + password.
16
+ * - `reset` Kick off a forgot-password flow (issues an OTP via email).
17
+ * - `verify` Verify any pending email OTP. With `newPassword`, completes a
18
+ * `reset` flow and updates the password. Without `newPassword`, completes
19
+ * the post-signup email confirmation. The OTP scope is enforced server-side
20
+ * by the issuing email provider.
21
+ * - `change` — Authenticated password change (requires `currentPassword`).
20
22
  *
21
23
  * ```ts
22
24
  * import { password } from "@robelest/convex-auth/providers";
23
25
  *
24
26
  * password()
27
+ * password({ verify: myEmailProvider, reset: myEmailProvider })
25
28
  * ```
26
29
  *
27
30
  * @module
28
31
  */
29
- const PASSWORD_FLOW_TAG = {
30
- signUp: "signUp",
31
- signIn: "signIn",
32
- reset: "reset",
33
- "reset-verification": "resetVerification",
34
- "email-verification": "emailVerification"
35
- };
32
+ const PASSWORD_FLOWS = [
33
+ "signUp",
34
+ "signIn",
35
+ "reset",
36
+ "verify",
37
+ "change"
38
+ ];
36
39
  function decodePasswordFlow(flow) {
37
- if (typeof flow !== "string") return {
40
+ if (typeof flow === "string" && PASSWORD_FLOWS.includes(flow)) return { tag: flow };
41
+ return {
38
42
  tag: "invalid",
39
43
  flow
40
44
  };
41
- const tag = PASSWORD_FLOW_TAG[flow];
42
- return tag === void 0 ? {
43
- tag: "invalid",
44
- flow
45
- } : { tag };
46
45
  }
47
46
  /**
48
47
  * Email and password authentication provider.
49
48
  *
50
- * Passwords are by default hashed using scrypt.
51
- * You can customize the hashing via the `crypto` option.
49
+ * Passwords are hashed with scrypt by default. Customize via `crypto`.
52
50
  *
53
- * Email verification is not required unless you pass
54
- * an email provider to the `verify` option.
51
+ * Email verification is opt-in via the `verify` option. Password reset is
52
+ * opt-in via the `reset` option (typically the same email provider).
55
53
  *
56
54
  * @example
57
55
  * ```ts
58
- * import { password } from "@robelest/convex-auth/providers";
59
- *
60
56
  * password()
61
- * password({ verify: myEmailProvider })
57
+ * password({ verify: myEmailProvider, reset: myEmailProvider })
62
58
  * ```
63
59
  *
64
60
  * @typeParam DataModel - The Convex data model used by the auth context.
65
61
  * @param config - Password flow hooks and optional verification providers.
66
62
  * @returns A configured password provider for `createAuth`.
67
- * @throws {Error} During sign-in flows when required password params are missing or reset is not enabled.
68
63
  */
69
64
  function password(config = {}) {
70
65
  const provider = config.id ?? "password";
@@ -81,12 +76,10 @@ function password(config = {}) {
81
76
  }
82
77
  validateDefaultPasswordRequirements(password);
83
78
  };
84
- if (flowDispatch.tag === "signUp") validatePasswordRequirements(params.password);
85
- else if (flowDispatch.tag === "resetVerification") validatePasswordRequirements(params.newPassword);
86
79
  const profile = config.profile?.(params, ctx) ?? defaultProfile(params);
87
80
  const { email } = profile;
88
- const requirePasswordParam = (value, flow) => {
89
- if (typeof value !== "string" || value.length === 0) throw new Error(`Missing \`password\` param for \`${flow}\` flow`);
81
+ const requireStringParam = (value, name, flow) => {
82
+ if (typeof value !== "string" || value.length === 0) throw new Error(`Missing \`${name}\` param for \`${flow}\` flow`);
90
83
  return value;
91
84
  };
92
85
  const finalizeCredentialsResult = async (account, user) => {
@@ -100,88 +93,150 @@ function password(config = {}) {
100
93
  hasTotp
101
94
  };
102
95
  };
103
- if (flowDispatch.tag === "signUp") {
104
- const secret = requirePasswordParam(params.password, "signUp");
105
- const created = await ctx.auth.account.create(ctx, {
106
- provider,
107
- account: {
108
- id: email,
109
- secret
110
- },
111
- profile,
112
- shouldLinkViaEmail: config.verify !== void 0,
113
- shouldLinkViaPhone: false
114
- });
115
- return await finalizeCredentialsResult(created.account, created.user);
116
- } else if (flowDispatch.tag === "signIn") {
117
- const result = await callCredentialsSignIn(ctx, {
118
- provider,
119
- account: {
120
- id: email,
121
- secret: requirePasswordParam(params.password, "signIn")
122
- },
123
- generateTokens: true,
124
- requireVerifiedEmail: verifyProvider !== void 0,
125
- enforceTotp: true
126
- });
127
- if (result.kind === "invalidAccount" || result.kind === "invalidSecret") throw new Error("Invalid credentials");
128
- if (result.kind === "tooManyAttempts") throw new ConvexError({
129
- code: "RATE_LIMITED",
130
- message: "Too many failed sign-in attempts. Please try again later."
131
- });
132
- if (result.kind === "emailVerificationRequired") return await ctx.auth.provider.signIn(ctx, verifyProvider, {
133
- accountId: result.account._id,
134
- params
135
- });
136
- const hasTotp = result.kind === "signedIn" ? result.user.hasTotp : true;
137
- return {
138
- userId: result.user._id,
139
- hasTotp,
140
- issuance: result.issuance
141
- };
142
- } else if (flowDispatch.tag === "reset") {
143
- if (!resetProvider) throw new Error(`Password reset is not enabled for ${provider}`);
144
- const { account } = await ctx.auth.account.get(ctx, {
145
- provider,
146
- account: { id: email }
147
- });
148
- return await ctx.auth.provider.signIn(ctx, resetProvider, {
149
- accountId: account._id,
150
- params
151
- });
152
- } else if (flowDispatch.tag === "resetVerification") {
153
- if (!resetProvider) throw new Error(`Password reset is not enabled for ${provider}`);
154
- if (params.newPassword === void 0) throw new Error("Missing `newPassword` param for `reset-verification` flow");
155
- const result = await ctx.auth.provider.signIn(ctx, resetProvider, { params });
156
- if (result === null) throw new Error("Invalid code");
157
- const { userId, sessionId } = result;
158
- const secret = params.newPassword;
159
- await ctx.auth.account.update(ctx, {
160
- provider,
161
- account: {
162
- id: email,
163
- secret
96
+ switch (flowDispatch.tag) {
97
+ case "signUp": {
98
+ const secret = requireStringParam(params.password, "password", "signUp");
99
+ validatePasswordRequirements(secret);
100
+ const created = await ctx.auth.account.create(ctx, {
101
+ provider,
102
+ account: {
103
+ id: email,
104
+ secret
105
+ },
106
+ profile,
107
+ shouldLinkViaEmail: config.verify !== void 0,
108
+ shouldLinkViaPhone: false
109
+ });
110
+ return await finalizeCredentialsResult(created.account, created.user);
111
+ }
112
+ case "signIn": {
113
+ const result = await callCredentialsSignIn(ctx, {
114
+ provider,
115
+ account: {
116
+ id: email,
117
+ secret: requireStringParam(params.password, "password", "signIn")
118
+ },
119
+ generateTokens: true,
120
+ requireVerifiedEmail: verifyProvider !== void 0,
121
+ enforceTotp: true
122
+ });
123
+ if (result.kind === "invalidAccount" || result.kind === "invalidSecret") throw new Error("Invalid credentials");
124
+ if (result.kind === "tooManyAttempts") throw new ConvexError({
125
+ code: "RATE_LIMITED",
126
+ message: "Too many failed sign-in attempts. Please try again later."
127
+ });
128
+ if (result.kind === "emailVerificationRequired") return await ctx.auth.provider.signIn(ctx, verifyProvider, {
129
+ accountId: result.account._id,
130
+ params
131
+ });
132
+ const hasTotp = result.kind === "signedIn" ? result.user.hasTotp : true;
133
+ return {
134
+ userId: result.user._id,
135
+ hasTotp,
136
+ issuance: result.issuance
137
+ };
138
+ }
139
+ case "reset": {
140
+ if (!resetProvider) throw new Error(`Password reset is not enabled for ${provider}`);
141
+ const { account } = await ctx.auth.account.get(ctx, {
142
+ provider,
143
+ account: { id: email }
144
+ });
145
+ return await ctx.auth.provider.signIn(ctx, resetProvider, {
146
+ accountId: account._id,
147
+ params
148
+ });
149
+ }
150
+ case "verify": {
151
+ const newPassword = params.newPassword;
152
+ if (typeof newPassword === "string" && newPassword.length > 0) {
153
+ if (!resetProvider) throw new Error(`Password reset is not enabled for ${provider}`);
154
+ validatePasswordRequirements(newPassword);
155
+ const result = await ctx.auth.provider.signIn(ctx, resetProvider, { params });
156
+ if (result === null) throw new Error("Invalid code");
157
+ const { userId, sessionId } = result;
158
+ await ctx.auth.account.update(ctx, {
159
+ provider,
160
+ account: {
161
+ id: email,
162
+ secret: newPassword
163
+ }
164
+ });
165
+ await ctx.auth.session.invalidate(ctx, {
166
+ userId,
167
+ except: [sessionId]
168
+ });
169
+ await ctx.auth.config.callbacks?.after?.(ctx, {
170
+ kind: "passwordChanged",
171
+ userId,
172
+ flow: "reset"
173
+ });
174
+ return {
175
+ userId,
176
+ sessionId
177
+ };
164
178
  }
165
- });
166
- await ctx.auth.session.invalidate(ctx, {
167
- userId,
168
- except: [sessionId]
169
- });
170
- return {
171
- userId,
172
- sessionId
173
- };
174
- } else if (flowDispatch.tag === "emailVerification") {
175
- if (!verifyProvider) throw new Error(`Email verification is not enabled for ${provider}`);
176
- const { account } = await ctx.auth.account.get(ctx, {
177
- provider,
178
- account: { id: email }
179
- });
180
- return await ctx.auth.provider.signIn(ctx, verifyProvider, {
181
- accountId: account._id,
182
- params
183
- });
184
- } else throw new Error("Missing `flow` param, it must be one of \"signUp\", \"signIn\", \"reset\", \"reset-verification\" or \"email-verification\"!");
179
+ if (!verifyProvider) throw new Error(`Email verification is not enabled for ${provider}`);
180
+ const { account } = await ctx.auth.account.get(ctx, {
181
+ provider,
182
+ account: { id: email }
183
+ });
184
+ return await ctx.auth.provider.signIn(ctx, verifyProvider, {
185
+ accountId: account._id,
186
+ params
187
+ });
188
+ }
189
+ case "change": {
190
+ const authedUserId = await getAuthenticatedUserIdOrNull(ctx);
191
+ if (authedUserId === null) throw new ConvexError({
192
+ code: "NOT_SIGNED_IN",
193
+ message: "Sign in first to change your password."
194
+ });
195
+ const currentPassword = requireStringParam(params.currentPassword, "currentPassword", "change");
196
+ const newPassword = requireStringParam(params.newPassword, "newPassword", "change");
197
+ validatePasswordRequirements(newPassword);
198
+ const result = await callCredentialsSignIn(ctx, {
199
+ provider,
200
+ account: {
201
+ id: email,
202
+ secret: currentPassword
203
+ },
204
+ generateTokens: true,
205
+ requireVerifiedEmail: false,
206
+ enforceTotp: false
207
+ });
208
+ if (result.kind === "invalidAccount" || result.kind === "invalidSecret") throw new Error("Invalid current password");
209
+ if (result.kind === "tooManyAttempts") throw new ConvexError({
210
+ code: "RATE_LIMITED",
211
+ message: "Too many failed attempts. Please try again later."
212
+ });
213
+ if (result.kind !== "signedIn") throw new Error(`Unexpected sign-in result: ${result.kind}`);
214
+ const verifiedUserId = result.user._id;
215
+ if (verifiedUserId !== authedUserId) throw new Error("Email does not match authenticated user");
216
+ await ctx.auth.account.update(ctx, {
217
+ provider,
218
+ account: {
219
+ id: email,
220
+ secret: newPassword
221
+ }
222
+ });
223
+ await ctx.auth.session.invalidate(ctx, {
224
+ userId: verifiedUserId,
225
+ except: [result.issuance.sessionId]
226
+ });
227
+ await ctx.auth.config.callbacks?.after?.(ctx, {
228
+ kind: "passwordChanged",
229
+ userId: verifiedUserId,
230
+ flow: "change"
231
+ });
232
+ return {
233
+ userId: verifiedUserId,
234
+ hasTotp: false,
235
+ issuance: result.issuance
236
+ };
237
+ }
238
+ default: throw new Error("Missing or invalid `flow` param. Expected one of: " + PASSWORD_FLOWS.join(", ") + ".");
239
+ }
185
240
  },
186
241
  crypto: config.crypto ?? {
187
242
  async hashSecret(password) {
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,20 @@
1
+ import { envOptionalString, readConfigSync } from "../server/env.js";
2
+
3
+ //#region src/providers/redirect.ts
4
+ function normalizeRoutePrefix(prefix) {
5
+ if (prefix === void 0 || prefix === "" || prefix === "/") return "";
6
+ return (prefix.startsWith("/") ? prefix : `/${prefix}`).replace(/\/$/, "");
7
+ }
8
+ /** @internal */
9
+ function defaultOAuthRedirectUri(providerId) {
10
+ const customAuthSiteUrl = readConfigSync(envOptionalString("CUSTOM_AUTH_SITE_URL"));
11
+ if (customAuthSiteUrl) return `${customAuthSiteUrl.replace(/\/$/, "")}/callback/${providerId}`;
12
+ const convexSiteUrl = readConfigSync(envOptionalString("CONVEX_SITE_URL"));
13
+ if (!convexSiteUrl) throw new Error(`Missing CONVEX_SITE_URL while configuring ${providerId} OAuth provider. Set CONVEX_SITE_URL or pass redirectUri explicitly.`);
14
+ const prefix = normalizeRoutePrefix(readConfigSync(envOptionalString("CONVEX_AUTH_HTTP_PREFIX")) ?? "/auth");
15
+ return `${convexSiteUrl.replace(/\/$/, "")}${prefix}/callback/${providerId}`;
16
+ }
17
+
18
+ //#endregion
19
+ export { defaultOAuthRedirectUri };
20
+ //# sourceMappingURL=redirect.js.map
@@ -1,12 +1,10 @@
1
1
  import { AuthApiRefs } from "../client/core/types.js";
2
2
  import "../client/index.js";
3
3
  import { AuthAuthorizationConfig, AuthGrant, AuthProviderConfig, AuthRoleId, ConvexAuthConfig, HasDeviceProvider, HasPasskeyProvider, HasSSO, HasTotpProvider } from "./types.js";
4
- import { AuthConfig, AuthContext, AuthContextConfig, AuthContextFactory as AuthContextFactory$1, AuthContextResolver as AuthContextResolver$1, InferAuth, OptionalAuthContext, UserDoc } from "./auth-context.js";
4
+ import { AuthConfig, AuthContext, AuthContextConfig, AuthContextFactory, AuthContextResolver, InferAuth, OptionalAuthContext, UserDoc } from "./facade.js";
5
5
  import { Auth } from "./runtime.js";
6
6
 
7
7
  //#region src/server/auth.d.ts
8
- type AuthContextResolver = AuthContextResolver$1;
9
- type AuthContextFactory = AuthContextFactory$1;
10
8
  type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = Omit<ReturnType<typeof Auth>["auth"]["member"], "create" | "list" | "update" | "inspect" | "require"> & {
11
9
  create: (ctx: Parameters<ReturnType<typeof Auth>["auth"]["member"]["create"]>[0], data: {
12
10
  groupId: string;
@@ -48,7 +46,7 @@ type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig |
48
46
  * The base auth API surface returned by {@link createAuth}.
49
47
  *
50
48
  * Provides core namespaces — `signIn`, `signOut`, `user`, `session`,
51
- * `member`, `invite`, `group`, `key`, and `http` — that are
49
+ * `member`, `invite`, `group`, `key`, and `request` — that are
52
50
  * always available regardless of which providers are configured.
53
51
  * Group SSO helpers under `group.sso` are added conditionally by
54
52
  * {@link AuthApi} when an SSO provider is present.
@@ -63,6 +61,7 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
63
61
  signIn: ReturnType<typeof Auth>["signIn"];
64
62
  signOut: ReturnType<typeof Auth>["signOut"];
65
63
  store: ReturnType<typeof Auth>["store"];
64
+ http: ReturnType<typeof Auth>["http"];
66
65
  user: ReturnType<typeof Auth>["auth"]["user"];
67
66
  session: ReturnType<typeof Auth>["auth"]["session"];
68
67
  provider: ReturnType<typeof Auth>["auth"]["provider"];
@@ -71,7 +70,7 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
71
70
  member: MemberApiWithAuthorization<TAuthorization>;
72
71
  invite: ReturnType<typeof Auth>["auth"]["invite"];
73
72
  key: ReturnType<typeof Auth>["auth"]["key"];
74
- http: ReturnType<typeof Auth>["auth"]["http"];
73
+ request: ReturnType<typeof Auth>["auth"]["request"];
75
74
  /**
76
75
  * Resolve the current request's auth context. Framework-agnostic — use
77
76
  * this in fluent-convex middleware, custom wrappers, or anywhere you
@@ -84,8 +83,8 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
84
83
  * Pass `{ optional: true }` to get a null-shaped auth object instead.
85
84
  *
86
85
  * @param ctx - Convex query, mutation, or action context.
87
- * @param config - Optional auth resolution config. Supports `optional`,
88
- * `resolve`, and `authResolve`.
86
+ * @param config - Optional auth resolution config. Supports `optional` and
87
+ * `resolve`.
89
88
  * @returns The current auth context.
90
89
  *
91
90
  * @example fluent-convex middleware
@@ -1,4 +1,4 @@
1
- import { createAuthContextCustomization, createAuthContextFacade, createPublicAuthContext } from "./auth-context.js";
1
+ import { createAuthContextFacade } from "./facade.js";
2
2
  import { Auth } from "./runtime.js";
3
3
  import { ConvexError } from "convex/values";
4
4
 
@@ -133,6 +133,7 @@ function createAuth(component, config) {
133
133
  signIn: authResult.signIn,
134
134
  signOut: authResult.signOut,
135
135
  store: authResult.store,
136
+ http: authResult.http,
136
137
  user: authResult.auth.user,
137
138
  session: authResult.auth.session,
138
139
  provider: authResult.auth.provider,
@@ -144,7 +145,7 @@ function createAuth(component, config) {
144
145
  member: authResult.auth.member,
145
146
  invite: authResult.auth.invite,
146
147
  key: authResult.auth.key,
147
- http: authResult.auth.http,
148
+ request: authResult.auth.request,
148
149
  ...createAuthContextFacade(authResult.auth)
149
150
  };
150
151
  }
@@ -1,4 +1,4 @@
1
- //#region src/server/ctxCache.ts
1
+ //#region src/server/cache/context.ts
2
2
  /**
3
3
  * Per-execution cache for Convex auth component reads.
4
4
  *
@@ -91,4 +91,4 @@ function invalidateCtxCache(ctx, keyPrefix) {
91
91
 
92
92
  //#endregion
93
93
  export { cached, ctxCacheHas, invalidateCtxCache };
94
- //# sourceMappingURL=ctxCache.js.map
94
+ //# sourceMappingURL=context.js.map
@@ -1,6 +1,6 @@
1
1
  import { GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
2
2
 
3
- //#region src/server/componentContext.d.ts
3
+ //#region src/server/component/context.d.ts
4
4
  type ComponentReadCtx = {
5
5
  runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
6
6
  };
@@ -9,4 +9,4 @@ type ComponentCtx = ComponentReadCtx & {
9
9
  };
10
10
  //#endregion
11
11
  export { ComponentCtx, ComponentReadCtx };
12
- //# sourceMappingURL=componentContext.d.ts.map
12
+ //# sourceMappingURL=context.d.ts.map
@@ -6,14 +6,7 @@ async function getSessionUserId(ctx) {
6
6
  return await getAuthenticatedUserIdOrNull(ctx);
7
7
  }
8
8
  /** @internal */
9
- async function getAuthContextForUser(auth, ctx, userId, opts) {
10
- if (opts?.group === false) return {
11
- userId,
12
- user: await auth.user.get(ctx, userId),
13
- groupId: null,
14
- role: null,
15
- grants: []
16
- };
9
+ async function getAuthContextForUser(auth, ctx, userId) {
17
10
  const [user, groupId] = await Promise.all([auth.user.get(ctx, userId), auth.user.getActiveGroup(ctx, { userId })]);
18
11
  let role = null;
19
12
  let grants = [];
@@ -36,10 +29,10 @@ async function getAuthContextForUser(auth, ctx, userId, opts) {
36
29
  };
37
30
  }
38
31
  /** @internal */
39
- async function getAuthContext(auth, ctx, opts) {
32
+ async function getAuthContext(auth, ctx) {
40
33
  const userId = await getSessionUserId(ctx);
41
34
  if (userId === null) return null;
42
- return await getAuthContextForUser(auth, ctx, userId, opts);
35
+ return await getAuthContextForUser(auth, ctx, userId);
43
36
  }
44
37
  /** @internal */
45
38
  function createUnauthenticatedAuthContext() {