@robelest/convex-auth 0.0.4-preview.22 → 0.0.4-preview.24
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/README.md +10 -11
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +36 -39
- package/dist/client/index.js.map +1 -1
- package/dist/component/client/index.d.ts +1 -2
- package/dist/component/index.js +2 -2
- package/dist/component/model.d.ts +9 -9
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.js.map +1 -1
- package/dist/component/public/enterprise/core.d.ts.map +1 -1
- package/dist/component/public/enterprise/core.js.map +1 -1
- package/dist/component/public/enterprise/domains.d.ts.map +1 -1
- package/dist/component/public/enterprise/domains.js.map +1 -1
- package/dist/component/public/enterprise/scim.d.ts.map +1 -1
- package/dist/component/public/enterprise/scim.js.map +1 -1
- package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
- package/dist/component/public/enterprise/secrets.js.map +1 -1
- package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
- package/dist/component/public/enterprise/webhooks.js.map +1 -1
- package/dist/component/public/factors/devices.d.ts.map +1 -1
- package/dist/component/public/factors/devices.js.map +1 -1
- package/dist/component/public/factors/passkeys.d.ts.map +1 -1
- package/dist/component/public/factors/passkeys.js.map +1 -1
- package/dist/component/public/factors/totp.d.ts.map +1 -1
- package/dist/component/public/factors/totp.js.map +1 -1
- package/dist/component/public/groups/core.js.map +1 -1
- package/dist/component/public/groups/invites.d.ts.map +1 -1
- package/dist/component/public/groups/invites.js.map +1 -1
- package/dist/component/public/groups/members.d.ts.map +1 -1
- package/dist/component/public/groups/members.js.map +1 -1
- package/dist/component/public/identity/accounts.d.ts.map +1 -1
- package/dist/component/public/identity/accounts.js.map +1 -1
- package/dist/component/public/identity/codes.d.ts.map +1 -1
- package/dist/component/public/identity/codes.js.map +1 -1
- package/dist/component/public/identity/sessions.d.ts.map +1 -1
- package/dist/component/public/identity/sessions.js.map +1 -1
- package/dist/component/public/identity/tokens.d.ts.map +1 -1
- package/dist/component/public/identity/tokens.js.map +1 -1
- package/dist/component/public/identity/users.d.ts.map +1 -1
- package/dist/component/public/identity/users.js.map +1 -1
- package/dist/component/public/identity/verifiers.d.ts.map +1 -1
- package/dist/component/public/identity/verifiers.js.map +1 -1
- package/dist/component/public/security/keys.d.ts.map +1 -1
- package/dist/component/public/security/keys.js.map +1 -1
- package/dist/component/public/security/limits.d.ts.map +1 -1
- package/dist/component/public/security/limits.js.map +1 -1
- package/dist/component/schema.d.ts +41 -41
- package/dist/component/server/auth.d.ts +127 -130
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +100 -64
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/context.js +53 -0
- package/dist/component/server/context.js.map +1 -0
- package/dist/component/server/core.js +113 -250
- package/dist/component/server/core.js.map +1 -1
- package/dist/component/server/crypto.js +25 -7
- package/dist/component/server/crypto.js.map +1 -1
- package/dist/component/server/device.js +59 -16
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/enterprise/domain.js +148 -59
- package/dist/component/server/enterprise/domain.js.map +1 -1
- package/dist/component/server/enterprise/http.js +36 -15
- package/dist/component/server/enterprise/http.js.map +1 -1
- package/dist/component/server/enterprise/oidc.js +1 -1
- package/dist/component/server/http.d.ts +85 -0
- package/dist/component/server/http.d.ts.map +1 -0
- package/dist/component/server/http.js +85 -22
- package/dist/component/server/http.js.map +1 -1
- package/dist/component/server/identity.js +5 -2
- package/dist/component/server/identity.js.map +1 -1
- package/dist/component/server/limits.js +21 -30
- package/dist/component/server/limits.js.map +1 -1
- package/dist/component/server/mutations/account.js +12 -10
- package/dist/component/server/mutations/account.js.map +1 -1
- package/dist/component/server/mutations/code.js +5 -2
- package/dist/component/server/mutations/code.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/invalidate.js.map +1 -1
- package/dist/component/server/mutations/oauth.js +10 -4
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +2 -2
- package/dist/component/server/mutations/refresh.js.map +1 -1
- package/dist/component/server/mutations/register.js +46 -42
- package/dist/component/server/mutations/register.js.map +1 -1
- package/dist/component/server/mutations/retrieve.js +21 -25
- package/dist/component/server/mutations/retrieve.js.map +1 -1
- package/dist/component/server/mutations/signature.js +10 -4
- package/dist/component/server/mutations/signature.js.map +1 -1
- package/dist/component/server/mutations/signout.js.map +1 -1
- package/dist/component/server/mutations/store.js +9 -24
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verifier.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/mutations/verify.js.map +1 -1
- package/dist/component/server/oauth.js +53 -16
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +115 -31
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/redirects.js +9 -3
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +10 -7
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/runtime.d.ts +5 -5
- package/dist/component/server/runtime.js +156 -113
- package/dist/component/server/runtime.js.map +1 -1
- package/dist/component/server/signin.js +34 -10
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/totp.js +79 -19
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +12 -20
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/types.js.map +1 -1
- package/dist/component/server/users.js +6 -3
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +10 -4
- package/dist/component/server/utils.js.map +1 -1
- package/dist/core/types.d.ts +14 -22
- package/dist/core/types.d.ts.map +1 -1
- package/dist/factors/device.js +8 -9
- package/dist/factors/device.js.map +1 -1
- package/dist/factors/passkey.js +18 -21
- package/dist/factors/passkey.js.map +1 -1
- package/dist/providers/password.js +66 -81
- package/dist/providers/password.js.map +1 -1
- package/dist/runtime/invite.js +2 -8
- package/dist/runtime/invite.js.map +1 -1
- package/dist/server/auth.d.ts +127 -130
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +100 -64
- package/dist/server/auth.js.map +1 -1
- package/dist/server/context.d.ts +1 -0
- package/dist/server/context.js +53 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/core.d.ts +74 -195
- package/dist/server/core.d.ts.map +1 -1
- package/dist/server/core.js +113 -250
- package/dist/server/core.js.map +1 -1
- package/dist/server/crypto.d.ts.map +1 -1
- package/dist/server/crypto.js +25 -7
- package/dist/server/crypto.js.map +1 -1
- package/dist/server/device.js +59 -16
- package/dist/server/device.js.map +1 -1
- package/dist/server/enterprise/domain.d.ts +0 -8
- package/dist/server/enterprise/domain.d.ts.map +1 -1
- package/dist/server/enterprise/domain.js +148 -59
- package/dist/server/enterprise/domain.js.map +1 -1
- package/dist/server/enterprise/http.d.ts.map +1 -1
- package/dist/server/enterprise/http.js +35 -14
- package/dist/server/enterprise/http.js.map +1 -1
- package/dist/server/http.d.ts +81 -3
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +84 -21
- package/dist/server/http.js.map +1 -1
- package/dist/server/identity.js +5 -2
- package/dist/server/identity.js.map +1 -1
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.js +2 -2
- package/dist/server/limits.js +21 -30
- package/dist/server/limits.js.map +1 -1
- package/dist/server/mounts.d.ts +25 -63
- package/dist/server/mounts.d.ts.map +1 -1
- package/dist/server/mounts.js +46 -107
- package/dist/server/mounts.js.map +1 -1
- package/dist/server/mutations/account.d.ts +8 -9
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/account.js +11 -9
- package/dist/server/mutations/account.js.map +1 -1
- package/dist/server/mutations/code.d.ts +12 -12
- package/dist/server/mutations/code.d.ts.map +1 -1
- package/dist/server/mutations/code.js +5 -2
- package/dist/server/mutations/code.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +4 -4
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/invalidate.js.map +1 -1
- package/dist/server/mutations/oauth.d.ts +14 -12
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -3
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +3 -3
- package/dist/server/mutations/refresh.d.ts.map +1 -1
- package/dist/server/mutations/refresh.js +1 -1
- package/dist/server/mutations/refresh.js.map +1 -1
- package/dist/server/mutations/register.d.ts +11 -11
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/register.js +45 -41
- package/dist/server/mutations/register.js.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +6 -6
- package/dist/server/mutations/retrieve.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.js +20 -24
- package/dist/server/mutations/retrieve.js.map +1 -1
- package/dist/server/mutations/signature.d.ts +6 -7
- package/dist/server/mutations/signature.d.ts.map +1 -1
- package/dist/server/mutations/signature.js +9 -3
- package/dist/server/mutations/signature.js.map +1 -1
- package/dist/server/mutations/signin.d.ts +5 -5
- package/dist/server/mutations/signout.js.map +1 -1
- package/dist/server/mutations/store.d.ts +83 -83
- package/dist/server/mutations/store.js +8 -23
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.js.map +1 -1
- package/dist/server/mutations/verify.d.ts +7 -7
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/mutations/verify.js.map +1 -1
- package/dist/server/oauth.js +53 -16
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts +2 -2
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +114 -30
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/redirects.js +9 -3
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.js +10 -7
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/runtime.d.ts +11 -11
- package/dist/server/runtime.js +155 -112
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/signin.js +34 -10
- package/dist/server/signin.js.map +1 -1
- package/dist/server/ssr.d.ts.map +1 -1
- package/dist/server/ssr.js +175 -184
- package/dist/server/ssr.js.map +1 -1
- package/dist/server/totp.js +78 -18
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +13 -21
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.js +6 -3
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.js +10 -4
- package/dist/server/utils.js.map +1 -1
- package/package.json +1 -5
- package/src/authorization/index.ts +1 -1
- package/src/client/core/types.ts +14 -14
- package/src/client/factors/device.ts +10 -12
- package/src/client/factors/passkey.ts +23 -26
- package/src/client/index.ts +54 -64
- package/src/client/runtime/invite.ts +5 -7
- package/src/component/index.ts +9 -3
- package/src/component/public/enterprise/audit.ts +6 -1
- package/src/component/public/enterprise/core.ts +1 -0
- package/src/component/public/enterprise/domains.ts +5 -1
- package/src/component/public/enterprise/scim.ts +1 -0
- package/src/component/public/enterprise/secrets.ts +1 -0
- package/src/component/public/enterprise/webhooks.ts +1 -0
- package/src/component/public/factors/devices.ts +1 -0
- package/src/component/public/factors/passkeys.ts +1 -0
- package/src/component/public/factors/totp.ts +1 -0
- package/src/component/public/groups/core.ts +1 -1
- package/src/component/public/groups/invites.ts +7 -1
- package/src/component/public/groups/members.ts +1 -0
- package/src/component/public/identity/accounts.ts +1 -0
- package/src/component/public/identity/codes.ts +1 -0
- package/src/component/public/identity/sessions.ts +1 -0
- package/src/component/public/identity/tokens.ts +1 -0
- package/src/component/public/identity/users.ts +1 -0
- package/src/component/public/identity/verifiers.ts +1 -0
- package/src/component/public/security/keys.ts +1 -0
- package/src/component/public/security/limits.ts +1 -0
- package/src/providers/password.ts +89 -110
- package/src/server/auth.ts +240 -182
- package/src/server/context.ts +90 -0
- package/src/server/core.ts +195 -286
- package/src/server/crypto.ts +31 -29
- package/src/server/device.ts +65 -32
- package/src/server/enterprise/domain.ts +158 -170
- package/src/server/enterprise/http.ts +46 -39
- package/src/server/http.ts +289 -30
- package/src/server/identity.ts +5 -5
- package/src/server/index.ts +9 -3
- package/src/server/limits.ts +53 -80
- package/src/server/mounts.ts +56 -80
- package/src/server/mutations/account.ts +22 -36
- package/src/server/mutations/code.ts +6 -6
- package/src/server/mutations/invalidate.ts +1 -1
- package/src/server/mutations/oauth.ts +14 -8
- package/src/server/mutations/refresh.ts +5 -4
- package/src/server/mutations/register.ts +87 -132
- package/src/server/mutations/retrieve.ts +44 -44
- package/src/server/mutations/signature.ts +13 -6
- package/src/server/mutations/signout.ts +1 -1
- package/src/server/mutations/store.ts +16 -31
- package/src/server/mutations/verifier.ts +1 -1
- package/src/server/mutations/verify.ts +3 -5
- package/src/server/oauth.ts +60 -69
- package/src/server/passkey.ts +567 -517
- package/src/server/redirects.ts +10 -6
- package/src/server/refresh.ts +14 -18
- package/src/server/runtime.ts +340 -302
- package/src/server/signin.ts +44 -37
- package/src/server/ssr.ts +390 -407
- package/src/server/totp.ts +85 -35
- package/src/server/types.ts +19 -22
- package/src/server/users.ts +7 -6
- package/src/server/utils.ts +10 -12
- package/dist/component/server/authError.js +0 -34
- package/dist/component/server/authError.js.map +0 -1
- package/dist/component/server/errors.d.ts +0 -1
- package/dist/component/server/errors.js +0 -137
- package/dist/component/server/errors.js.map +0 -1
- package/dist/server/authError.d.ts +0 -46
- package/dist/server/authError.d.ts.map +0 -1
- package/dist/server/authError.js +0 -34
- package/dist/server/authError.js.map +0 -1
- package/dist/server/errors.d.ts +0 -177
- package/dist/server/errors.d.ts.map +0 -1
- package/dist/server/errors.js +0 -212
- package/dist/server/errors.js.map +0 -1
- package/src/server/authError.ts +0 -44
- package/src/server/errors.ts +0 -290
package/src/server/runtime.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { Fx } from "@robelest/fx";
|
|
2
|
+
import { Cv } from "@robelest/fx/convex";
|
|
1
3
|
import {
|
|
2
|
-
Auth,
|
|
3
4
|
GenericActionCtx,
|
|
4
5
|
GenericDataModel,
|
|
5
6
|
HttpRouter,
|
|
@@ -9,10 +10,10 @@ import {
|
|
|
9
10
|
import { v } from "convex/values";
|
|
10
11
|
import { serialize as serializeCookie } from "cookie";
|
|
11
12
|
|
|
12
|
-
import {
|
|
13
|
+
import { configDefaults, listAvailableProviders } from "./config";
|
|
13
14
|
import { redirectToParamCookie, useRedirectToParam } from "./cookies";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import { createCoreDomains } from "./core";
|
|
16
|
+
import { GetProviderOrThrowFunc } from "./crypto";
|
|
16
17
|
import {
|
|
17
18
|
getOidcConfig,
|
|
18
19
|
getPublicOidcConfig,
|
|
@@ -20,29 +21,30 @@ import {
|
|
|
20
21
|
upsertProtocolConfig,
|
|
21
22
|
withOidcSecretState,
|
|
22
23
|
} from "./enterprise/config";
|
|
23
|
-
import {
|
|
24
|
+
import { createEnterpriseDomain } from "./enterprise/domain";
|
|
25
|
+
import { addEnterpriseHttpRuntime } from "./enterprise/http";
|
|
26
|
+
import {
|
|
27
|
+
normalizeEnterprisePolicy,
|
|
28
|
+
patchEnterprisePolicy,
|
|
29
|
+
} from "./enterprise/policy";
|
|
24
30
|
import {
|
|
25
31
|
createServiceProviderMetadata,
|
|
26
32
|
getSamlServiceProviderOptions,
|
|
27
33
|
parseSamlIdpMetadata,
|
|
28
34
|
} from "./enterprise/saml";
|
|
29
|
-
import {
|
|
30
|
-
parseScimPath,
|
|
31
|
-
} from "./enterprise/scim";
|
|
35
|
+
import { parseScimPath } from "./enterprise/scim";
|
|
32
36
|
import {
|
|
33
37
|
enterpriseOidcProviderId,
|
|
34
38
|
getEnterpriseOidcUrls,
|
|
35
39
|
isEnterpriseSamlSourceActive,
|
|
36
40
|
normalizeDomain,
|
|
37
41
|
} from "./enterprise/shared";
|
|
38
|
-
import { Fx } from "@robelest/fx";
|
|
39
|
-
|
|
40
|
-
import { AuthError } from "./authError";
|
|
41
42
|
import {
|
|
42
43
|
addAuthRoutes,
|
|
43
44
|
addOpenIdRoutes,
|
|
44
45
|
convertErrorsToResponse,
|
|
45
46
|
createHttpAction,
|
|
47
|
+
createHttpContext,
|
|
46
48
|
createHttpRoute,
|
|
47
49
|
getCookies,
|
|
48
50
|
} from "./http";
|
|
@@ -58,8 +60,6 @@ import {
|
|
|
58
60
|
storeImpl,
|
|
59
61
|
} from "./mutations/index";
|
|
60
62
|
import { createOAuthAuthorizationURL, handleOAuthCallback } from "./oauth";
|
|
61
|
-
import { GetProviderOrThrowFunc } from "./crypto";
|
|
62
|
-
import { configDefaults, listAvailableProviders } from "./config";
|
|
63
63
|
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects";
|
|
64
64
|
import { signInImpl } from "./signin";
|
|
65
65
|
import type {
|
|
@@ -171,9 +171,11 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
171
171
|
`Provider \`${id}\` is not configured, ` +
|
|
172
172
|
`available providers are ${listAvailableProviders(config, allowExtraProviders)}.`;
|
|
173
173
|
logWithLevel(LOG_LEVELS.ERROR, detail);
|
|
174
|
-
throw
|
|
174
|
+
throw Cv.error({
|
|
175
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
176
|
+
message: detail,
|
|
175
177
|
provider: id,
|
|
176
|
-
})
|
|
178
|
+
});
|
|
177
179
|
}
|
|
178
180
|
return provider;
|
|
179
181
|
};
|
|
@@ -231,10 +233,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
231
233
|
},
|
|
232
234
|
);
|
|
233
235
|
if (!enterprise) {
|
|
234
|
-
throw
|
|
235
|
-
"INVALID_PARAMETERS",
|
|
236
|
-
enterpriseNotFoundError,
|
|
237
|
-
)
|
|
236
|
+
throw Cv.error({
|
|
237
|
+
code: "INVALID_PARAMETERS",
|
|
238
|
+
message: enterpriseNotFoundError,
|
|
239
|
+
});
|
|
238
240
|
}
|
|
239
241
|
return enterprise;
|
|
240
242
|
};
|
|
@@ -245,10 +247,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
245
247
|
) => {
|
|
246
248
|
const enterprise = await loadEnterpriseOrThrow(ctx, enterpriseId);
|
|
247
249
|
if (enterprise.status !== "active") {
|
|
248
|
-
throw
|
|
249
|
-
"INVALID_PARAMETERS",
|
|
250
|
-
"Enterprise connection is not active.",
|
|
251
|
-
)
|
|
250
|
+
throw Cv.error({
|
|
251
|
+
code: "INVALID_PARAMETERS",
|
|
252
|
+
message: "Enterprise connection is not active.",
|
|
253
|
+
});
|
|
252
254
|
}
|
|
253
255
|
return enterprise;
|
|
254
256
|
};
|
|
@@ -268,17 +270,17 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
268
270
|
enterprise,
|
|
269
271
|
};
|
|
270
272
|
if (!isEnterpriseSamlSourceActive(loaded)) {
|
|
271
|
-
throw
|
|
272
|
-
"INVALID_PARAMETERS",
|
|
273
|
-
"Enterprise connection is not active.",
|
|
274
|
-
)
|
|
273
|
+
throw Cv.error({
|
|
274
|
+
code: "INVALID_PARAMETERS",
|
|
275
|
+
message: "Enterprise connection is not active.",
|
|
276
|
+
});
|
|
275
277
|
}
|
|
276
278
|
const saml = getSamlConfig(loaded.config);
|
|
277
279
|
if (!saml.idp?.metadataXml) {
|
|
278
|
-
throw
|
|
279
|
-
"PROVIDER_NOT_CONFIGURED",
|
|
280
|
-
"SAML is not configured for this enterprise.",
|
|
281
|
-
)
|
|
280
|
+
throw Cv.error({
|
|
281
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
282
|
+
message: "SAML is not configured for this enterprise.",
|
|
283
|
+
});
|
|
282
284
|
}
|
|
283
285
|
return { loaded, enterprise, saml };
|
|
284
286
|
};
|
|
@@ -290,10 +292,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
290
292
|
const enterprise = await loadActiveEnterpriseOrThrow(ctx, enterpriseId);
|
|
291
293
|
const oidc = await getEnterpriseOidcConfigWithSecret(ctx, enterprise);
|
|
292
294
|
if (oidc.enabled !== true) {
|
|
293
|
-
throw
|
|
294
|
-
"PROVIDER_NOT_CONFIGURED",
|
|
295
|
-
"OIDC is not configured for this enterprise.",
|
|
296
|
-
)
|
|
295
|
+
throw Cv.error({
|
|
296
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
297
|
+
message: "OIDC is not configured for this enterprise.",
|
|
298
|
+
});
|
|
297
299
|
}
|
|
298
300
|
return { enterprise, oidc };
|
|
299
301
|
};
|
|
@@ -407,7 +409,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
407
409
|
) => {
|
|
408
410
|
const authHeader = request.headers.get("Authorization");
|
|
409
411
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
410
|
-
throw
|
|
412
|
+
throw Cv.error({
|
|
413
|
+
code: "MISSING_BEARER_TOKEN",
|
|
414
|
+
message: "Missing or malformed Authorization: Bearer header.",
|
|
415
|
+
});
|
|
411
416
|
}
|
|
412
417
|
const token = authHeader.slice(7);
|
|
413
418
|
const scimConfig = await ctx.runQuery(
|
|
@@ -415,17 +420,17 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
415
420
|
{ tokenHash: await sha256(token) },
|
|
416
421
|
);
|
|
417
422
|
if (!scimConfig || scimConfig.status !== "active") {
|
|
418
|
-
throw
|
|
419
|
-
"INVALID_API_KEY",
|
|
420
|
-
"Invalid SCIM token.",
|
|
421
|
-
)
|
|
423
|
+
throw Cv.error({
|
|
424
|
+
code: "INVALID_API_KEY",
|
|
425
|
+
message: "Invalid SCIM token.",
|
|
426
|
+
});
|
|
422
427
|
}
|
|
423
428
|
const parsedPath = parseScimPath(new URL(request.url).pathname);
|
|
424
429
|
if (parsedPath.enterpriseId !== scimConfig.enterpriseId) {
|
|
425
|
-
throw
|
|
426
|
-
"INVALID_API_KEY",
|
|
427
|
-
"SCIM token/tenant mismatch.",
|
|
428
|
-
)
|
|
430
|
+
throw Cv.error({
|
|
431
|
+
code: "INVALID_API_KEY",
|
|
432
|
+
message: "SCIM token/tenant mismatch.",
|
|
433
|
+
});
|
|
429
434
|
}
|
|
430
435
|
const enterprise = await ctx.runQuery(
|
|
431
436
|
config.component.public.enterpriseGet,
|
|
@@ -434,10 +439,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
434
439
|
},
|
|
435
440
|
);
|
|
436
441
|
if (enterprise === null) {
|
|
437
|
-
throw
|
|
438
|
-
"INVALID_PARAMETERS",
|
|
439
|
-
"Enterprise not found.",
|
|
440
|
-
)
|
|
442
|
+
throw Cv.error({
|
|
443
|
+
code: "INVALID_PARAMETERS",
|
|
444
|
+
message: "Enterprise not found.",
|
|
445
|
+
});
|
|
441
446
|
}
|
|
442
447
|
return { scimConfig, enterprise, parsedPath };
|
|
443
448
|
};
|
|
@@ -488,272 +493,305 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
488
493
|
getPolicyFromEnterprise,
|
|
489
494
|
patchEnterprisePolicy,
|
|
490
495
|
}),
|
|
491
|
-
|
|
492
|
-
// dense mix of OAuth, SAML, SCIM, cookie, and response helpers.
|
|
493
|
-
http: {
|
|
494
|
-
/**
|
|
495
|
-
* Register core HTTP routes for JWT verification and OAuth sign-in.
|
|
496
|
-
*
|
|
497
|
-
* ```ts
|
|
498
|
-
* import { httpRouter } from "convex/server";
|
|
499
|
-
* import { auth } from "./auth";
|
|
500
|
-
*
|
|
501
|
-
* const http = httpRouter();
|
|
502
|
-
*
|
|
503
|
-
* auth.http.add(http);
|
|
504
|
-
*
|
|
505
|
-
* export default http;
|
|
506
|
-
* ```
|
|
507
|
-
*
|
|
508
|
-
* The following routes are handled always:
|
|
509
|
-
*
|
|
510
|
-
* - `/.well-known/openid-configuration`
|
|
511
|
-
* - `/.well-known/jwks.json`
|
|
512
|
-
*
|
|
513
|
-
* The following routes are handled if OAuth is configured:
|
|
514
|
-
*
|
|
515
|
-
* - `/api/auth/signin/*`
|
|
516
|
-
* - `/api/auth/callback/*`
|
|
517
|
-
*
|
|
518
|
-
* @param http your HTTP router
|
|
519
|
-
*/
|
|
520
|
-
add: (http: HttpRouter) => {
|
|
521
|
-
addOpenIdRoutes(http, {
|
|
522
|
-
getIssuer: () => requireEnv("CONVEX_SITE_URL"),
|
|
523
|
-
getJwks: () => requireEnv("JWKS"),
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
addEnterpriseHttpRuntime({
|
|
527
|
-
http,
|
|
528
|
-
hasSSO,
|
|
529
|
-
auth,
|
|
530
|
-
config,
|
|
531
|
-
routeBase: ENTERPRISE_CONTROL_ROUTE_BASE,
|
|
532
|
-
requireEnv,
|
|
533
|
-
loadActiveEnterpriseSamlOrThrow,
|
|
534
|
-
loadEnterpriseOidcOrThrow,
|
|
535
|
-
getEnterpriseScimContext,
|
|
536
|
-
getPolicyFromEnterprise,
|
|
537
|
-
normalizeEnterprisePolicy,
|
|
538
|
-
recordEnterpriseAuditEvent,
|
|
539
|
-
emitEnterpriseWebhookDeliveries,
|
|
540
|
-
generateRandomString,
|
|
541
|
-
inviteTokenAlphabet: INVITE_TOKEN_ALPHABET,
|
|
542
|
-
callUserOAuth,
|
|
543
|
-
callVerifierSignature,
|
|
544
|
-
});
|
|
496
|
+
};
|
|
545
497
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
498
|
+
// HTTP wiring stays local to the factory because it still depends on a
|
|
499
|
+
// dense mix of OAuth, SAML, SCIM, cookie, and response helpers.
|
|
500
|
+
auth.http = {
|
|
501
|
+
/**
|
|
502
|
+
* Register core HTTP routes for JWT verification and OAuth sign-in.
|
|
503
|
+
*
|
|
504
|
+
* ```ts
|
|
505
|
+
* import { httpRouter } from "convex/server";
|
|
506
|
+
* import { auth } from "./auth";
|
|
507
|
+
*
|
|
508
|
+
* const http = httpRouter();
|
|
509
|
+
*
|
|
510
|
+
* auth.http.add(http);
|
|
511
|
+
*
|
|
512
|
+
* export default http;
|
|
513
|
+
* ```
|
|
514
|
+
*
|
|
515
|
+
* The following routes are handled always:
|
|
516
|
+
*
|
|
517
|
+
* - `/.well-known/openid-configuration`
|
|
518
|
+
* - `/.well-known/jwks.json`
|
|
519
|
+
*
|
|
520
|
+
* The following routes are handled if OAuth is configured:
|
|
521
|
+
*
|
|
522
|
+
* - `/api/auth/signin/*`
|
|
523
|
+
* - `/api/auth/callback/*`
|
|
524
|
+
*
|
|
525
|
+
* @param http your HTTP router
|
|
526
|
+
*/
|
|
527
|
+
add: (http: HttpRouter) => {
|
|
528
|
+
addOpenIdRoutes(http, {
|
|
529
|
+
getIssuer: () => requireEnv("CONVEX_SITE_URL"),
|
|
530
|
+
getJwks: () => requireEnv("JWKS"),
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
addEnterpriseHttpRuntime({
|
|
534
|
+
http,
|
|
535
|
+
hasSSO,
|
|
536
|
+
auth,
|
|
537
|
+
config,
|
|
538
|
+
routeBase: ENTERPRISE_CONTROL_ROUTE_BASE,
|
|
539
|
+
requireEnv,
|
|
540
|
+
loadActiveEnterpriseSamlOrThrow,
|
|
541
|
+
loadEnterpriseOidcOrThrow,
|
|
542
|
+
getEnterpriseScimContext,
|
|
543
|
+
getPolicyFromEnterprise,
|
|
544
|
+
normalizeEnterprisePolicy,
|
|
545
|
+
recordEnterpriseAuditEvent,
|
|
546
|
+
emitEnterpriseWebhookDeliveries,
|
|
547
|
+
generateRandomString,
|
|
548
|
+
inviteTokenAlphabet: INVITE_TOKEN_ALPHABET,
|
|
549
|
+
callUserOAuth,
|
|
550
|
+
callVerifierSignature,
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
if (hasOAuth) {
|
|
554
|
+
addAuthRoutes(http, {
|
|
555
|
+
handleSignIn: convertErrorsToResponse(400, async (ctx, request) => {
|
|
556
|
+
const url = new URL(request.url);
|
|
557
|
+
const pathParts = url.pathname.split("/");
|
|
558
|
+
const providerId = pathParts[pathParts.length - 1]!;
|
|
559
|
+
if (providerId === null) {
|
|
560
|
+
throw Cv.error({
|
|
561
|
+
code: "OAUTH_MISSING_PROVIDER",
|
|
562
|
+
message: "Missing OAuth provider ID.",
|
|
572
563
|
});
|
|
564
|
+
}
|
|
565
|
+
const verifier = url.searchParams.get("code");
|
|
566
|
+
if (verifier === null) {
|
|
567
|
+
throw Cv.error({
|
|
568
|
+
code: "OAUTH_MISSING_VERIFIER",
|
|
569
|
+
message: "Missing sign-in verifier.",
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
const provider = getProviderOrThrow(providerId);
|
|
573
573
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const headers = new Headers({ Location: redirect });
|
|
580
|
-
for (const { name, value, options } of cookies) {
|
|
581
|
-
headers.append(
|
|
582
|
-
"Set-Cookie",
|
|
583
|
-
serializeCookie(name, value, options as any),
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
return new Response(null, { status: 302, headers });
|
|
588
|
-
}),
|
|
589
|
-
handleCallback: async (ctx, request) => {
|
|
590
|
-
const url = new URL(request.url);
|
|
591
|
-
const providerId = new URL(request.url).pathname
|
|
592
|
-
.split("/")
|
|
593
|
-
.at(-1);
|
|
594
|
-
if (!providerId) {
|
|
595
|
-
throw new AuthError("OAUTH_MISSING_PROVIDER").toConvexError();
|
|
596
|
-
}
|
|
597
|
-
logWithLevel(
|
|
598
|
-
LOG_LEVELS.DEBUG,
|
|
599
|
-
"Handling OAuth callback for provider:",
|
|
574
|
+
const oauthConfig = provider as OAuthMaterializedConfig;
|
|
575
|
+
const { redirect, cookies, signature } =
|
|
576
|
+
await createOAuthAuthorizationURL(
|
|
600
577
|
providerId,
|
|
578
|
+
oauthConfig.provider,
|
|
579
|
+
oauthConfig,
|
|
601
580
|
);
|
|
602
|
-
const provider = getProviderOrThrow(providerId);
|
|
603
|
-
|
|
604
|
-
const cookies = getCookies(request);
|
|
605
581
|
|
|
606
|
-
|
|
582
|
+
await callVerifierSignature(ctx, {
|
|
583
|
+
verifier,
|
|
584
|
+
signature,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
const redirectTo = url.searchParams.get("redirectTo");
|
|
588
|
+
if (redirectTo !== null) {
|
|
589
|
+
cookies.push(redirectToParamCookie(providerId, redirectTo));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const headers = new Headers({ Location: redirect });
|
|
593
|
+
for (const { name, value, options } of cookies) {
|
|
594
|
+
headers.append(
|
|
595
|
+
"Set-Cookie",
|
|
596
|
+
serializeCookie(name, value, options as any),
|
|
597
|
+
);
|
|
598
|
+
}
|
|
607
599
|
|
|
608
|
-
|
|
609
|
-
|
|
600
|
+
return new Response(null, { status: 302, headers });
|
|
601
|
+
}),
|
|
602
|
+
handleCallback: async (ctx, request) => {
|
|
603
|
+
const url = new URL(request.url);
|
|
604
|
+
const callbackPathParts = new URL(request.url).pathname.split("/");
|
|
605
|
+
const providerId = callbackPathParts[callbackPathParts.length - 1];
|
|
606
|
+
if (!providerId) {
|
|
607
|
+
throw Cv.error({
|
|
608
|
+
code: "OAUTH_MISSING_PROVIDER",
|
|
609
|
+
message: "Missing OAuth provider ID.",
|
|
610
610
|
});
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
611
|
+
}
|
|
612
|
+
logWithLevel(
|
|
613
|
+
LOG_LEVELS.DEBUG,
|
|
614
|
+
"Handling OAuth callback for provider:",
|
|
615
|
+
providerId,
|
|
616
|
+
);
|
|
617
|
+
const provider = getProviderOrThrow(providerId);
|
|
618
|
+
|
|
619
|
+
const cookies = getCookies(request);
|
|
620
|
+
|
|
621
|
+
const maybeRedirectTo = useRedirectToParam(provider.id, cookies);
|
|
622
|
+
|
|
623
|
+
const destinationUrl = await redirectAbsoluteUrl(config, {
|
|
624
|
+
redirectTo: maybeRedirectTo?.redirectTo,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const params = url.searchParams;
|
|
628
|
+
|
|
629
|
+
if (
|
|
630
|
+
request.headers.get("Content-Type") ===
|
|
631
|
+
"application/x-www-form-urlencoded"
|
|
632
|
+
) {
|
|
633
|
+
const formData = await request.formData();
|
|
634
|
+
formData.forEach((value, key) => {
|
|
635
|
+
if (typeof value === "string") {
|
|
636
|
+
params.append(key, value);
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return Fx.run(
|
|
642
|
+
Fx.from({
|
|
643
|
+
ok: async () => {
|
|
644
|
+
const oauthConfig = provider as OAuthMaterializedConfig;
|
|
645
|
+
const result = await Fx.run(
|
|
646
|
+
handleOAuthCallback(
|
|
647
|
+
providerId,
|
|
648
|
+
oauthConfig.provider,
|
|
649
|
+
oauthConfig,
|
|
650
|
+
Object.fromEntries(params.entries()),
|
|
651
|
+
cookies,
|
|
652
|
+
),
|
|
653
|
+
);
|
|
654
|
+
const oauthCookies = result.cookies;
|
|
655
|
+
const { id: profileId, ...profileData } = result.profile;
|
|
656
|
+
const { signature } = result;
|
|
657
|
+
|
|
658
|
+
const verificationCode = await callUserOAuth(ctx, {
|
|
659
|
+
provider: providerId,
|
|
660
|
+
providerAccountId: profileId,
|
|
661
|
+
profile: profileData,
|
|
662
|
+
signature,
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
const redirUrl = setURLSearchParam(
|
|
666
|
+
destinationUrl,
|
|
667
|
+
"code",
|
|
668
|
+
verificationCode,
|
|
669
|
+
);
|
|
670
|
+
const redirHeaders = new Headers({ Location: redirUrl });
|
|
671
|
+
redirHeaders.set("Cache-Control", "must-revalidate");
|
|
672
|
+
for (const { name, value, options } of [
|
|
673
|
+
...oauthCookies,
|
|
674
|
+
...(maybeRedirectTo !== null
|
|
675
|
+
? [maybeRedirectTo.updatedCookie]
|
|
676
|
+
: []),
|
|
677
|
+
] as any) {
|
|
678
|
+
redirHeaders.append(
|
|
679
|
+
"Set-Cookie",
|
|
680
|
+
serializeCookie(name, value, options),
|
|
638
681
|
);
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
682
|
+
}
|
|
683
|
+
return new Response(null, {
|
|
684
|
+
status: 302,
|
|
685
|
+
headers: redirHeaders,
|
|
686
|
+
});
|
|
687
|
+
},
|
|
688
|
+
err: (error) => error,
|
|
689
|
+
}).pipe(
|
|
690
|
+
Fx.recover((error) => {
|
|
691
|
+
logError(error);
|
|
692
|
+
const respHeaders = new Headers({
|
|
693
|
+
Location: destinationUrl,
|
|
694
|
+
});
|
|
695
|
+
for (const { name, value, options } of maybeRedirectTo !== null
|
|
696
|
+
? [maybeRedirectTo.updatedCookie]
|
|
697
|
+
: []) {
|
|
698
|
+
respHeaders.append(
|
|
699
|
+
"Set-Cookie",
|
|
700
|
+
serializeCookie(name, value, options),
|
|
654
701
|
);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
...oauthCookies,
|
|
659
|
-
...(maybeRedirectTo !== null
|
|
660
|
-
? [maybeRedirectTo.updatedCookie]
|
|
661
|
-
: []),
|
|
662
|
-
] as any) {
|
|
663
|
-
redirHeaders.append(
|
|
664
|
-
"Set-Cookie",
|
|
665
|
-
serializeCookie(name, value, options),
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
return new Response(null, {
|
|
702
|
+
}
|
|
703
|
+
return Fx.succeed(
|
|
704
|
+
new Response(null, {
|
|
669
705
|
status: 302,
|
|
670
|
-
headers:
|
|
671
|
-
})
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
});
|
|
680
|
-
for (const { name, value, options } of maybeRedirectTo !==
|
|
681
|
-
null
|
|
682
|
-
? [maybeRedirectTo.updatedCookie]
|
|
683
|
-
: []) {
|
|
684
|
-
respHeaders.append(
|
|
685
|
-
"Set-Cookie",
|
|
686
|
-
serializeCookie(name, value, options),
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
return Fx.succeed(
|
|
690
|
-
new Response(null, {
|
|
691
|
-
status: 302,
|
|
692
|
-
headers: respHeaders,
|
|
693
|
-
}),
|
|
694
|
-
);
|
|
695
|
-
}),
|
|
696
|
-
),
|
|
697
|
-
);
|
|
698
|
-
},
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
},
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* Wrap an HTTP action handler with Bearer token authentication.
|
|
705
|
-
*
|
|
706
|
-
* Extracts the `Authorization: Bearer <key>` header, verifies the
|
|
707
|
-
* API key via `auth.key.verify()`, and injects `ctx.key` with the
|
|
708
|
-
* verified key info. Returns structured JSON error responses for
|
|
709
|
-
* missing/invalid/revoked/expired/rate-limited keys.
|
|
710
|
-
*
|
|
711
|
-
* If the handler returns a plain object, it is auto-wrapped in a
|
|
712
|
-
* `200 JSON` response. If it returns a `Response`, CORS headers
|
|
713
|
-
* are merged and the response is passed through.
|
|
714
|
-
*
|
|
715
|
-
* ```ts
|
|
716
|
-
* const handler = auth.http.action(async (ctx, request) => {
|
|
717
|
-
* const data = await ctx.runQuery(api.data.get, { userId: ctx.key.userId });
|
|
718
|
-
* return { data };
|
|
719
|
-
* });
|
|
720
|
-
* http.route({ path: "/api/data", method: "GET", handler });
|
|
721
|
-
* ```
|
|
722
|
-
*
|
|
723
|
-
* @param handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
724
|
-
* @param options.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
725
|
-
* @param options.cors - CORS config; defaults to permissive (`*`).
|
|
726
|
-
*/
|
|
727
|
-
action: createHttpAction(auth),
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Register a Bearer-authenticated route **and** its OPTIONS preflight
|
|
731
|
-
* in a single call.
|
|
732
|
-
*
|
|
733
|
-
* ```ts
|
|
734
|
-
* auth.http.route(http, {
|
|
735
|
-
* path: "/api/messages",
|
|
736
|
-
* method: "POST",
|
|
737
|
-
* handler: async (ctx, request) => {
|
|
738
|
-
* const { body } = await request.json();
|
|
739
|
-
* await ctx.runMutation(internal.messages.sendAsUser, {
|
|
740
|
-
* userId: ctx.key.userId,
|
|
741
|
-
* body,
|
|
742
|
-
* });
|
|
743
|
-
* return { success: true };
|
|
744
|
-
* },
|
|
745
|
-
* });
|
|
746
|
-
* ```
|
|
747
|
-
*
|
|
748
|
-
* @param http - The Convex HTTP router.
|
|
749
|
-
* @param routeConfig.path - The URL path to match.
|
|
750
|
-
* @param routeConfig.method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
751
|
-
* @param routeConfig.handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
752
|
-
* @param routeConfig.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
753
|
-
* @param routeConfig.cors - CORS config; defaults to permissive (`*`).
|
|
754
|
-
*/
|
|
755
|
-
route: createHttpRoute(createHttpAction(auth)),
|
|
706
|
+
headers: respHeaders,
|
|
707
|
+
}),
|
|
708
|
+
);
|
|
709
|
+
}),
|
|
710
|
+
),
|
|
711
|
+
);
|
|
712
|
+
},
|
|
713
|
+
});
|
|
714
|
+
}
|
|
756
715
|
},
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Resolve mixed HTTP auth for a raw `httpAction`.
|
|
719
|
+
*
|
|
720
|
+
* Checks session auth first, then falls back to `Authorization: Bearer sk_*`
|
|
721
|
+
* API keys. This is the low-level helper for endpoints that intentionally
|
|
722
|
+
* accept either browser sessions or API keys.
|
|
723
|
+
* Pass `{ optional: true }` to get a null-shaped auth object instead of a
|
|
724
|
+
* `NOT_SIGNED_IN` error.
|
|
725
|
+
*
|
|
726
|
+
* ```ts
|
|
727
|
+
* http.route({
|
|
728
|
+
* path: "/api/data",
|
|
729
|
+
* method: "GET",
|
|
730
|
+
* handler: httpAction(async (ctx, request) => {
|
|
731
|
+
* const authContext = await auth.http.context(ctx, request);
|
|
732
|
+
* return Response.json({
|
|
733
|
+
* userId: authContext.userId,
|
|
734
|
+
* source: authContext.source,
|
|
735
|
+
* });
|
|
736
|
+
* }),
|
|
737
|
+
* });
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
740
|
+
context: createHttpContext(auth),
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Wrap an HTTP action handler with Bearer token authentication.
|
|
744
|
+
*
|
|
745
|
+
* Extracts the `Authorization: Bearer <key>` header, verifies the
|
|
746
|
+
* API key via `auth.key.verify()`, and injects `ctx.key` with the
|
|
747
|
+
* verified key info. Returns structured JSON error responses for
|
|
748
|
+
* missing/invalid/revoked/expired/rate-limited keys.
|
|
749
|
+
*
|
|
750
|
+
* If the handler returns a plain object, it is auto-wrapped in a
|
|
751
|
+
* `200 JSON` response. If it returns a `Response`, CORS headers
|
|
752
|
+
* are merged and the response is passed through.
|
|
753
|
+
*
|
|
754
|
+
* ```ts
|
|
755
|
+
* const handler = auth.http.action(async (ctx, request) => {
|
|
756
|
+
* const data = await ctx.runQuery(api.data.get, { userId: ctx.key.userId });
|
|
757
|
+
* return { data };
|
|
758
|
+
* });
|
|
759
|
+
* http.route({ path: "/api/data", method: "GET", handler });
|
|
760
|
+
* ```
|
|
761
|
+
*
|
|
762
|
+
* @param handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
763
|
+
* @param options.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
764
|
+
* @param options.cors - CORS config; defaults to permissive (`*`).
|
|
765
|
+
*/
|
|
766
|
+
action: createHttpAction(auth),
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Register a Bearer-authenticated route **and** its OPTIONS preflight
|
|
770
|
+
* in a single call.
|
|
771
|
+
*
|
|
772
|
+
* ```ts
|
|
773
|
+
* auth.http.route(http, {
|
|
774
|
+
* path: "/api/messages",
|
|
775
|
+
* method: "POST",
|
|
776
|
+
* handler: async (ctx, request) => {
|
|
777
|
+
* const { body } = await request.json();
|
|
778
|
+
* await ctx.runMutation(internal.messages.sendAsUser, {
|
|
779
|
+
* userId: ctx.key.userId,
|
|
780
|
+
* body,
|
|
781
|
+
* });
|
|
782
|
+
* return { success: true };
|
|
783
|
+
* },
|
|
784
|
+
* });
|
|
785
|
+
* ```
|
|
786
|
+
*
|
|
787
|
+
* @param http - The Convex HTTP router.
|
|
788
|
+
* @param routeConfig.path - The URL path to match.
|
|
789
|
+
* @param routeConfig.method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
790
|
+
* @param routeConfig.handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
791
|
+
* @param routeConfig.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
792
|
+
* @param routeConfig.cors - CORS config; defaults to permissive (`*`).
|
|
793
|
+
*/
|
|
794
|
+
route: createHttpRoute(createHttpAction(auth)),
|
|
757
795
|
};
|
|
758
796
|
|
|
759
797
|
const enrichCtx = <DataModel extends GenericDataModel>(
|