@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.
- package/dist/component/_generated/component.d.ts +1611 -2039
- package/dist/component/account.js +3 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/factor/device.js +3 -0
- package/dist/component/factor/passkey.js +3 -0
- package/dist/component/factor/totp.js +3 -0
- package/dist/component/group/invite.js +3 -0
- package/dist/component/group/member.js +3 -0
- package/dist/component/group.js +3 -0
- package/dist/component/model.d.ts +342 -30
- package/dist/component/model.js +22 -4
- package/dist/component/modules.js +24 -21
- package/dist/component/public/factors/devices.js +37 -106
- package/dist/component/public/factors/passkeys.js +29 -149
- package/dist/component/public/factors/totp.js +32 -159
- package/dist/component/public/groups/core.js +19 -82
- package/dist/component/public/groups/invites.js +15 -104
- package/dist/component/public/groups/members.js +26 -149
- package/dist/component/public/identity/accounts.js +12 -94
- package/dist/component/public/identity/codes.js +13 -73
- package/dist/component/public/identity/sessions.js +5 -107
- package/dist/component/public/identity/tokens.js +13 -103
- package/dist/component/public/identity/users.js +188 -185
- package/dist/component/public/identity/verifiers.js +17 -80
- package/dist/component/public/security/keys.js +13 -120
- package/dist/component/public/security/limits.js +0 -43
- package/dist/component/public/sso/audit.js +0 -28
- package/dist/component/public/sso/core.js +31 -104
- package/dist/component/public/sso/domains.js +0 -71
- package/dist/component/public/sso/scim.js +63 -239
- package/dist/component/public/sso/secrets.js +0 -30
- package/dist/component/public/sso/webhooks.js +23 -128
- package/dist/component/rateLimit.js +3 -0
- package/dist/component/schema.d.ts +378 -342
- package/dist/component/schema.js +11 -1
- package/dist/component/session.js +3 -0
- package/dist/component/sso/audit.js +3 -0
- package/dist/component/sso/connection/domain/verification.js +3 -0
- package/dist/component/sso/connection/domain.js +3 -0
- package/dist/component/sso/connection/scim/config.js +3 -0
- package/dist/component/sso/connection/scim/identity.js +3 -0
- package/dist/component/sso/connection/secret.js +3 -0
- package/dist/component/sso/connection.js +3 -0
- package/dist/component/sso/webhook/delivery.js +3 -0
- package/dist/component/sso/webhook/endpoint.js +3 -0
- package/dist/component/token/pkce.js +3 -0
- package/dist/component/token/refresh.js +3 -0
- package/dist/component/token/verification.js +3 -0
- package/dist/component/user/email.js +3 -0
- package/dist/component/user/key.js +3 -0
- package/dist/component/user.js +62 -0
- package/dist/core/index.d.ts +131 -28
- package/dist/core/index.js +2 -0
- package/dist/model.js +391 -0
- package/dist/providers/credentials.d.ts +1 -1
- package/dist/providers/github.js +6 -0
- package/dist/providers/password.js +1 -2
- package/dist/server/auth.d.ts +73 -7
- package/dist/server/auth.js +4 -1
- package/dist/server/context.js +30 -3
- package/dist/server/contract.js +42 -42
- package/dist/server/core.js +224 -86
- package/dist/server/db.js +45 -37
- package/dist/server/facade.d.ts +39 -0
- package/dist/server/facade.js +16 -0
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +3 -1
- package/dist/server/mounts.d.ts +101 -101
- package/dist/server/mutations/credentials/signin.js +3 -7
- package/dist/server/mutations/oauth.js +9 -6
- package/dist/server/runtime.d.ts +147 -46
- package/dist/server/runtime.js +10 -8
- package/dist/server/services/group.js +9 -9
- package/dist/server/sso/domain.d.ts +1 -1
- package/dist/server/sso/domain.js +40 -40
- package/dist/server/sso/http.js +18 -18
- package/dist/server/sso/oidc.js +1 -1
- package/dist/server/sso/policies.js +3 -3
- package/dist/server/sso/policy.js +12 -4
- package/dist/server/sso/provision.js +9 -9
- package/dist/server/sso/validators.js +2 -2
- package/dist/server/sso/webhook.js +8 -8
- package/dist/server/types.d.ts +185 -124
- package/dist/server/types.js +29 -24
- package/dist/server/users.js +49 -2
- package/dist/server/validators.d.ts +745 -0
- package/dist/server/validators.js +60 -0
- package/package.json +1 -1
- 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
|
|
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;
|
package/dist/providers/github.js
CHANGED
|
@@ -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
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/server/auth.js
CHANGED
|
@@ -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.
|
|
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
|
};
|
package/dist/server/context.js
CHANGED
|
@@ -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,
|
|
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
|
|