@robelest/convex-auth 0.0.4-preview.30 → 0.0.4-preview.31
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/bin.js +125 -36
- package/dist/browser/index.d.ts +3 -13
- package/dist/browser/index.js +47 -12
- package/dist/browser/navigation.js +1 -1
- package/dist/browser/passkey.js +7 -7
- package/dist/browser/runtime.js +13 -15
- package/dist/client/core/types.d.ts +179 -63
- package/dist/client/core/types.js +6 -0
- package/dist/client/factors/totp.js +1 -1
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +115 -56
- package/dist/client/runtime/mutex.js +3 -2
- package/dist/component/_generated/component.d.ts +40 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/http.js +9 -0
- package/dist/component/index.d.ts +1 -1
- package/dist/component/model.d.ts +25 -25
- package/dist/component/model.js +2 -1
- package/dist/component/modules.js +1 -0
- package/dist/component/public/factors/passkeys.js +31 -1
- package/dist/component/public/identity/codes.js +1 -1
- package/dist/component/public/identity/tokens.js +2 -1
- package/dist/component/public/identity/verifiers.js +15 -5
- package/dist/component/public.js +2 -2
- package/dist/component/schema.d.ts +292 -290
- package/dist/component/schema.js +2 -1
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.js +7 -2
- package/dist/expo/index.d.ts +21 -0
- package/dist/expo/index.js +148 -0
- package/dist/expo/passkey.js +174 -0
- package/dist/providers/apple.d.ts +1 -1
- package/dist/providers/apple.js +6 -8
- package/dist/providers/custom.d.ts +1 -1
- package/dist/providers/custom.js +4 -7
- package/dist/providers/github.d.ts +1 -1
- package/dist/providers/github.js +5 -8
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.js +5 -8
- package/dist/providers/microsoft.d.ts +1 -1
- package/dist/providers/microsoft.js +5 -9
- package/dist/providers/password.d.ts +18 -37
- package/dist/providers/password.js +170 -115
- package/dist/providers/redirect.d.ts +1 -0
- package/dist/providers/redirect.js +20 -0
- package/dist/server/auth.d.ts +6 -7
- package/dist/server/auth.js +3 -2
- package/dist/server/{ctxCache.js → cache/context.js} +2 -2
- package/dist/server/{componentContext.d.ts → component/context.d.ts} +2 -2
- package/dist/server/context.js +3 -10
- package/dist/server/contract.d.ts +2 -87
- package/dist/server/contract.js +1 -1
- package/dist/server/cookies.js +25 -1
- package/dist/server/core.js +1 -1
- package/dist/server/errors.js +24 -1
- package/dist/server/{auth-context.d.ts → facade.d.ts} +3 -45
- package/dist/server/{auth-context.js → facade.js} +11 -12
- package/dist/server/http.d.ts +7 -7
- package/dist/server/http.js +36 -7
- package/dist/server/{convexIdentity.d.ts → identity/convex.d.ts} +3 -3
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +3 -2
- package/dist/server/mounts.d.ts +175 -22
- package/dist/server/mutations/code.js +7 -1
- package/dist/server/mutations/{credentialsSignIn.js → credentials/signin.js} +10 -10
- package/dist/server/mutations/index.js +1 -1
- package/dist/server/mutations/invalidate.js +11 -1
- package/dist/server/mutations/oauth.js +25 -27
- package/dist/server/mutations/signin.js +6 -0
- package/dist/server/mutations/signout.js +5 -0
- package/dist/server/mutations/store.js +1 -1
- package/dist/server/oauth/factory.js +11 -3
- package/dist/server/passkey.js +126 -110
- package/dist/server/prefetch.js +8 -1
- package/dist/server/redirects.js +11 -3
- package/dist/server/refresh.js +6 -1
- package/dist/server/runtime.d.ts +58 -36
- package/dist/server/runtime.js +333 -36
- package/dist/server/services/group.js +4 -0
- package/dist/server/sessions.js +1 -0
- package/dist/server/signin.js +8 -6
- package/dist/server/sso/domain.d.ts +159 -16
- package/dist/server/sso/domain.js +1 -1
- package/dist/server/sso/http.js +144 -60
- package/dist/server/sso/oidc.js +28 -12
- package/dist/server/sso/policy.js +30 -14
- package/dist/server/sso/provision.js +1 -1
- package/dist/server/sso/saml.js +18 -9
- package/dist/server/sso/scim.js +12 -4
- package/dist/server/sso/shared.js +5 -5
- package/dist/server/telemetry.js +3 -0
- package/dist/server/tokens.js +10 -2
- package/dist/server/totp.js +127 -100
- package/dist/server/types.d.ts +224 -151
- package/dist/server/url.js +1 -1
- package/dist/server/users.js +93 -53
- package/dist/server/wellknown.d.ts +130 -0
- package/dist/server/wellknown.js +195 -0
- package/dist/shared/errors.js +0 -1
- package/package.json +36 -4
- package/dist/server/oauth/index.js +0 -12
- package/dist/server/utils/dispatch.js +0 -36
- package/dist/shared/authResults.d.ts +0 -16
- /package/dist/server/{componentContext.js → component/context.js} +0 -0
- /package/dist/server/{convexIdentity.js → identity/convex.js} +0 -0
package/dist/server/passkey.js
CHANGED
|
@@ -28,17 +28,20 @@ import { COSEKeyType, ClientDataType, coseAlgorithmES256, coseAlgorithmRS256, cr
|
|
|
28
28
|
*
|
|
29
29
|
* @module
|
|
30
30
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Passkey provider has three single-word flows:
|
|
33
|
+
*
|
|
34
|
+
* - `register` — issue a WebAuthn registration challenge (phase 1).
|
|
35
|
+
* - `signIn` — issue a WebAuthn authentication challenge (phase 1).
|
|
36
|
+
* - `verify` — consume the WebAuthn response (phase 2). The server
|
|
37
|
+
* auto-detects whether this completes a registration
|
|
38
|
+
* (`attestationObject` present) or a sign-in
|
|
39
|
+
* (`signature` + `credentialId` present).
|
|
40
|
+
*/
|
|
37
41
|
const PASSKEY_FLOWS = [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
PASSKEY_FLOW.authVerify
|
|
42
|
+
"register",
|
|
43
|
+
"signIn",
|
|
44
|
+
"verify"
|
|
42
45
|
];
|
|
43
46
|
const requireStringParam = (value, name) => {
|
|
44
47
|
if (typeof value !== "string") throw convexError("INVALID_PARAMETERS", `Missing \`${name}\` parameter.`);
|
|
@@ -107,7 +110,7 @@ function verifyUserFlags(authData, rp) {
|
|
|
107
110
|
function resolvePasskeyDispatch(params) {
|
|
108
111
|
const flow = params.flow;
|
|
109
112
|
if (typeof flow === "string" && PASSKEY_FLOWS.includes(flow)) return { flow };
|
|
110
|
-
throw convexError("PASSKEY_MISSING_FLOW", "Missing `flow` parameter. Expected one of:
|
|
113
|
+
throw convexError("PASSKEY_MISSING_FLOW", "Missing `flow` parameter. Expected one of: " + PASSKEY_FLOWS.join(", "));
|
|
111
114
|
}
|
|
112
115
|
function requirePasskeyVerifier(verifier) {
|
|
113
116
|
if (verifier != null) return verifier;
|
|
@@ -163,8 +166,112 @@ function verifyAssertionSignature(passkey, signature, messageHash) {
|
|
|
163
166
|
async function handlePasskeyFx(ctx, provider, args) {
|
|
164
167
|
const params = args.params ?? {};
|
|
165
168
|
const dispatch = resolvePasskeyDispatch(params);
|
|
169
|
+
const handleRegisterVerify = async () => {
|
|
170
|
+
const userId = await requireAuthenticatedUserId(ctx);
|
|
171
|
+
const rp = resolveRpOptions(provider);
|
|
172
|
+
const verifier = requirePasskeyVerifier(args.verifier);
|
|
173
|
+
const clientData = parseClientDataJSON(decodeBase64urlIgnorePadding(requireStringParam(params.clientDataJSON, "clientDataJSON")));
|
|
174
|
+
verifyClientDataType(clientData, ClientDataType.Create, "webauthn.create");
|
|
175
|
+
verifyOrigin(clientData, rp);
|
|
176
|
+
await verifyAndConsumeChallenge(clientData, ctx, verifier);
|
|
177
|
+
const authData = parseAttestationObject(decodeBase64urlIgnorePadding(requireStringParam(params.attestationObject, "attestationObject"))).authenticatorData;
|
|
178
|
+
verifyRpId(authData, rp.rpId);
|
|
179
|
+
verifyUserFlags(authData, rp);
|
|
180
|
+
if (authData.credential == null) throw convexError("PASSKEY_NO_CREDENTIAL", "No credential in attestation.");
|
|
181
|
+
const credential = authData.credential;
|
|
182
|
+
const credentialId = encodeBase64urlNoPadding(credential.id);
|
|
183
|
+
const publicKey = credential.publicKey;
|
|
184
|
+
const algorithm = publicKey.isAlgorithmDefined() ? publicKey.algorithm() : publicKey.type() === COSEKeyType.EC2 ? coseAlgorithmES256 : publicKey.type() === COSEKeyType.RSA ? coseAlgorithmRS256 : coseAlgorithmES256;
|
|
185
|
+
const publicKeyBytes = resolveRegistrationPublicKeyBytes(publicKey, algorithm);
|
|
186
|
+
try {
|
|
187
|
+
const deviceType = params.deviceType ?? "single-device";
|
|
188
|
+
const backedUp = params.backedUp ?? false;
|
|
189
|
+
await authDb(ctx, ctx.auth.config).accounts.create({
|
|
190
|
+
userId,
|
|
191
|
+
provider: provider.id,
|
|
192
|
+
providerAccountId: credentialId
|
|
193
|
+
});
|
|
194
|
+
const passkeyId = await mutatePasskeyInsert(ctx, {
|
|
195
|
+
userId,
|
|
196
|
+
credentialId,
|
|
197
|
+
publicKey: publicKeyBytes.buffer.slice(publicKeyBytes.byteOffset, publicKeyBytes.byteOffset + publicKeyBytes.byteLength),
|
|
198
|
+
algorithm,
|
|
199
|
+
counter: authData.signatureCounter,
|
|
200
|
+
transports: params.transports,
|
|
201
|
+
deviceType,
|
|
202
|
+
backedUp,
|
|
203
|
+
name: params.passkeyName,
|
|
204
|
+
createdAt: Date.now()
|
|
205
|
+
});
|
|
206
|
+
await ctx.auth.config.callbacks?.after?.(ctx, {
|
|
207
|
+
kind: "passkeyAdded",
|
|
208
|
+
userId,
|
|
209
|
+
passkeyId,
|
|
210
|
+
credentialId
|
|
211
|
+
});
|
|
212
|
+
} catch {
|
|
213
|
+
throw convexError("INTERNAL_ERROR", "An unexpected error occurred.");
|
|
214
|
+
}
|
|
215
|
+
let signInResult;
|
|
216
|
+
try {
|
|
217
|
+
signInResult = await callSignIn(ctx, {
|
|
218
|
+
userId,
|
|
219
|
+
generateTokens: true
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey registration.");
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
kind: "signedIn",
|
|
226
|
+
session: signInResult
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
const handleAuthVerify = async () => {
|
|
230
|
+
const rp = resolveRpOptions(provider);
|
|
231
|
+
const verifier = requirePasskeyVerifier(args.verifier);
|
|
232
|
+
const clientDataJSON = decodeBase64urlIgnorePadding(requireStringParam(params.clientDataJSON, "clientDataJSON"));
|
|
233
|
+
const clientData = parseClientDataJSON(clientDataJSON);
|
|
234
|
+
verifyClientDataType(clientData, ClientDataType.Get, "webauthn.get");
|
|
235
|
+
verifyOrigin(clientData, rp);
|
|
236
|
+
await verifyAndConsumeChallenge(clientData, ctx, verifier);
|
|
237
|
+
const credentialId = params.credentialId;
|
|
238
|
+
if (credentialId == null) throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Missing credential ID");
|
|
239
|
+
let passkey;
|
|
240
|
+
try {
|
|
241
|
+
passkey = await queryPasskeyByCredentialId(ctx, credentialId);
|
|
242
|
+
} catch {
|
|
243
|
+
throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Unknown passkey credential.");
|
|
244
|
+
}
|
|
245
|
+
if (passkey === null) throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Unknown credential");
|
|
246
|
+
const authenticatorDataBytes = decodeBase64urlIgnorePadding(requireStringParam(params.authenticatorData, "authenticatorData"));
|
|
247
|
+
const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);
|
|
248
|
+
const signatureBytes = decodeBase64urlIgnorePadding(requireStringParam(params.signature, "signature"));
|
|
249
|
+
const messageHash = sha256(createAssertionSignatureMessage(authenticatorDataBytes, clientDataJSON));
|
|
250
|
+
verifyRpId(authenticatorData, rp.rpId);
|
|
251
|
+
verifyUserFlags(authenticatorData, rp);
|
|
252
|
+
verifyAssertionSignature(passkey, signatureBytes, messageHash);
|
|
253
|
+
if (passkey.counter !== 0 && authenticatorData.signatureCounter !== 0 && authenticatorData.signatureCounter <= passkey.counter) throw convexError("PASSKEY_COUNTER_ERROR", "Authenticator counter did not increase — possible credential cloning detected.");
|
|
254
|
+
try {
|
|
255
|
+
await mutatePasskeyUpdateCounter(ctx, passkey._id, authenticatorData.signatureCounter, Date.now());
|
|
256
|
+
} catch (error) {
|
|
257
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to update passkey counter.");
|
|
258
|
+
}
|
|
259
|
+
let signInResult;
|
|
260
|
+
try {
|
|
261
|
+
signInResult = await callSignIn(ctx, {
|
|
262
|
+
userId: passkey.userId,
|
|
263
|
+
generateTokens: true
|
|
264
|
+
});
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey sign-in.");
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
kind: "signedIn",
|
|
270
|
+
session: signInResult
|
|
271
|
+
};
|
|
272
|
+
};
|
|
166
273
|
const handler = {
|
|
167
|
-
|
|
274
|
+
register: async () => {
|
|
168
275
|
const userId = await requireAuthenticatedUserId(ctx);
|
|
169
276
|
const rp = resolveRpOptions(provider);
|
|
170
277
|
const challenge = new Uint8Array(32);
|
|
@@ -225,61 +332,7 @@ async function handlePasskeyFx(ctx, provider, args) {
|
|
|
225
332
|
verifier
|
|
226
333
|
};
|
|
227
334
|
},
|
|
228
|
-
|
|
229
|
-
const userId = await requireAuthenticatedUserId(ctx);
|
|
230
|
-
const rp = resolveRpOptions(provider);
|
|
231
|
-
const verifier = requirePasskeyVerifier(args.verifier);
|
|
232
|
-
const clientData = parseClientDataJSON(decodeBase64urlIgnorePadding(requireStringParam(params.clientDataJSON, "clientDataJSON")));
|
|
233
|
-
verifyClientDataType(clientData, ClientDataType.Create, "webauthn.create");
|
|
234
|
-
verifyOrigin(clientData, rp);
|
|
235
|
-
await verifyAndConsumeChallenge(clientData, ctx, verifier);
|
|
236
|
-
const authData = parseAttestationObject(decodeBase64urlIgnorePadding(requireStringParam(params.attestationObject, "attestationObject"))).authenticatorData;
|
|
237
|
-
verifyRpId(authData, rp.rpId);
|
|
238
|
-
verifyUserFlags(authData, rp);
|
|
239
|
-
if (authData.credential == null) throw convexError("PASSKEY_NO_CREDENTIAL", "No credential in attestation.");
|
|
240
|
-
const credential = authData.credential;
|
|
241
|
-
const credentialId = encodeBase64urlNoPadding(credential.id);
|
|
242
|
-
const publicKey = credential.publicKey;
|
|
243
|
-
const algorithm = publicKey.isAlgorithmDefined() ? publicKey.algorithm() : publicKey.type() === COSEKeyType.EC2 ? coseAlgorithmES256 : publicKey.type() === COSEKeyType.RSA ? coseAlgorithmRS256 : coseAlgorithmES256;
|
|
244
|
-
const publicKeyBytes = resolveRegistrationPublicKeyBytes(publicKey, algorithm);
|
|
245
|
-
try {
|
|
246
|
-
const deviceType = params.deviceType ?? "single-device";
|
|
247
|
-
const backedUp = params.backedUp ?? false;
|
|
248
|
-
await authDb(ctx, ctx.auth.config).accounts.create({
|
|
249
|
-
userId,
|
|
250
|
-
provider: provider.id,
|
|
251
|
-
providerAccountId: credentialId
|
|
252
|
-
});
|
|
253
|
-
await mutatePasskeyInsert(ctx, {
|
|
254
|
-
userId,
|
|
255
|
-
credentialId,
|
|
256
|
-
publicKey: publicKeyBytes.buffer.slice(publicKeyBytes.byteOffset, publicKeyBytes.byteOffset + publicKeyBytes.byteLength),
|
|
257
|
-
algorithm,
|
|
258
|
-
counter: authData.signatureCounter,
|
|
259
|
-
transports: params.transports,
|
|
260
|
-
deviceType,
|
|
261
|
-
backedUp,
|
|
262
|
-
name: params.passkeyName,
|
|
263
|
-
createdAt: Date.now()
|
|
264
|
-
});
|
|
265
|
-
} catch {
|
|
266
|
-
throw convexError("INTERNAL_ERROR", "An unexpected error occurred.");
|
|
267
|
-
}
|
|
268
|
-
let signInResult;
|
|
269
|
-
try {
|
|
270
|
-
signInResult = await callSignIn(ctx, {
|
|
271
|
-
userId,
|
|
272
|
-
generateTokens: true
|
|
273
|
-
});
|
|
274
|
-
} catch (error) {
|
|
275
|
-
throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey registration.");
|
|
276
|
-
}
|
|
277
|
-
return {
|
|
278
|
-
kind: "signedIn",
|
|
279
|
-
session: signInResult
|
|
280
|
-
};
|
|
281
|
-
},
|
|
282
|
-
authOptions: async () => {
|
|
335
|
+
signIn: async () => {
|
|
283
336
|
const rp = resolveRpOptions(provider);
|
|
284
337
|
const challenge = new Uint8Array(32);
|
|
285
338
|
crypto.getRandomValues(challenge);
|
|
@@ -326,49 +379,12 @@ async function handlePasskeyFx(ctx, provider, args) {
|
|
|
326
379
|
verifier
|
|
327
380
|
};
|
|
328
381
|
},
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
verifyOrigin(clientData, rp);
|
|
336
|
-
await verifyAndConsumeChallenge(clientData, ctx, verifier);
|
|
337
|
-
const credentialId = params.credentialId;
|
|
338
|
-
if (credentialId == null) throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Missing credential ID");
|
|
339
|
-
let passkey;
|
|
340
|
-
try {
|
|
341
|
-
passkey = await queryPasskeyByCredentialId(ctx, credentialId);
|
|
342
|
-
} catch {
|
|
343
|
-
throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Unknown passkey credential.");
|
|
344
|
-
}
|
|
345
|
-
if (passkey === null) throw convexError("PASSKEY_UNKNOWN_CREDENTIAL", "Unknown credential");
|
|
346
|
-
const authenticatorDataBytes = decodeBase64urlIgnorePadding(requireStringParam(params.authenticatorData, "authenticatorData"));
|
|
347
|
-
const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);
|
|
348
|
-
const signatureBytes = decodeBase64urlIgnorePadding(requireStringParam(params.signature, "signature"));
|
|
349
|
-
const messageHash = sha256(createAssertionSignatureMessage(authenticatorDataBytes, clientDataJSON));
|
|
350
|
-
verifyRpId(authenticatorData, rp.rpId);
|
|
351
|
-
verifyUserFlags(authenticatorData, rp);
|
|
352
|
-
verifyAssertionSignature(passkey, signatureBytes, messageHash);
|
|
353
|
-
if (passkey.counter !== 0 && authenticatorData.signatureCounter !== 0 && authenticatorData.signatureCounter <= passkey.counter) throw convexError("PASSKEY_COUNTER_ERROR", "Authenticator counter did not increase — possible credential cloning detected.");
|
|
354
|
-
try {
|
|
355
|
-
await mutatePasskeyUpdateCounter(ctx, passkey._id, authenticatorData.signatureCounter, Date.now());
|
|
356
|
-
} catch (error) {
|
|
357
|
-
throw asConvexError(error, "INTERNAL_ERROR", "Failed to update passkey counter.");
|
|
358
|
-
}
|
|
359
|
-
let signInResult;
|
|
360
|
-
try {
|
|
361
|
-
signInResult = await callSignIn(ctx, {
|
|
362
|
-
userId: passkey.userId,
|
|
363
|
-
generateTokens: true
|
|
364
|
-
});
|
|
365
|
-
} catch (error) {
|
|
366
|
-
throw asConvexError(error, "INTERNAL_ERROR", "Failed to finalize passkey sign-in.");
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
kind: "signedIn",
|
|
370
|
-
session: signInResult
|
|
371
|
-
};
|
|
382
|
+
verify: async () => {
|
|
383
|
+
const isRegistration = typeof params.attestationObject === "string" && params.attestationObject.length > 0;
|
|
384
|
+
const isAuthentication = typeof params.signature === "string" && params.signature.length > 0;
|
|
385
|
+
if (isRegistration && !isAuthentication) return await handleRegisterVerify();
|
|
386
|
+
if (isAuthentication && !isRegistration) return await handleAuthVerify();
|
|
387
|
+
throw convexError("PASSKEY_INVALID_VERIFY", "`verify` flow requires either `attestationObject` (to complete a `register`) or `signature` + `credentialId` (to complete a `signIn`).");
|
|
372
388
|
}
|
|
373
389
|
}[dispatch.flow];
|
|
374
390
|
if (!handler) throw convexError("PASSKEY_MISSING_FLOW", `Unknown passkey flow: ${dispatch.flow}`);
|
package/dist/server/prefetch.js
CHANGED
|
@@ -239,10 +239,17 @@ function convexSiteIssuerFromCloudUrl(value) {
|
|
|
239
239
|
parsed.hostname = parsed.hostname.slice(0, -13) + ".convex.site";
|
|
240
240
|
return normalizeIssuer(parsed.toString());
|
|
241
241
|
}
|
|
242
|
+
function appendAuthIssuerPrefix(value) {
|
|
243
|
+
const issuer = normalizeIssuer(value);
|
|
244
|
+
return issuer.endsWith("/auth") ? issuer : `${issuer}/auth`;
|
|
245
|
+
}
|
|
242
246
|
function defaultAcceptedIssuersForUrl(value) {
|
|
243
247
|
const issuers = [normalizeIssuer(value)];
|
|
244
248
|
const siteIssuer = convexSiteIssuerFromCloudUrl(value);
|
|
245
|
-
if (siteIssuer !== null)
|
|
249
|
+
if (siteIssuer !== null) {
|
|
250
|
+
issuers.push(siteIssuer);
|
|
251
|
+
issuers.push(appendAuthIssuerPrefix(siteIssuer));
|
|
252
|
+
}
|
|
246
253
|
return issuers;
|
|
247
254
|
}
|
|
248
255
|
/**
|
package/dist/server/redirects.js
CHANGED
|
@@ -8,15 +8,23 @@ const describeUnknown = (value) => {
|
|
|
8
8
|
return JSON.stringify(value) ?? Object.prototype.toString.call(value);
|
|
9
9
|
};
|
|
10
10
|
/** @internal */
|
|
11
|
-
async function redirectAbsoluteUrl(config, params) {
|
|
11
|
+
async function redirectAbsoluteUrl(ctx, config, params) {
|
|
12
12
|
if (params.redirectTo === void 0) return requireEnv("SITE_URL").replace(/\/$/, "");
|
|
13
13
|
if (typeof params.redirectTo !== "string") throw new ConvexError({
|
|
14
14
|
code: "INVALID_REDIRECT",
|
|
15
15
|
message: `Expected \`redirectTo\` to be a string, got ${describeUnknown(params.redirectTo)}`
|
|
16
16
|
});
|
|
17
|
-
const
|
|
17
|
+
const redirectTo = params.redirectTo;
|
|
18
18
|
try {
|
|
19
|
-
|
|
19
|
+
const before = config.callbacks?.before;
|
|
20
|
+
if (before !== void 0) {
|
|
21
|
+
const result = await before(ctx, {
|
|
22
|
+
kind: "redirect",
|
|
23
|
+
redirectTo
|
|
24
|
+
});
|
|
25
|
+
if (typeof result === "string") return result;
|
|
26
|
+
}
|
|
27
|
+
return await defaultRedirectCallback({ redirectTo });
|
|
20
28
|
} catch {
|
|
21
29
|
throw new ConvexError({
|
|
22
30
|
code: "INTERNAL_ERROR",
|
package/dist/server/refresh.js
CHANGED
|
@@ -10,8 +10,13 @@ const REFRESH_TOKEN_REUSE_WINDOW_MS = 10 * 1e3;
|
|
|
10
10
|
const refreshTokenExpirationTime = (config, now = Date.now()) => now + (config.session?.inactiveDurationMs ?? readConfigSync(envOptionalNumber("AUTH_SESSION_INACTIVE_DURATION_MS")) ?? DEFAULT_SESSION_INACTIVE_DURATION_MS);
|
|
11
11
|
/** @internal */
|
|
12
12
|
const parseRefreshToken = (refreshToken) => {
|
|
13
|
-
const
|
|
13
|
+
const parts = refreshToken.split(REFRESH_TOKEN_DIVIDER);
|
|
14
14
|
const message = `Can't parse refresh token: ${maybeRedact(refreshToken)}`;
|
|
15
|
+
if (parts.length !== 2) throw new ConvexError({
|
|
16
|
+
code: "INVALID_REFRESH_TOKEN",
|
|
17
|
+
message
|
|
18
|
+
});
|
|
19
|
+
const [refreshTokenId, sessionId] = parts;
|
|
15
20
|
if (refreshTokenId == null || sessionId == null) throw new ConvexError({
|
|
16
21
|
code: "INVALID_REFRESH_TOKEN",
|
|
17
22
|
message
|
package/dist/server/runtime.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { AuthProviderConfig, ConvexAuthConfig, CorsConfig, Doc, HttpKeyContext, KeyDoc, KeyScope, ScopeChecker, UserOrderBy, UserWhere } from "./types.js";
|
|
2
|
-
import { ComponentCtx, ComponentReadCtx } from "./
|
|
2
|
+
import { ComponentCtx, ComponentReadCtx } from "./component/context.js";
|
|
3
3
|
import { AuthProfile, SignInParams } from "./payloads.js";
|
|
4
4
|
import { HttpAuthContext, HttpAuthContextConfig, OptionalHttpAuthContext } from "./http.js";
|
|
5
5
|
import { createGroupConnectionDomain } from "./sso/domain.js";
|
|
6
|
-
import
|
|
7
|
-
import * as
|
|
6
|
+
import { GenericId } from "convex/values";
|
|
7
|
+
import * as convex_server105 from "convex/server";
|
|
8
8
|
import { GenericActionCtx, GenericDataModel, HttpRouter } from "convex/server";
|
|
9
9
|
|
|
10
10
|
//#region src/server/runtime.d.ts
|
|
11
|
+
type AuthHttpRouteOptions = {
|
|
12
|
+
/** Prefix where auth HTTP routes are mounted, e.g. `/auth`. */prefix?: string;
|
|
13
|
+
};
|
|
11
14
|
/**
|
|
12
15
|
* Configure the Convex Auth library. Returns an object with
|
|
13
16
|
* functions and `auth` helper. You must export the functions
|
|
@@ -20,16 +23,16 @@ import { GenericActionCtx, GenericDataModel, HttpRouter } from "convex/server";
|
|
|
20
23
|
* export const auth = createAuth(components.auth, {
|
|
21
24
|
* providers: [],
|
|
22
25
|
* });
|
|
23
|
-
* export const
|
|
26
|
+
* export const signIn = auth.signIn;
|
|
27
|
+
* export const signOut = auth.signOut;
|
|
28
|
+
* export const store = auth.store;
|
|
29
|
+
* export const http = auth.http;
|
|
24
30
|
* ```
|
|
25
31
|
*
|
|
26
32
|
* @returns An object with fields you should reexport from your
|
|
27
33
|
* `convex/auth.ts` file.
|
|
28
34
|
*/
|
|
29
35
|
declare function Auth(config_: ConvexAuthConfig): {
|
|
30
|
-
/**
|
|
31
|
-
* Helper for configuring HTTP actions.
|
|
32
|
-
*/
|
|
33
36
|
auth: {
|
|
34
37
|
user: {
|
|
35
38
|
get: {
|
|
@@ -44,7 +47,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
44
47
|
order?: "asc" | "desc";
|
|
45
48
|
}) => Promise<any>;
|
|
46
49
|
viewer: (ctx: ComponentReadCtx & {
|
|
47
|
-
auth:
|
|
50
|
+
auth: convex_server105.Auth;
|
|
48
51
|
}) => Promise<Doc<"User"> | null>;
|
|
49
52
|
update: (ctx: ComponentCtx, userId: string, data: Record<string, unknown>) => Promise<{
|
|
50
53
|
userId: string;
|
|
@@ -70,11 +73,11 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
70
73
|
};
|
|
71
74
|
session: {
|
|
72
75
|
invalidate: <DataModel extends GenericDataModel>(ctx: GenericActionCtx<DataModel>, args: {
|
|
73
|
-
userId:
|
|
74
|
-
except?:
|
|
76
|
+
userId: GenericId<"User">;
|
|
77
|
+
except?: GenericId<"Session">[];
|
|
75
78
|
}) => Promise<{
|
|
76
|
-
userId:
|
|
77
|
-
except:
|
|
79
|
+
userId: GenericId<"User">;
|
|
80
|
+
except: GenericId<"Session">[];
|
|
78
81
|
}>;
|
|
79
82
|
get: (ctx: ComponentReadCtx, sessionId: string) => Promise<any>;
|
|
80
83
|
list: (ctx: ComponentReadCtx, opts: {
|
|
@@ -143,7 +146,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
143
146
|
};
|
|
144
147
|
provider: {
|
|
145
148
|
signIn: (<DataModel extends GenericDataModel>(ctx: GenericActionCtx<DataModel>, providerConfig: AuthProviderConfig, args: {
|
|
146
|
-
accountId?:
|
|
149
|
+
accountId?: GenericId<"Account">;
|
|
147
150
|
params?: SignInParams;
|
|
148
151
|
}) => Promise<{
|
|
149
152
|
userId: string;
|
|
@@ -397,7 +400,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
397
400
|
} & {
|
|
398
401
|
sso: ReturnType<typeof createGroupConnectionDomain>;
|
|
399
402
|
} & {
|
|
400
|
-
|
|
403
|
+
request: {
|
|
401
404
|
/**
|
|
402
405
|
* Register core HTTP routes for JWT verification and OAuth sign-in.
|
|
403
406
|
*
|
|
@@ -405,26 +408,39 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
405
408
|
* import { httpRouter } from "convex/server";
|
|
406
409
|
* import { auth } from "./auth";
|
|
407
410
|
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* auth.http.add(http);
|
|
411
|
-
*
|
|
412
|
-
* export default http;
|
|
411
|
+
* export default auth.http();
|
|
413
412
|
* ```
|
|
414
413
|
*
|
|
415
414
|
* The following routes are handled always:
|
|
416
415
|
*
|
|
417
416
|
* - `/.well-known/openid-configuration`
|
|
418
417
|
* - `/.well-known/jwks.json`
|
|
418
|
+
* - `/.well-known/webauthn` (returns 404 unless `WEBAUTHN_ALT_ORIGINS` or
|
|
419
|
+
* `SECONDARY_URL` is set)
|
|
419
420
|
*
|
|
420
421
|
* The following routes are handled if OAuth is configured:
|
|
421
422
|
*
|
|
422
|
-
* - `/
|
|
423
|
-
* - `/
|
|
423
|
+
* - `/signin/*`
|
|
424
|
+
* - `/callback/*`
|
|
424
425
|
*
|
|
425
426
|
* @param http your HTTP router
|
|
427
|
+
* @param options.prefix where to mount auth protocol routes, e.g. `/auth`
|
|
426
428
|
*/
|
|
427
|
-
add: (http: HttpRouter) => void;
|
|
429
|
+
add: (http: HttpRouter, options?: AuthHttpRouteOptions) => void;
|
|
430
|
+
/**
|
|
431
|
+
* Create a Convex HTTP router with auth protocol routes already mounted.
|
|
432
|
+
*
|
|
433
|
+
* Defaults to the `/auth` prefix. Use {@link request.add add} instead when
|
|
434
|
+
* you need to compose auth routes with app-specific HTTP routes in the same
|
|
435
|
+
* router.
|
|
436
|
+
*
|
|
437
|
+
* ```ts
|
|
438
|
+
* import { auth } from "./auth";
|
|
439
|
+
*
|
|
440
|
+
* export default auth.http();
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
router: (options?: AuthHttpRouteOptions) => HttpRouter;
|
|
428
444
|
/**
|
|
429
445
|
* Resolve mixed HTTP auth for a raw `httpAction`.
|
|
430
446
|
*
|
|
@@ -439,7 +455,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
439
455
|
* path: "/api/data",
|
|
440
456
|
* method: "GET",
|
|
441
457
|
* handler: httpAction(async (ctx, request) => {
|
|
442
|
-
* const authContext = await auth.
|
|
458
|
+
* const authContext = await auth.request.context(ctx, request);
|
|
443
459
|
* return Response.json({
|
|
444
460
|
* userId: authContext.userId,
|
|
445
461
|
* source: authContext.source,
|
|
@@ -451,22 +467,22 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
451
467
|
context: {
|
|
452
468
|
<TResolve extends Record<string, unknown> = Record<string, never>, TCtx extends {
|
|
453
469
|
auth: {
|
|
454
|
-
getUserIdentity: () => Promise<
|
|
470
|
+
getUserIdentity: () => Promise<convex_server105.UserIdentity | null>;
|
|
455
471
|
};
|
|
456
472
|
} & ComponentReadCtx = {
|
|
457
473
|
auth: {
|
|
458
|
-
getUserIdentity: () => Promise<
|
|
474
|
+
getUserIdentity: () => Promise<convex_server105.UserIdentity | null>;
|
|
459
475
|
};
|
|
460
476
|
} & ComponentReadCtx>(ctx: TCtx, request: Request, config: HttpAuthContextConfig<TResolve, TCtx> & {
|
|
461
477
|
optional: true;
|
|
462
478
|
}): Promise<OptionalHttpAuthContext & TResolve>;
|
|
463
479
|
<TResolve extends Record<string, unknown> = Record<string, never>, TCtx extends {
|
|
464
480
|
auth: {
|
|
465
|
-
getUserIdentity: () => Promise<
|
|
481
|
+
getUserIdentity: () => Promise<convex_server105.UserIdentity | null>;
|
|
466
482
|
};
|
|
467
483
|
} & ComponentReadCtx = {
|
|
468
484
|
auth: {
|
|
469
|
-
getUserIdentity: () => Promise<
|
|
485
|
+
getUserIdentity: () => Promise<convex_server105.UserIdentity | null>;
|
|
470
486
|
};
|
|
471
487
|
} & ComponentReadCtx>(ctx: TCtx, request: Request, config?: HttpAuthContextConfig<TResolve, TCtx>): Promise<HttpAuthContext & TResolve>;
|
|
472
488
|
};
|
|
@@ -483,7 +499,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
483
499
|
* are merged and the response is passed through.
|
|
484
500
|
*
|
|
485
501
|
* ```ts
|
|
486
|
-
* const handler = auth.
|
|
502
|
+
* const handler = auth.request.action(async (ctx, request) => {
|
|
487
503
|
* const data = await ctx.runQuery(api.data.get, { userId: ctx.key.userId });
|
|
488
504
|
* return { data };
|
|
489
505
|
* });
|
|
@@ -500,13 +516,13 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
500
516
|
action: string;
|
|
501
517
|
};
|
|
502
518
|
cors?: CorsConfig;
|
|
503
|
-
}) =>
|
|
519
|
+
}) => convex_server105.PublicHttpAction;
|
|
504
520
|
/**
|
|
505
521
|
* Register a Bearer-authenticated route **and** its OPTIONS preflight
|
|
506
522
|
* in a single call.
|
|
507
523
|
*
|
|
508
524
|
* ```ts
|
|
509
|
-
* auth.
|
|
525
|
+
* auth.request.route(http, {
|
|
510
526
|
* path: "/api/messages",
|
|
511
527
|
* method: "POST",
|
|
512
528
|
* handler: async (ctx, request) => {
|
|
@@ -546,7 +562,7 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
546
562
|
*
|
|
547
563
|
* Also used for refreshing the session.
|
|
548
564
|
*/
|
|
549
|
-
signIn:
|
|
565
|
+
signIn: convex_server105.RegisteredAction<"public", {
|
|
550
566
|
provider?: string | undefined;
|
|
551
567
|
verifier?: string | undefined;
|
|
552
568
|
params?: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null> | undefined;
|
|
@@ -556,12 +572,12 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
556
572
|
/**
|
|
557
573
|
* Action called by the client to invalidate the current session.
|
|
558
574
|
*/
|
|
559
|
-
signOut:
|
|
575
|
+
signOut: convex_server105.RegisteredAction<"public", {}, Promise<void>>;
|
|
560
576
|
/**
|
|
561
577
|
* Internal mutation used by the library to read and write
|
|
562
578
|
* to the database during signin and signout.
|
|
563
579
|
*/
|
|
564
|
-
store:
|
|
580
|
+
store: convex_server105.RegisteredMutation<"internal", {
|
|
565
581
|
args: {
|
|
566
582
|
sessionId?: string | undefined;
|
|
567
583
|
type: "signIn";
|
|
@@ -604,11 +620,11 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
604
620
|
sessionIndex?: string | undefined;
|
|
605
621
|
} | undefined;
|
|
606
622
|
} | undefined;
|
|
607
|
-
profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
|
|
608
623
|
type: "userOAuth";
|
|
609
624
|
provider: string;
|
|
610
625
|
providerAccountId: string;
|
|
611
626
|
signature: string;
|
|
627
|
+
profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
|
|
612
628
|
} | {
|
|
613
629
|
email?: string | undefined;
|
|
614
630
|
phone?: string | undefined;
|
|
@@ -621,9 +637,9 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
621
637
|
} | {
|
|
622
638
|
shouldLinkViaEmail?: boolean | undefined;
|
|
623
639
|
shouldLinkViaPhone?: boolean | undefined;
|
|
624
|
-
profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
|
|
625
640
|
type: "createAccountFromCredentials";
|
|
626
641
|
provider: string;
|
|
642
|
+
profile: Record<string, string | number | boolean | (string | number | boolean | null)[] | Record<string, string | number | boolean | (string | number | boolean | null)[] | null> | null>;
|
|
627
643
|
account: {
|
|
628
644
|
secret?: string | undefined;
|
|
629
645
|
id: string;
|
|
@@ -657,7 +673,13 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
657
673
|
type: "invalidateSessions";
|
|
658
674
|
userId: string;
|
|
659
675
|
};
|
|
660
|
-
}, Promise<unknown>>;
|
|
676
|
+
}, Promise<unknown>>; /** App-side HTTP protocol delegate retained for generated API compatibility. */
|
|
677
|
+
http: ((options?: AuthHttpRouteOptions) => HttpRouter) & {
|
|
678
|
+
isConvexFunction: true;
|
|
679
|
+
isAction: true;
|
|
680
|
+
} & {
|
|
681
|
+
isPublic: true;
|
|
682
|
+
};
|
|
661
683
|
};
|
|
662
684
|
//#endregion
|
|
663
685
|
export { Auth };
|