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

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 (89) hide show
  1. package/dist/component/_generated/component.d.ts +1611 -2039
  2. package/dist/component/account.js +3 -0
  3. package/dist/component/convex.config.d.ts +2 -2
  4. package/dist/component/factor/device.js +3 -0
  5. package/dist/component/factor/passkey.js +3 -0
  6. package/dist/component/factor/totp.js +3 -0
  7. package/dist/component/group/invite.js +3 -0
  8. package/dist/component/group/member.js +3 -0
  9. package/dist/component/group.js +3 -0
  10. package/dist/component/model.d.ts +342 -30
  11. package/dist/component/model.js +22 -4
  12. package/dist/component/modules.js +24 -21
  13. package/dist/component/public/factors/devices.js +37 -106
  14. package/dist/component/public/factors/passkeys.js +29 -149
  15. package/dist/component/public/factors/totp.js +32 -159
  16. package/dist/component/public/groups/core.js +19 -82
  17. package/dist/component/public/groups/invites.js +15 -104
  18. package/dist/component/public/groups/members.js +26 -149
  19. package/dist/component/public/identity/accounts.js +12 -94
  20. package/dist/component/public/identity/codes.js +13 -73
  21. package/dist/component/public/identity/sessions.js +5 -107
  22. package/dist/component/public/identity/tokens.js +13 -103
  23. package/dist/component/public/identity/users.js +188 -185
  24. package/dist/component/public/identity/verifiers.js +17 -80
  25. package/dist/component/public/security/keys.js +13 -120
  26. package/dist/component/public/security/limits.js +0 -43
  27. package/dist/component/public/sso/audit.js +0 -28
  28. package/dist/component/public/sso/core.js +31 -104
  29. package/dist/component/public/sso/domains.js +0 -71
  30. package/dist/component/public/sso/scim.js +63 -239
  31. package/dist/component/public/sso/secrets.js +0 -30
  32. package/dist/component/public/sso/webhooks.js +23 -128
  33. package/dist/component/rateLimit.js +3 -0
  34. package/dist/component/schema.d.ts +378 -342
  35. package/dist/component/schema.js +11 -1
  36. package/dist/component/session.js +3 -0
  37. package/dist/component/sso/audit.js +3 -0
  38. package/dist/component/sso/connection/domain/verification.js +3 -0
  39. package/dist/component/sso/connection/domain.js +3 -0
  40. package/dist/component/sso/connection/scim/config.js +3 -0
  41. package/dist/component/sso/connection/scim/identity.js +3 -0
  42. package/dist/component/sso/connection/secret.js +3 -0
  43. package/dist/component/sso/connection.js +3 -0
  44. package/dist/component/sso/webhook/delivery.js +3 -0
  45. package/dist/component/sso/webhook/endpoint.js +3 -0
  46. package/dist/component/token/pkce.js +3 -0
  47. package/dist/component/token/refresh.js +3 -0
  48. package/dist/component/token/verification.js +3 -0
  49. package/dist/component/user/email.js +3 -0
  50. package/dist/component/user/key.js +3 -0
  51. package/dist/component/user.js +62 -0
  52. package/dist/core/index.d.ts +131 -28
  53. package/dist/core/index.js +2 -0
  54. package/dist/model.js +391 -0
  55. package/dist/providers/credentials.d.ts +1 -1
  56. package/dist/providers/github.js +6 -0
  57. package/dist/providers/password.js +1 -2
  58. package/dist/server/auth.d.ts +73 -7
  59. package/dist/server/auth.js +4 -1
  60. package/dist/server/context.js +30 -3
  61. package/dist/server/contract.js +42 -42
  62. package/dist/server/core.js +224 -86
  63. package/dist/server/db.js +45 -37
  64. package/dist/server/facade.d.ts +39 -0
  65. package/dist/server/facade.js +16 -0
  66. package/dist/server/index.d.ts +5 -3
  67. package/dist/server/index.js +3 -1
  68. package/dist/server/mounts.d.ts +101 -101
  69. package/dist/server/mutations/credentials/signin.js +3 -7
  70. package/dist/server/mutations/oauth.js +9 -6
  71. package/dist/server/runtime.d.ts +147 -46
  72. package/dist/server/runtime.js +10 -8
  73. package/dist/server/services/group.js +9 -9
  74. package/dist/server/sso/domain.d.ts +1 -1
  75. package/dist/server/sso/domain.js +40 -40
  76. package/dist/server/sso/http.js +18 -18
  77. package/dist/server/sso/oidc.js +1 -1
  78. package/dist/server/sso/policies.js +3 -3
  79. package/dist/server/sso/policy.js +12 -4
  80. package/dist/server/sso/provision.js +9 -9
  81. package/dist/server/sso/validators.js +2 -2
  82. package/dist/server/sso/webhook.js +8 -8
  83. package/dist/server/types.d.ts +185 -124
  84. package/dist/server/types.js +29 -24
  85. package/dist/server/users.js +49 -2
  86. package/dist/server/validators.d.ts +745 -0
  87. package/dist/server/validators.js +60 -0
  88. package/package.json +1 -1
  89. package/dist/component/public.js +0 -22
package/dist/model.js ADDED
@@ -0,0 +1,391 @@
1
+ import { v } from "convex/values";
2
+
3
+ //#region src/component/model.ts
4
+ const TABLES = {
5
+ User: "User",
6
+ UserEmail: "UserEmail",
7
+ Session: "Session",
8
+ Account: "Account",
9
+ AuthVerifier: "AuthVerifier",
10
+ VerificationCode: "VerificationCode",
11
+ RefreshToken: "RefreshToken",
12
+ Passkey: "Passkey",
13
+ TotpFactor: "TotpFactor",
14
+ RateLimit: "RateLimit",
15
+ Group: "Group",
16
+ GroupTag: "GroupTag",
17
+ GroupMember: "GroupMember",
18
+ GroupInvite: "GroupInvite",
19
+ GroupConnection: "GroupConnection",
20
+ GroupConnectionDomain: "GroupConnectionDomain",
21
+ GroupConnectionDomainVerification: "GroupConnectionDomainVerification",
22
+ GroupConnectionSecret: "GroupConnectionSecret",
23
+ GroupConnectionScimConfig: "GroupConnectionScimConfig",
24
+ GroupConnectionScimIdentity: "GroupConnectionScimIdentity",
25
+ GroupAuditEvent: "GroupAuditEvent",
26
+ GroupWebhookEndpoint: "GroupWebhookEndpoint",
27
+ GroupWebhookDelivery: "GroupWebhookDelivery",
28
+ ApiKey: "ApiKey",
29
+ DeviceCode: "DeviceCode"
30
+ };
31
+ const vTag = v.object({
32
+ key: v.string(),
33
+ value: v.string()
34
+ });
35
+ const vPaginated = (item) => v.object({
36
+ items: v.array(item),
37
+ nextCursor: v.union(v.string(), v.null())
38
+ });
39
+ const vInviteStatus = v.union(v.literal("pending"), v.literal("accepted"), v.literal("revoked"), v.literal("expired"));
40
+ const vDeviceStatus = v.union(v.literal("pending"), v.literal("authorized"), v.literal("denied"));
41
+ const vGroupConnectionAccountLinkingPolicy = v.union(v.literal("verifiedEmail"), v.literal("none"), v.literal("sameConnection"));
42
+ const vGroupConnectionScimReuseUserPolicy = v.union(v.literal("externalId"), v.literal("none"));
43
+ const vGroupConnectionJitProvisioningMode = v.union(v.literal("off"), v.literal("createUser"), v.literal("createUserAndMembership"));
44
+ const vGroupConnectionDeprovisionMode = v.union(v.literal("soft"), v.literal("hard"));
45
+ const vGroupConnectionProfileUpdateMode = v.union(v.literal("never"), v.literal("missing"), v.literal("always"));
46
+ const vGroupConnectionProvisioningAuthority = v.union(v.literal("app"), v.literal("sso"), v.literal("scim"));
47
+ const vGroupConnectionGroupSyncMode = v.union(v.literal("ignore"), v.literal("sync"));
48
+ const vGroupConnectionRoleSyncMode = v.union(v.literal("ignore"), v.literal("map"));
49
+ const vGroupConnectionStatus = v.union(v.literal("draft"), v.literal("active"), v.literal("disabled"));
50
+ const vGroupConnectionProtocol = v.union(v.literal("oidc"), v.literal("saml"));
51
+ const vGroupConnectionPolicy = v.object({
52
+ version: v.literal(1),
53
+ identity: v.object({ accountLinking: v.object({
54
+ oidc: vGroupConnectionAccountLinkingPolicy,
55
+ saml: vGroupConnectionAccountLinkingPolicy
56
+ }) }),
57
+ provisioning: v.object({
58
+ user: v.object({
59
+ createOnSignIn: v.boolean(),
60
+ updateProfileOnLogin: vGroupConnectionProfileUpdateMode,
61
+ updateProfileFromScim: vGroupConnectionProfileUpdateMode,
62
+ authority: vGroupConnectionProvisioningAuthority
63
+ }),
64
+ scimReuse: v.object({ user: vGroupConnectionScimReuseUserPolicy }),
65
+ jit: v.object({
66
+ mode: vGroupConnectionJitProvisioningMode,
67
+ defaultRole: v.optional(v.string()),
68
+ defaultRoleIds: v.optional(v.array(v.string()))
69
+ }),
70
+ deprovision: v.object({ mode: vGroupConnectionDeprovisionMode }),
71
+ groups: v.object({
72
+ mode: vGroupConnectionGroupSyncMode,
73
+ source: v.literal("protocol"),
74
+ mapping: v.optional(v.record(v.string(), v.array(v.string())))
75
+ }),
76
+ roles: v.object({
77
+ mode: vGroupConnectionRoleSyncMode,
78
+ source: v.literal("protocol"),
79
+ mapping: v.optional(v.record(v.string(), v.array(v.string())))
80
+ })
81
+ }),
82
+ extend: v.optional(v.any())
83
+ });
84
+ const vScimStatus = v.union(v.literal("draft"), v.literal("active"), v.literal("disabled"));
85
+ const vScimResourceType = v.union(v.literal("user"), v.literal("group"));
86
+ const vAuditActorType = v.union(v.literal("user"), v.literal("system"), v.literal("scim"), v.literal("api_key"), v.literal("webhook"));
87
+ const vAuditStatus = v.union(v.literal("success"), v.literal("failure"));
88
+ const vWebhookEndpointStatus = v.union(v.literal("active"), v.literal("disabled"));
89
+ const vWebhookDeliveryStatus = v.union(v.literal("pending"), v.literal("processing"), v.literal("delivered"), v.literal("failed"));
90
+ const vInviteTokenAcceptStatus = v.union(v.literal("accepted"), v.literal("already_accepted"));
91
+ const vMembershipStatus = v.union(v.literal("joined"), v.literal("already_joined"), v.literal("not_applicable"));
92
+ const vApiKeyScope = v.object({
93
+ resource: v.string(),
94
+ actions: v.array(v.string())
95
+ });
96
+ const vApiKeyRateLimit = v.object({
97
+ maxRequests: v.number(),
98
+ windowMs: v.number()
99
+ });
100
+ const vApiKeyRateLimitState = v.object({
101
+ attemptsLeft: v.number(),
102
+ lastAttemptTime: v.number()
103
+ });
104
+ const vGroupConnectionSecretKind = v.union(v.literal("oidc_client_secret"));
105
+ function vDocMeta(tableName) {
106
+ return {
107
+ _id: v.id(tableName),
108
+ _creationTime: v.number()
109
+ };
110
+ }
111
+ const vUserDoc = v.object({
112
+ ...vDocMeta(TABLES.User),
113
+ name: v.optional(v.string()),
114
+ image: v.optional(v.string()),
115
+ email: v.optional(v.string()),
116
+ emailVerificationTime: v.optional(v.number()),
117
+ phone: v.optional(v.string()),
118
+ phoneVerificationTime: v.optional(v.number()),
119
+ isAnonymous: v.optional(v.boolean()),
120
+ lastActiveGroup: v.optional(v.id(TABLES.Group)),
121
+ extend: v.optional(v.any())
122
+ });
123
+ const vUserEmailSource = v.union(v.literal("password"), v.literal("oauth"), v.literal("oidc"), v.literal("saml"), v.literal("scim"));
124
+ const vProfileEmail = v.object({
125
+ email: v.string(),
126
+ primary: v.optional(v.boolean()),
127
+ verified: v.optional(v.boolean())
128
+ });
129
+ const vUserEmailDoc = v.object({
130
+ ...vDocMeta(TABLES.UserEmail),
131
+ userId: v.id(TABLES.User),
132
+ email: v.string(),
133
+ verificationTime: v.optional(v.number()),
134
+ isPrimary: v.boolean(),
135
+ source: vUserEmailSource,
136
+ accountId: v.optional(v.id(TABLES.Account)),
137
+ provider: v.optional(v.string()),
138
+ connectionId: v.optional(v.id(TABLES.GroupConnection))
139
+ });
140
+ const vSessionDoc = v.object({
141
+ ...vDocMeta(TABLES.Session),
142
+ userId: v.id(TABLES.User),
143
+ expirationTime: v.number()
144
+ });
145
+ const vAccountDoc = v.object({
146
+ ...vDocMeta(TABLES.Account),
147
+ userId: v.id(TABLES.User),
148
+ provider: v.string(),
149
+ providerAccountId: v.string(),
150
+ secret: v.optional(v.string()),
151
+ emailVerified: v.optional(v.string()),
152
+ phoneVerified: v.optional(v.string()),
153
+ extend: v.optional(v.any())
154
+ });
155
+ const vAuthVerifierDoc = v.object({
156
+ ...vDocMeta(TABLES.AuthVerifier),
157
+ sessionId: v.optional(v.id(TABLES.Session)),
158
+ signature: v.optional(v.string()),
159
+ expirationTime: v.optional(v.number())
160
+ });
161
+ const vVerificationCodeDoc = v.object({
162
+ ...vDocMeta(TABLES.VerificationCode),
163
+ accountId: v.id(TABLES.Account),
164
+ provider: v.string(),
165
+ code: v.string(),
166
+ expirationTime: v.number(),
167
+ verifier: v.optional(v.string()),
168
+ emailVerified: v.optional(v.string()),
169
+ phoneVerified: v.optional(v.string())
170
+ });
171
+ const vRefreshTokenDoc = v.object({
172
+ ...vDocMeta(TABLES.RefreshToken),
173
+ sessionId: v.id(TABLES.Session),
174
+ expirationTime: v.number(),
175
+ firstUsedTime: v.optional(v.number()),
176
+ parentRefreshTokenId: v.optional(v.id(TABLES.RefreshToken))
177
+ });
178
+ const vPasskeyDoc = v.object({
179
+ ...vDocMeta(TABLES.Passkey),
180
+ userId: v.id(TABLES.User),
181
+ credentialId: v.string(),
182
+ publicKey: v.bytes(),
183
+ algorithm: v.number(),
184
+ counter: v.number(),
185
+ transports: v.optional(v.array(v.string())),
186
+ deviceType: v.string(),
187
+ backedUp: v.boolean(),
188
+ name: v.optional(v.string()),
189
+ createdAt: v.number(),
190
+ lastUsedAt: v.optional(v.number())
191
+ });
192
+ const vTotpFactorDoc = v.object({
193
+ ...vDocMeta(TABLES.TotpFactor),
194
+ userId: v.id(TABLES.User),
195
+ secret: v.bytes(),
196
+ digits: v.number(),
197
+ period: v.number(),
198
+ verified: v.boolean(),
199
+ name: v.optional(v.string()),
200
+ createdAt: v.number(),
201
+ lastUsedAt: v.optional(v.number())
202
+ });
203
+ const vRateLimitDoc = v.object({
204
+ ...vDocMeta(TABLES.RateLimit),
205
+ identifier: v.string(),
206
+ last_attempt_time: v.number(),
207
+ attempts_left: v.number()
208
+ });
209
+ const vGroupDoc = v.object({
210
+ ...vDocMeta(TABLES.Group),
211
+ name: v.string(),
212
+ slug: v.optional(v.string()),
213
+ type: v.optional(v.string()),
214
+ parentGroupId: v.optional(v.id(TABLES.Group)),
215
+ rootGroupId: v.optional(v.id(TABLES.Group)),
216
+ isRoot: v.optional(v.boolean()),
217
+ tags: v.optional(v.array(vTag)),
218
+ policy: v.optional(vGroupConnectionPolicy),
219
+ extend: v.optional(v.any())
220
+ });
221
+ const vGroupMemberDoc = v.object({
222
+ ...vDocMeta(TABLES.GroupMember),
223
+ groupId: v.id(TABLES.Group),
224
+ userId: v.id(TABLES.User),
225
+ role: v.optional(v.string()),
226
+ roleIds: v.optional(v.array(v.string())),
227
+ status: v.optional(v.string()),
228
+ extend: v.optional(v.any())
229
+ });
230
+ const vGroupInviteDoc = v.object({
231
+ ...vDocMeta(TABLES.GroupInvite),
232
+ groupId: v.optional(v.id(TABLES.Group)),
233
+ invitedByUserId: v.optional(v.id(TABLES.User)),
234
+ email: v.optional(v.string()),
235
+ tokenHash: v.string(),
236
+ role: v.optional(v.string()),
237
+ roleIds: v.optional(v.array(v.string())),
238
+ status: vInviteStatus,
239
+ expiresTime: v.optional(v.number()),
240
+ acceptedByUserId: v.optional(v.id(TABLES.User)),
241
+ acceptedTime: v.optional(v.number()),
242
+ extend: v.optional(v.any())
243
+ });
244
+ const vApiKeyDoc = v.object({
245
+ ...vDocMeta(TABLES.ApiKey),
246
+ userId: v.id(TABLES.User),
247
+ prefix: v.string(),
248
+ hashedKey: v.string(),
249
+ name: v.string(),
250
+ scopes: v.array(vApiKeyScope),
251
+ rateLimit: v.optional(vApiKeyRateLimit),
252
+ rateLimitState: v.optional(vApiKeyRateLimitState),
253
+ expiresAt: v.optional(v.number()),
254
+ lastUsedAt: v.optional(v.number()),
255
+ createdAt: v.number(),
256
+ revoked: v.boolean(),
257
+ metadata: v.optional(v.any())
258
+ });
259
+ const vDeviceCodeDoc = v.object({
260
+ ...vDocMeta(TABLES.DeviceCode),
261
+ deviceCodeHash: v.string(),
262
+ userCode: v.string(),
263
+ expiresAt: v.number(),
264
+ interval: v.number(),
265
+ status: vDeviceStatus,
266
+ userId: v.optional(v.id(TABLES.User)),
267
+ sessionId: v.optional(v.id(TABLES.Session)),
268
+ lastPolledAt: v.optional(v.number())
269
+ });
270
+ const vGroupConnectionDoc = v.object({
271
+ ...vDocMeta(TABLES.GroupConnection),
272
+ groupId: v.id(TABLES.Group),
273
+ slug: v.optional(v.string()),
274
+ name: v.optional(v.string()),
275
+ protocol: vGroupConnectionProtocol,
276
+ status: vGroupConnectionStatus,
277
+ config: v.optional(v.any()),
278
+ extend: v.optional(v.any())
279
+ });
280
+ const vGroupConnectionDomainDoc = v.object({
281
+ ...vDocMeta(TABLES.GroupConnectionDomain),
282
+ connectionId: v.id(TABLES.GroupConnection),
283
+ groupId: v.id(TABLES.Group),
284
+ domain: v.string(),
285
+ isPrimary: v.boolean(),
286
+ verifiedAt: v.optional(v.number())
287
+ });
288
+ const vGroupConnectionDomainVerificationDoc = v.object({
289
+ ...vDocMeta(TABLES.GroupConnectionDomainVerification),
290
+ connectionId: v.id(TABLES.GroupConnection),
291
+ groupId: v.id(TABLES.Group),
292
+ domainId: v.id(TABLES.GroupConnectionDomain),
293
+ domain: v.string(),
294
+ recordName: v.string(),
295
+ token: v.string(),
296
+ tokenHash: v.string(),
297
+ requestedAt: v.number(),
298
+ expiresAt: v.number()
299
+ });
300
+ const vGroupConnectionSecretDoc = v.object({
301
+ ...vDocMeta(TABLES.GroupConnectionSecret),
302
+ connectionId: v.id(TABLES.GroupConnection),
303
+ groupId: v.id(TABLES.Group),
304
+ kind: vGroupConnectionSecretKind,
305
+ ciphertext: v.string(),
306
+ updatedAt: v.number()
307
+ });
308
+ const vGroupConnectionScimConfigDoc = v.object({
309
+ ...vDocMeta(TABLES.GroupConnectionScimConfig),
310
+ connectionId: v.id(TABLES.GroupConnection),
311
+ groupId: v.id(TABLES.Group),
312
+ status: vScimStatus,
313
+ basePath: v.string(),
314
+ tokenHash: v.string(),
315
+ lastRotatedAt: v.optional(v.number()),
316
+ extend: v.optional(v.any())
317
+ });
318
+ const vGroupConnectionScimIdentityDoc = v.object({
319
+ ...vDocMeta(TABLES.GroupConnectionScimIdentity),
320
+ connectionId: v.id(TABLES.GroupConnection),
321
+ groupId: v.id(TABLES.Group),
322
+ resourceType: vScimResourceType,
323
+ externalId: v.string(),
324
+ userId: v.optional(v.id(TABLES.User)),
325
+ mappedGroupId: v.optional(v.id(TABLES.Group)),
326
+ lastProvisionedAt: v.optional(v.number()),
327
+ active: v.optional(v.boolean()),
328
+ raw: v.optional(v.any())
329
+ });
330
+ const vGroupAuditEventDoc = v.object({
331
+ ...vDocMeta(TABLES.GroupAuditEvent),
332
+ connectionId: v.optional(v.id(TABLES.GroupConnection)),
333
+ groupId: v.id(TABLES.Group),
334
+ eventType: v.string(),
335
+ actorType: vAuditActorType,
336
+ actorId: v.optional(v.string()),
337
+ subjectType: v.string(),
338
+ subjectId: v.optional(v.string()),
339
+ status: vAuditStatus,
340
+ occurredAt: v.number(),
341
+ requestId: v.optional(v.string()),
342
+ ip: v.optional(v.string()),
343
+ metadata: v.optional(v.any())
344
+ });
345
+ const vGroupWebhookEndpointDoc = v.object({
346
+ ...vDocMeta(TABLES.GroupWebhookEndpoint),
347
+ connectionId: v.id(TABLES.GroupConnection),
348
+ groupId: v.id(TABLES.Group),
349
+ url: v.string(),
350
+ status: vWebhookEndpointStatus,
351
+ secretHash: v.string(),
352
+ subscriptions: v.array(v.string()),
353
+ createdByUserId: v.optional(v.id(TABLES.User)),
354
+ lastSuccessAt: v.optional(v.number()),
355
+ lastFailureAt: v.optional(v.number()),
356
+ failureCount: v.number(),
357
+ extend: v.optional(v.any())
358
+ });
359
+ const vGroupWebhookDeliveryDoc = v.object({
360
+ ...vDocMeta(TABLES.GroupWebhookDelivery),
361
+ connectionId: v.id(TABLES.GroupConnection),
362
+ endpointId: v.id(TABLES.GroupWebhookEndpoint),
363
+ auditEventId: v.optional(v.id(TABLES.GroupAuditEvent)),
364
+ eventType: v.string(),
365
+ status: vWebhookDeliveryStatus,
366
+ attemptCount: v.number(),
367
+ nextAttemptAt: v.number(),
368
+ lastAttemptAt: v.optional(v.number()),
369
+ lastResponseStatus: v.optional(v.number()),
370
+ lastError: v.optional(v.string()),
371
+ payload: v.any()
372
+ });
373
+ const vRateLimitResult = v.object({
374
+ ...vDocMeta(TABLES.RateLimit),
375
+ identifier: v.string(),
376
+ last_attempt_time: v.number(),
377
+ attempts_left: v.number(),
378
+ attemptsLeft: v.number(),
379
+ lastAttemptTime: v.number()
380
+ });
381
+ const vInviteRedeemResult = v.object({
382
+ inviteId: v.id(TABLES.GroupInvite),
383
+ groupId: v.union(v.id(TABLES.Group), v.null()),
384
+ memberId: v.optional(v.id(TABLES.GroupMember)),
385
+ inviteStatus: vInviteTokenAcceptStatus,
386
+ membershipStatus: vMembershipStatus
387
+ });
388
+
389
+ //#endregion
390
+ export { vGroupDoc, vGroupInviteDoc, vGroupMemberDoc, vPaginated, vProfileEmail, vUserDoc, vUserEmailDoc };
391
+ //# sourceMappingURL=model.js.map
@@ -16,7 +16,7 @@ interface CredentialsConfig<DataModel extends GenericDataModel = GenericDataMode
16
16
  userId: GenericId<"User">;
17
17
  sessionId?: GenericId<"Session">;
18
18
  /**
19
- * TOTP step-up hint. `false` skips the `totpGetVerifiedByUserId` query;
19
+ * TOTP step-up hint. `false` skips the verified-TOTP lookup;
20
20
  * `true`/`undefined` falls back to it.
21
21
  */
22
22
  hasTotp?: boolean;
@@ -56,9 +56,15 @@ function github(config) {
56
56
  const emails = await emailResponse.json();
57
57
  const primaryEmail = emails.find((email) => email.primary)?.email ?? emails.find((email) => email.verified)?.email ?? user.email ?? void 0;
58
58
  const verifiedEmail = emails.find((email) => email.primary)?.verified ?? emails.find((email) => email.verified)?.verified ?? false;
59
+ const allEmails = emails.filter((e) => typeof e.email === "string").map((e) => ({
60
+ email: e.email,
61
+ primary: e.primary === true,
62
+ verified: e.verified === true
63
+ }));
59
64
  return {
60
65
  id: String(user.id),
61
66
  email: typeof primaryEmail === "string" ? primaryEmail : void 0,
67
+ emails: allEmails.length > 0 ? allEmails : void 0,
62
68
  emailVerified: verifiedEmail,
63
69
  name: typeof user.name === "string" ? user.name : void 0,
64
70
  image: typeof user.avatar_url === "string" ? user.avatar_url : void 0
@@ -87,10 +87,9 @@ function password(config = {}) {
87
87
  accountId: account._id,
88
88
  params
89
89
  });
90
- const hasTotp = user.hasTotp;
91
90
  return {
92
91
  userId: user._id,
93
- hasTotp
92
+ hasTotp: false
94
93
  };
95
94
  };
96
95
  switch (flowDispatch.tag) {
@@ -3,6 +3,7 @@ import "../client/index.js";
3
3
  import { AuthAuthorizationConfig, AuthGrant, AuthProviderConfig, AuthRoleId, ConvexAuthConfig, HasDeviceProvider, HasPasskeyProvider, HasSSO, HasTotpProvider } from "./types.js";
4
4
  import { AuthConfig, AuthContext, AuthContextConfig, AuthContextFactory, AuthContextResolver, InferAuth, OptionalAuthContext, UserDoc } from "./facade.js";
5
5
  import { Auth } from "./runtime.js";
6
+ import { AuthExtendValidators, AuthValidators } from "./validators.js";
6
7
 
7
8
  //#region src/server/auth.d.ts
8
9
  type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = Omit<ReturnType<typeof Auth>["auth"]["member"], "create" | "list" | "update" | "inspect" | "require"> & {
@@ -57,7 +58,57 @@ type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig |
57
58
  * @typeParam TAuthorization - The authorization config, used to narrow
58
59
  * role IDs and grant strings on the `member` API.
59
60
  */
60
- type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = {
61
+ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = undefined, TExtend extends AuthExtendValidators = {}> = {
62
+ /**
63
+ * Convex `returns:` validators for the auth read surface.
64
+ *
65
+ * Set these as a function's `returns:` so client-side `useQuery`
66
+ * inference flows end-to-end without hand-rolled validators or DTO
67
+ * mappers. The `extend` field of each document carries the shape
68
+ * supplied via `createAuth({ extend: { ... } })`.
69
+ *
70
+ * Available validators:
71
+ * - `v.user` / `v.group` / `v.member` — single documents (extend-aware).
72
+ * - `v.invite` — a single group invite document.
73
+ * - `v.viewer` — `User | null`, for a current-user query.
74
+ * - `v.list(item)` — wraps an item validator in `{ items, nextCursor }`.
75
+ *
76
+ * Compose these for richer reads — e.g. a current user plus their
77
+ * memberships and groups — using the existing `auth.user.viewer`,
78
+ * `auth.member.list`, and `auth.group.get` facade methods.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * export const viewer = authQuery({
83
+ * returns: auth.v.viewer,
84
+ * handler: (ctx) => ctx.auth.user.viewer(ctx),
85
+ * });
86
+ *
87
+ * export const groups = authQuery({
88
+ * returns: v.union(
89
+ * v.object({
90
+ * ...auth.v.user.fields,
91
+ * memberships: v.array(auth.v.member),
92
+ * groups: v.array(auth.v.group),
93
+ * }),
94
+ * v.null(),
95
+ * ),
96
+ * handler: async (ctx) => {
97
+ * const me = await ctx.auth.user.viewer(ctx);
98
+ * if (me === null) return null;
99
+ * const { items: memberships } = await ctx.auth.member.list(ctx, {
100
+ * where: { userId: me._id },
101
+ * });
102
+ * const groups = await ctx.auth.group.get(
103
+ * ctx,
104
+ * memberships.map((m) => m.groupId),
105
+ * );
106
+ * return { ...me, memberships, groups };
107
+ * },
108
+ * });
109
+ * ```
110
+ */
111
+ v: AuthValidators<TExtend>;
61
112
  signIn: ReturnType<typeof Auth>["signIn"];
62
113
  signOut: ReturnType<typeof Auth>["signOut"];
63
114
  store: ReturnType<typeof Auth>["store"];
@@ -69,7 +120,8 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
69
120
  group: ReturnType<typeof Auth>["auth"]["group"];
70
121
  member: MemberApiWithAuthorization<TAuthorization>;
71
122
  invite: ReturnType<typeof Auth>["auth"]["invite"];
72
- key: ReturnType<typeof Auth>["auth"]["key"];
123
+ key: ReturnType<typeof Auth>["auth"]["key"]; /** Current user's active-group selection (`get` / `set` / `clear`). */
124
+ active: ReturnType<typeof Auth>["auth"]["active"];
73
125
  request: ReturnType<typeof Auth>["auth"]["request"];
74
126
  /**
75
127
  * Resolve the current request's auth context. Framework-agnostic — use
@@ -244,8 +296,8 @@ type PublicGroupSsoApi = {
244
296
  * @typeParam TAuthorization - The authorization config, forwarded to
245
297
  * {@link AuthApiBase} for typed role IDs and grant strings.
246
298
  */
247
- type AuthApi<TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = AuthApiBase<TAuthorization> & {
248
- group: AuthApiBase<TAuthorization>["group"] & {
299
+ type AuthApi<TAuthorization extends AuthAuthorizationConfig | undefined = undefined, TExtend extends AuthExtendValidators = {}> = AuthApiBase<TAuthorization, TExtend> & {
300
+ group: AuthApiBase<TAuthorization, TExtend>["group"] & {
249
301
  sso: PublicGroupSsoApi;
250
302
  };
251
303
  };
@@ -264,7 +316,7 @@ type AuthApi<TAuthorization extends AuthAuthorizationConfig | undefined = undefi
264
316
  * @typeParam P - The tuple of provider configs passed to `createAuth`.
265
317
  * @typeParam TAuthorization - Optional authorization config for typed roles/grants.
266
318
  */
267
- type ConvexAuthResult<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = HasSSO<P> extends true ? AuthApi<TAuthorization> : AuthApiBase<TAuthorization>;
319
+ type ConvexAuthResult<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined, TExtend extends AuthExtendValidators = {}> = HasSSO<P> extends true ? AuthApi<TAuthorization, TExtend> : AuthApiBase<TAuthorization, TExtend>;
268
320
  /**
269
321
  * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.
270
322
  *
@@ -309,10 +361,24 @@ type InferClientApi<T> = T extends ConvexAuthResult<infer P> ? AuthApiRefs<HasPa
309
361
  *
310
362
  * @see {@link AuthContextConfig}
311
363
  */
312
- declare function createAuth<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined>(component: ConvexAuthConfig["component"], config: Omit<AuthConfig, "providers" | "authorization"> & {
364
+ declare function createAuth<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined, TExtend extends AuthExtendValidators = {}>(component: ConvexAuthConfig["component"], config: Omit<AuthConfig, "providers" | "authorization"> & {
313
365
  providers: P;
314
366
  authorization?: TAuthorization;
315
- }): ConvexAuthResult<P, TAuthorization>;
367
+ /**
368
+ * Validators for the `extend` field of each table. Drives both the
369
+ * inferred type of `auth.v.*` (so `viewer.extend.<field>` is typed)
370
+ * and runtime validation of consumer return shapes.
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * createAuth(components.auth, {
375
+ * providers: [password()],
376
+ * extend: { User: v.object({ stripeCustomerId: v.optional(v.string()) }) },
377
+ * });
378
+ * ```
379
+ */
380
+ extend?: TExtend;
381
+ }): ConvexAuthResult<P, TAuthorization, TExtend>;
316
382
  //#endregion
317
383
  export { AuthApi, AuthApiBase, ConvexAuthResult, InferClientApi, createAuth };
318
384
  //# sourceMappingURL=auth.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { createAuthContextFacade } from "./facade.js";
2
2
  import { Auth } from "./runtime.js";
3
+ import { buildAuthValidators } from "./validators.js";
3
4
  import { ConvexError } from "convex/values";
4
5
 
5
6
  //#region src/server/auth.ts
@@ -81,7 +82,7 @@ function createAuth(component, config) {
81
82
  domain: nextDomain.domain,
82
83
  isPrimary: Boolean(nextDomain.isPrimary)
83
84
  });
84
- if (current?.verifiedAt !== void 0) await ctx.runMutation(component.public.groupConnectionDomainVerify, {
85
+ if (current?.verifiedAt !== void 0) await ctx.runMutation(component.sso.connection.domain.verify, {
85
86
  domainId,
86
87
  verifiedAt: current.verifiedAt
87
88
  });
@@ -130,6 +131,7 @@ function createAuth(component, config) {
130
131
  }
131
132
  };
132
133
  return {
134
+ v: buildAuthValidators(config.extend ?? {}),
133
135
  signIn: authResult.signIn,
134
136
  signOut: authResult.signOut,
135
137
  store: authResult.store,
@@ -145,6 +147,7 @@ function createAuth(component, config) {
145
147
  member: authResult.auth.member,
146
148
  invite: authResult.auth.invite,
147
149
  key: authResult.auth.key,
150
+ active: authResult.auth.active,
148
151
  request: authResult.auth.request,
149
152
  ...createAuthContextFacade(authResult.auth)
150
153
  };
@@ -1,13 +1,38 @@
1
1
  import { getAuthenticatedUserIdOrNull } from "./identity.js";
2
+ import { ConvexError } from "convex/values";
2
3
 
3
4
  //#region src/server/context.ts
4
5
  /** @internal */
5
6
  async function getSessionUserId(ctx) {
6
7
  return await getAuthenticatedUserIdOrNull(ctx);
7
8
  }
9
+ /**
10
+ * Build the `ctx.auth.require` grant guard from the resolved grants and
11
+ * active group. `require(grant)` throws when a grant is missing;
12
+ * `require(grant, doc)` additionally asserts the group-owned `doc` belongs
13
+ * to the active group. Reuses `member.require`'s `MISSING_GRANTS` code.
14
+ *
15
+ * @internal
16
+ */
17
+ function makeRequire(groupId, grants) {
18
+ return (grant, doc) => {
19
+ if ((Array.isArray(grant) ? grant : [grant]).filter((g) => !grants.includes(g)).length > 0) throw new ConvexError({
20
+ code: "MISSING_GRANTS",
21
+ message: "User is missing required grants."
22
+ });
23
+ if (doc !== void 0) {
24
+ const docGroupId = doc.groupId;
25
+ if (groupId === null || String(docGroupId) !== groupId) throw new ConvexError({
26
+ code: "FORBIDDEN",
27
+ message: "Record is not in the active group."
28
+ });
29
+ }
30
+ };
31
+ }
8
32
  /** @internal */
9
33
  async function getAuthContextForUser(auth, ctx, userId) {
10
- const [user, groupId] = await Promise.all([auth.user.get(ctx, userId), auth.user.getActiveGroup(ctx, { userId })]);
34
+ const [user, activeGroup] = await Promise.all([auth.user.get(ctx, userId), auth.active.get(ctx, { userId })]);
35
+ const groupId = activeGroup?.groupId ?? null;
11
36
  let role = null;
12
37
  let grants = [];
13
38
  if (groupId) {
@@ -25,7 +50,8 @@ async function getAuthContextForUser(auth, ctx, userId) {
25
50
  user,
26
51
  groupId,
27
52
  role,
28
- grants
53
+ grants,
54
+ require: makeRequire(groupId, grants)
29
55
  };
30
56
  }
31
57
  /** @internal */
@@ -41,7 +67,8 @@ function createUnauthenticatedAuthContext() {
41
67
  user: null,
42
68
  groupId: null,
43
69
  role: null,
44
- grants: []
70
+ grants: [],
71
+ require: makeRequire(null, [])
45
72
  };
46
73
  }
47
74