@lupinum/better-convex-nuxt 0.8.0-beta.40
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/LICENSE +21 -0
- package/LICENSES/Apache-2.0.txt +201 -0
- package/README.md +151 -0
- package/THIRD_PARTY_NOTICES.md +38 -0
- package/dist/module.d.mts +55 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +309 -0
- package/dist/runtime/auth/auth-identity.d.ts +31 -0
- package/dist/runtime/auth/auth-identity.js +21 -0
- package/dist/runtime/auth/better-auth-browser-adapter.d.ts +33 -0
- package/dist/runtime/auth/better-auth-browser-adapter.js +215 -0
- package/dist/runtime/auth/client-engine-types.d.ts +4 -0
- package/dist/runtime/auth/client-engine-types.js +0 -0
- package/dist/runtime/auth/integrated-client.d.ts +25 -0
- package/dist/runtime/auth/integrated-client.js +132 -0
- package/dist/runtime/auth/operation-tracker.d.ts +7 -0
- package/dist/runtime/auth/operation-tracker.js +14 -0
- package/dist/runtime/auth/session-synchronization.d.ts +23 -0
- package/dist/runtime/auth/session-synchronization.js +156 -0
- package/dist/runtime/auth/token-fetcher.d.ts +66 -0
- package/dist/runtime/auth/token-fetcher.js +131 -0
- package/dist/runtime/auth/validate-auth-client-definition.d.ts +12 -0
- package/dist/runtime/auth/validate-auth-client-definition.js +47 -0
- package/dist/runtime/auth-client/convex-client-plugin.d.ts +31 -0
- package/dist/runtime/auth-client/convex-client-plugin.js +14 -0
- package/dist/runtime/auth-client/definition-types.d.ts +6 -0
- package/dist/runtime/auth-client/definition-types.js +0 -0
- package/dist/runtime/auth-client/empty-definition.d.ts +2 -0
- package/dist/runtime/auth-client/empty-definition.js +2 -0
- package/dist/runtime/auth-client/index.d.ts +71 -0
- package/dist/runtime/auth-client/index.js +3 -0
- package/dist/runtime/cli/auth-schema.d.ts +2 -0
- package/dist/runtime/cli/auth-schema.js +169 -0
- package/dist/runtime/cli/convex.d.ts +11 -0
- package/dist/runtime/cli/convex.js +274 -0
- package/dist/runtime/composables/useConvex.d.ts +4 -0
- package/dist/runtime/composables/useConvex.js +9 -0
- package/dist/runtime/composables/useConvexAction.d.ts +4 -0
- package/dist/runtime/composables/useConvexAction.js +18 -0
- package/dist/runtime/composables/useConvexAttachment.d.ts +9 -0
- package/dist/runtime/composables/useConvexAttachment.js +14 -0
- package/dist/runtime/composables/useConvexAuth.d.ts +10 -0
- package/dist/runtime/composables/useConvexAuth.js +37 -0
- package/dist/runtime/composables/useConvexConfig.d.ts +12 -0
- package/dist/runtime/composables/useConvexConfig.js +7 -0
- package/dist/runtime/composables/useConvexConnectionState.d.ts +33 -0
- package/dist/runtime/composables/useConvexConnectionState.js +24 -0
- package/dist/runtime/composables/useConvexFileUpload.d.ts +169 -0
- package/dist/runtime/composables/useConvexFileUpload.js +198 -0
- package/dist/runtime/composables/useConvexMutation.d.ts +13 -0
- package/dist/runtime/composables/useConvexMutation.js +24 -0
- package/dist/runtime/composables/useConvexPaginatedQuery.d.ts +13 -0
- package/dist/runtime/composables/useConvexPaginatedQuery.js +256 -0
- package/dist/runtime/composables/useConvexQuery.d.ts +19 -0
- package/dist/runtime/composables/useConvexQuery.js +308 -0
- package/dist/runtime/convex-auth/adapter/create-adapter.d.ts +33 -0
- package/dist/runtime/convex-auth/adapter/create-adapter.js +308 -0
- package/dist/runtime/convex-auth/adapter/define-functions.d.ts +160 -0
- package/dist/runtime/convex-auth/adapter/define-functions.js +497 -0
- package/dist/runtime/convex-auth/adapter/generate-schema.d.ts +16 -0
- package/dist/runtime/convex-auth/adapter/generate-schema.js +332 -0
- package/dist/runtime/convex-auth/adapter/metadata.d.ts +44 -0
- package/dist/runtime/convex-auth/adapter/metadata.js +91 -0
- package/dist/runtime/convex-auth/adapter/query.d.ts +32 -0
- package/dist/runtime/convex-auth/adapter/query.js +275 -0
- package/dist/runtime/convex-auth/adapter/relationships.d.ts +20 -0
- package/dist/runtime/convex-auth/adapter/relationships.js +128 -0
- package/dist/runtime/convex-auth/component/_generated/api.js +5 -0
- package/dist/runtime/convex-auth/component/_generated/component.d.ts +116 -0
- package/dist/runtime/convex-auth/component/_generated/component.js +0 -0
- package/dist/runtime/convex-auth/component/_generated/dataModel.js +0 -0
- package/dist/runtime/convex-auth/component/_generated/server.js +16 -0
- package/dist/runtime/convex-auth/component/adapter.d.ts +142 -0
- package/dist/runtime/convex-auth/component/adapter.js +16 -0
- package/dist/runtime/convex-auth/component/convex.config.d.ts +2 -0
- package/dist/runtime/convex-auth/component/convex.config.js +3 -0
- package/dist/runtime/convex-auth/component/schema.d.ts +799 -0
- package/dist/runtime/convex-auth/component/schema.js +180 -0
- package/dist/runtime/convex-auth/component/schemaMetadata.d.ts +2094 -0
- package/dist/runtime/convex-auth/component/schemaMetadata.js +2161 -0
- package/dist/runtime/convex-auth/context.d.ts +5 -0
- package/dist/runtime/convex-auth/context.js +8 -0
- package/dist/runtime/convex-auth/create-auth-component.d.ts +41 -0
- package/dist/runtime/convex-auth/create-auth-component.js +157 -0
- package/dist/runtime/convex-auth/index.d.ts +11 -0
- package/dist/runtime/convex-auth/index.js +7 -0
- package/dist/runtime/convex-auth/internal-session.d.ts +2 -0
- package/dist/runtime/convex-auth/internal-session.js +1 -0
- package/dist/runtime/convex-auth/jwks-rotation.d.ts +58 -0
- package/dist/runtime/convex-auth/jwks-rotation.js +163 -0
- package/dist/runtime/convex-auth/oauth-live-access.d.ts +17 -0
- package/dist/runtime/convex-auth/oauth-live-access.js +78 -0
- package/dist/runtime/convex-auth/oauth-provider-compat.d.ts +10 -0
- package/dist/runtime/convex-auth/oauth-provider-compat.js +170 -0
- package/dist/runtime/convex-auth/oauth-resource.d.ts +39 -0
- package/dist/runtime/convex-auth/oauth-resource.js +118 -0
- package/dist/runtime/convex-auth/oauth-security.d.ts +82 -0
- package/dist/runtime/convex-auth/oauth-security.js +454 -0
- package/dist/runtime/convex-auth/origin.d.ts +4 -0
- package/dist/runtime/convex-auth/origin.js +9 -0
- package/dist/runtime/convex-auth/plugin.d.ts +18 -0
- package/dist/runtime/convex-auth/plugin.js +605 -0
- package/dist/runtime/convex-auth/provider.d.ts +2 -0
- package/dist/runtime/convex-auth/provider.js +11 -0
- package/dist/runtime/convex-auth/test.d.ts +410 -0
- package/dist/runtime/convex-auth/test.js +9 -0
- package/dist/runtime/convex-auth/types.d.ts +18 -0
- package/dist/runtime/convex-auth/types.js +0 -0
- package/dist/runtime/convex-auth/user-projection.d.ts +106 -0
- package/dist/runtime/convex-auth/user-projection.js +128 -0
- package/dist/runtime/devtools/auth-proxy-registry.d.ts +4 -0
- package/dist/runtime/devtools/auth-proxy-registry.js +47 -0
- package/dist/runtime/devtools/bridge-setup.d.ts +21 -0
- package/dist/runtime/devtools/bridge-setup.js +165 -0
- package/dist/runtime/devtools/path-utils.d.ts +2 -0
- package/dist/runtime/devtools/path-utils.js +12 -0
- package/dist/runtime/devtools/server.d.ts +2 -0
- package/dist/runtime/devtools/server.js +108 -0
- package/dist/runtime/devtools/setup-client.d.ts +16 -0
- package/dist/runtime/devtools/setup-client.js +43 -0
- package/dist/runtime/devtools/sink.d.ts +18 -0
- package/dist/runtime/devtools/sink.js +110 -0
- package/dist/runtime/devtools/transport.d.ts +16 -0
- package/dist/runtime/devtools/transport.js +149 -0
- package/dist/runtime/devtools/types.d.ts +167 -0
- package/dist/runtime/devtools/types.js +1 -0
- package/dist/runtime/devtools/ui/dist/200.html +1 -0
- package/dist/runtime/devtools/ui/dist/404.html +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/C4MASuCh.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DJHkqtZq.js +3 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DWwrCltl.js +2 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DZ_UiYoH.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/_m9ujb-i.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/builds/latest.json +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/builds/meta/better-convex-nuxt-devtools.json +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/entry.DpFDKYZ5.css +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/error-404.DYUJo5xt.css +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/error-500.DQdoJJXg.css +1 -0
- package/dist/runtime/devtools/ui/dist/convex-logo.svg +5 -0
- package/dist/runtime/devtools/ui/dist/index.html +1 -0
- package/dist/runtime/errors/index.d.ts +7 -0
- package/dist/runtime/errors/index.js +5 -0
- package/dist/runtime/middleware/convex-auth.global.d.ts +2 -0
- package/dist/runtime/middleware/convex-auth.global.js +31 -0
- package/dist/runtime/plugin.auth.client.d.ts +3 -0
- package/dist/runtime/plugin.auth.client.js +217 -0
- package/dist/runtime/plugin.client.d.ts +3 -0
- package/dist/runtime/plugin.client.js +43 -0
- package/dist/runtime/plugin.server.d.ts +12 -0
- package/dist/runtime/plugin.server.js +98 -0
- package/dist/runtime/plugins/convex-call-error-payload.d.ts +14 -0
- package/dist/runtime/plugins/convex-call-error-payload.js +12 -0
- package/dist/runtime/runtime-context.d.ts +26 -0
- package/dist/runtime/runtime-context.js +48 -0
- package/dist/runtime/server/api/auth/[...].d.ts +10 -0
- package/dist/runtime/server/api/auth/[...].js +509 -0
- package/dist/runtime/server/api/auth/authorization-server-metadata.d.ts +2 -0
- package/dist/runtime/server/api/auth/authorization-server-metadata.js +6 -0
- package/dist/runtime/server/api/auth/body-size.d.ts +15 -0
- package/dist/runtime/server/api/auth/body-size.js +179 -0
- package/dist/runtime/server/api/auth/headers.d.ts +6 -0
- package/dist/runtime/server/api/auth/headers.js +98 -0
- package/dist/runtime/server/api/auth/jwks.d.ts +2 -0
- package/dist/runtime/server/api/auth/jwks.js +6 -0
- package/dist/runtime/server/api/auth/security.d.ts +14 -0
- package/dist/runtime/server/api/auth/security.js +85 -0
- package/dist/runtime/server/api/release-fingerprint.d.ts +2 -0
- package/dist/runtime/server/api/release-fingerprint.js +36 -0
- package/dist/runtime/server/index.d.ts +1 -0
- package/dist/runtime/server/index.js +3 -0
- package/dist/runtime/server/plugins/runtime-config.d.ts +7 -0
- package/dist/runtime/server/plugins/runtime-config.js +7 -0
- package/dist/runtime/server/utils/auth-snapshot.d.ts +26 -0
- package/dist/runtime/server/utils/auth-snapshot.js +165 -0
- package/dist/runtime/server/utils/http.d.ts +16 -0
- package/dist/runtime/server/utils/http.js +133 -0
- package/dist/runtime/server/utils/server-convex-caller.d.ts +31 -0
- package/dist/runtime/server/utils/server-convex-caller.js +151 -0
- package/dist/runtime/server/utils/server-convex-options.d.ts +83 -0
- package/dist/runtime/server/utils/server-convex-options.js +69 -0
- package/dist/runtime/server/utils/signed-client-ip.d.ts +6 -0
- package/dist/runtime/server/utils/signed-client-ip.js +29 -0
- package/dist/runtime/server/utils/ssr-auth-headers.d.ts +26 -0
- package/dist/runtime/server/utils/ssr-auth-headers.js +49 -0
- package/dist/runtime/server/utils/token-exchange.d.ts +27 -0
- package/dist/runtime/server/utils/token-exchange.js +96 -0
- package/dist/runtime/shared/auth-cookie.d.ts +4 -0
- package/dist/runtime/shared/auth-cookie.js +25 -0
- package/dist/runtime/shared/auth-origin.d.ts +15 -0
- package/dist/runtime/shared/auth-origin.js +42 -0
- package/dist/runtime/shared/bounded-stream.d.ts +2 -0
- package/dist/runtime/shared/bounded-stream.js +50 -0
- package/dist/runtime/shared/client-ip.d.ts +21 -0
- package/dist/runtime/shared/client-ip.js +123 -0
- package/dist/runtime/shared/release-fingerprint.d.ts +5 -0
- package/dist/runtime/shared/release-fingerprint.js +17 -0
- package/dist/runtime/types.d.ts +19 -0
- package/dist/runtime/utils/auth-config.d.ts +24 -0
- package/dist/runtime/utils/auth-config.js +45 -0
- package/dist/runtime/utils/auth-contract.d.ts +22 -0
- package/dist/runtime/utils/auth-contract.js +0 -0
- package/dist/runtime/utils/auth-errors.d.ts +5 -0
- package/dist/runtime/utils/auth-errors.js +22 -0
- package/dist/runtime/utils/auth-identity-state.d.ts +4 -0
- package/dist/runtime/utils/auth-identity-state.js +8 -0
- package/dist/runtime/utils/auth-pending-state.d.ts +13 -0
- package/dist/runtime/utils/auth-pending-state.js +4 -0
- package/dist/runtime/utils/auth-pending.d.ts +10 -0
- package/dist/runtime/utils/auth-pending.js +30 -0
- package/dist/runtime/utils/auth-route-protection.d.ts +16 -0
- package/dist/runtime/utils/auth-route-protection.js +58 -0
- package/dist/runtime/utils/auth-status.d.ts +47 -0
- package/dist/runtime/utils/auth-status.js +8 -0
- package/dist/runtime/utils/bounded-convex-fetch.d.ts +18 -0
- package/dist/runtime/utils/bounded-convex-fetch.js +152 -0
- package/dist/runtime/utils/callable-devtools.d.ts +17 -0
- package/dist/runtime/utils/callable-devtools.js +49 -0
- package/dist/runtime/utils/callable-observer.d.ts +2 -0
- package/dist/runtime/utils/callable-observer.js +1 -0
- package/dist/runtime/utils/composable-scope.d.ts +2 -0
- package/dist/runtime/utils/composable-scope.js +8 -0
- package/dist/runtime/utils/config-defaults.d.ts +8 -0
- package/dist/runtime/utils/config-defaults.js +8 -0
- package/dist/runtime/utils/convex-cache.d.ts +68 -0
- package/dist/runtime/utils/convex-cache.js +47 -0
- package/dist/runtime/utils/convex-config.d.ts +16 -0
- package/dist/runtime/utils/convex-config.js +50 -0
- package/dist/runtime/utils/convex-shared.d.ts +67 -0
- package/dist/runtime/utils/convex-shared.js +134 -0
- package/dist/runtime/utils/identity-changed-error.d.ts +4 -0
- package/dist/runtime/utils/identity-changed-error.js +12 -0
- package/dist/runtime/utils/identity-key.d.ts +19 -0
- package/dist/runtime/utils/identity-key.js +10 -0
- package/dist/runtime/utils/integrated-auth-client.d.ts +5 -0
- package/dist/runtime/utils/integrated-auth-client.js +0 -0
- package/dist/runtime/utils/logger.d.ts +42 -0
- package/dist/runtime/utils/logger.js +190 -0
- package/dist/runtime/utils/mime-type.d.ts +33 -0
- package/dist/runtime/utils/mime-type.js +10 -0
- package/dist/runtime/utils/query-execution-gate.d.ts +47 -0
- package/dist/runtime/utils/query-execution-gate.js +63 -0
- package/dist/runtime/utils/query-execution.d.ts +8 -0
- package/dist/runtime/utils/query-execution.js +19 -0
- package/dist/runtime/utils/query-foundation.d.ts +20 -0
- package/dist/runtime/utils/query-foundation.js +39 -0
- package/dist/runtime/utils/query-state.d.ts +12 -0
- package/dist/runtime/utils/query-state.js +12 -0
- package/dist/runtime/utils/reactive-args.d.ts +5 -0
- package/dist/runtime/utils/reactive-args.js +42 -0
- package/dist/runtime/utils/runtime-config-normalize.d.ts +22 -0
- package/dist/runtime/utils/runtime-config-normalize.js +28 -0
- package/dist/runtime/utils/runtime-config.d.ts +4 -0
- package/dist/runtime/utils/runtime-config.js +8 -0
- package/dist/runtime/utils/sanitize-diagnostic.d.ts +6 -0
- package/dist/runtime/utils/sanitize-diagnostic.js +81 -0
- package/dist/runtime/utils/shared-helpers.d.ts +46 -0
- package/dist/runtime/utils/shared-helpers.js +139 -0
- package/dist/runtime/utils/site-url.d.ts +4 -0
- package/dist/runtime/utils/site-url.js +15 -0
- package/dist/runtime/utils/ssr-pagination-state.d.ts +3 -0
- package/dist/runtime/utils/ssr-pagination-state.js +3 -0
- package/dist/runtime/utils/types.d.ts +29 -0
- package/dist/runtime/utils/types.js +0 -0
- package/dist/runtime/utils/upload-core.d.ts +16 -0
- package/dist/runtime/utils/upload-core.js +116 -0
- package/dist/types.d.mts +21 -0
- package/package.json +253 -0
- package/security/upstream-convex-better-auth.json +477 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { APIError } from "better-auth/api";
|
|
2
|
+
import {
|
|
3
|
+
OAuthSecurityError,
|
|
4
|
+
assertSafeStoredOAuthClient,
|
|
5
|
+
assertSafeStoredOAuthResource
|
|
6
|
+
} from "./oauth-security.js";
|
|
7
|
+
const DISABLED_PINNED_PROVIDER_PATHS = /* @__PURE__ */ new Set([
|
|
8
|
+
"/get-access-token",
|
|
9
|
+
"/refresh-token",
|
|
10
|
+
"/.well-known/openid-configuration",
|
|
11
|
+
"/oauth2/client/rotate-secret",
|
|
12
|
+
"/oauth2/create-client",
|
|
13
|
+
"/oauth2/delete-client",
|
|
14
|
+
"/oauth2/end-session",
|
|
15
|
+
"/oauth2/get-client",
|
|
16
|
+
"/oauth2/get-clients",
|
|
17
|
+
"/oauth2/introspect",
|
|
18
|
+
"/oauth2/register",
|
|
19
|
+
"/oauth2/update-client",
|
|
20
|
+
"/oauth2/userinfo",
|
|
21
|
+
"/token"
|
|
22
|
+
]);
|
|
23
|
+
function invalidConfiguration() {
|
|
24
|
+
throw new OAuthSecurityError("AUTH_OAUTH_CONFIG_INVALID");
|
|
25
|
+
}
|
|
26
|
+
export function validatePinnedOAuthProviderRuntime(context, options, hardened) {
|
|
27
|
+
const configuredPlugins = context.options.plugins ?? [];
|
|
28
|
+
const jwtIndexes = configuredPlugins.map((plugin, index) => plugin.id === "jwt" ? index : -1).filter((index) => index >= 0);
|
|
29
|
+
const convexIndexes = configuredPlugins.map((plugin, index) => plugin.id === "@lupinum/better-convex-nuxt" ? index : -1).filter((index) => index >= 0);
|
|
30
|
+
const oauthIndexes = configuredPlugins.map((plugin, index) => plugin.id === "oauth-provider" ? index : -1).filter((index) => index >= 0);
|
|
31
|
+
if (jwtIndexes.length !== 1 || convexIndexes.length !== 1 || oauthIndexes.length !== 1 || !(jwtIndexes[0] < convexIndexes[0] && convexIndexes[0] < oauthIndexes[0])) {
|
|
32
|
+
invalidConfiguration();
|
|
33
|
+
}
|
|
34
|
+
const disabledPaths = new Set(context.options.disabledPaths ?? []);
|
|
35
|
+
if ([...DISABLED_PINNED_PROVIDER_PATHS].some((path) => !disabledPaths.has(path))) {
|
|
36
|
+
invalidConfiguration();
|
|
37
|
+
}
|
|
38
|
+
if (context.options.account?.encryptOAuthTokens !== true || context.options.account.storeAccountCookie !== false || context.options.verification?.storeIdentifier !== "hashed") {
|
|
39
|
+
invalidConfiguration();
|
|
40
|
+
}
|
|
41
|
+
const issuer = context.baseURL;
|
|
42
|
+
let issuerUrl;
|
|
43
|
+
try {
|
|
44
|
+
issuerUrl = new URL(issuer);
|
|
45
|
+
} catch {
|
|
46
|
+
invalidConfiguration();
|
|
47
|
+
}
|
|
48
|
+
if (issuerUrl.pathname !== "/api/auth" || issuerUrl.search || issuerUrl.hash || issuerUrl.username || issuerUrl.password) {
|
|
49
|
+
invalidConfiguration();
|
|
50
|
+
}
|
|
51
|
+
const jwtPlugin = configuredPlugins[jwtIndexes[0]];
|
|
52
|
+
const jwtOptions = jwtPlugin.options?.jwt;
|
|
53
|
+
const jwksOptions = jwtPlugin.options?.jwks;
|
|
54
|
+
if (!jwtOptions || typeof jwtOptions !== "object" || jwtOptions.issuer !== issuer || jwtOptions.audience !== issuer || jwtOptions.expirationTime !== "10m" || !jwksOptions || typeof jwksOptions !== "object" || jwksOptions.keyPairConfig?.alg !== "RS256" || jwksOptions.disablePrivateKeyEncryption !== false) {
|
|
55
|
+
invalidConfiguration();
|
|
56
|
+
}
|
|
57
|
+
const oauthPlugin = configuredPlugins[oauthIndexes[0]];
|
|
58
|
+
const providerOptions = oauthPlugin.options;
|
|
59
|
+
if (!providerOptions || providerOptions.clientPrivileges !== hardened.clientPrivileges || providerOptions.resourcePrivileges !== hardened.resourcePrivileges || providerOptions.customAccessTokenClaims !== hardened.customAccessTokenClaims || providerOptions.accessTokenExpiresIn !== options.accessTokenExpiresIn || providerOptions.codeExpiresIn !== options.codeExpiresIn || !Array.isArray(providerOptions.grantTypes) || providerOptions.grantTypes.length !== 1 || providerOptions.grantTypes[0] !== "authorization_code") {
|
|
60
|
+
invalidConfiguration();
|
|
61
|
+
}
|
|
62
|
+
if (process.env.NODE_ENV === "production") {
|
|
63
|
+
const proxySecret = process.env.BCN_AUTH_PROXY_IP_SECRET;
|
|
64
|
+
if (typeof proxySecret !== "string" || proxySecret.length < 32) {
|
|
65
|
+
invalidConfiguration();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return providerOptions;
|
|
69
|
+
}
|
|
70
|
+
function parseScope(value) {
|
|
71
|
+
const scopes = value.split(" ");
|
|
72
|
+
if (scopes.some((scope) => scope.length === 0) || new Set(scopes).size !== scopes.length) {
|
|
73
|
+
invalidConfiguration();
|
|
74
|
+
}
|
|
75
|
+
return scopes;
|
|
76
|
+
}
|
|
77
|
+
function invalidClientProfile() {
|
|
78
|
+
throw new APIError("BAD_REQUEST", {
|
|
79
|
+
message: "AUTH_OAUTH_CLIENT_PROFILE_INVALID"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function projectPinnedClientRecord(input, allowedScopes, updating) {
|
|
83
|
+
const tokenEndpointAuthMethod = updating && input.token_endpoint_auth_method === void 0 ? "client_secret_basic" : input.token_endpoint_auth_method;
|
|
84
|
+
const rawExpiry = input.client_secret_expires_at;
|
|
85
|
+
const expiresAt = rawExpiry === void 0 || rawExpiry === 0 || rawExpiry === "0" ? void 0 : typeof rawExpiry === "number" || typeof rawExpiry === "string" ? new Date(Number(rawExpiry) * 1e3) : rawExpiry;
|
|
86
|
+
return {
|
|
87
|
+
backchannelLogoutSessionRequired: input.backchannel_logout_session_required,
|
|
88
|
+
backchannelLogoutUri: input.backchannel_logout_uri,
|
|
89
|
+
clientId: "provisioning-client",
|
|
90
|
+
clientSecret: tokenEndpointAuthMethod === "client_secret_basic" ? "provisioning-secret" : null,
|
|
91
|
+
disabled: input.disabled,
|
|
92
|
+
dpopBoundAccessTokens: updating && input.dpop_bound_access_tokens === void 0 ? false : input.dpop_bound_access_tokens,
|
|
93
|
+
enableEndSession: updating && input.enable_end_session === void 0 ? false : input.enable_end_session,
|
|
94
|
+
expiresAt,
|
|
95
|
+
grantTypes: updating && input.grant_types === void 0 ? ["authorization_code"] : input.grant_types,
|
|
96
|
+
jwks: input.jwks,
|
|
97
|
+
jwksUri: input.jwks_uri,
|
|
98
|
+
metadata: input.metadata,
|
|
99
|
+
postLogoutRedirectUris: input.post_logout_redirect_uris,
|
|
100
|
+
public: updating ? false : tokenEndpointAuthMethod === "none",
|
|
101
|
+
redirectUris: updating && input.redirect_uris === void 0 ? ["https://provisioning.invalid/callback"] : input.redirect_uris,
|
|
102
|
+
requirePKCE: updating && input.require_pkce === void 0 ? true : input.require_pkce,
|
|
103
|
+
responseTypes: updating && input.response_types === void 0 ? ["code"] : input.response_types,
|
|
104
|
+
scopes: typeof input.scope === "string" ? parseScope(input.scope) : updating ? [...allowedScopes] : [],
|
|
105
|
+
skipConsent: updating && input.skip_consent === void 0 ? false : input.skip_consent,
|
|
106
|
+
softwareStatement: input.software_statement,
|
|
107
|
+
subjectType: input.subject_type,
|
|
108
|
+
tokenEndpointAuthMethod,
|
|
109
|
+
type: updating && input.type === void 0 ? "web" : input.type
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function assertSafePinnedClientProvisioning(method, body, allowedScopes) {
|
|
113
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) invalidClientProfile();
|
|
114
|
+
const input = body;
|
|
115
|
+
try {
|
|
116
|
+
if (method !== "POST") invalidConfiguration();
|
|
117
|
+
assertSafeStoredOAuthClient(
|
|
118
|
+
projectPinnedClientRecord(input, allowedScopes, false),
|
|
119
|
+
allowedScopes
|
|
120
|
+
);
|
|
121
|
+
} catch {
|
|
122
|
+
invalidClientProfile();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export function assertSafePinnedClientUpdate(method, body, allowedScopes) {
|
|
126
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) invalidClientProfile();
|
|
127
|
+
const input = body;
|
|
128
|
+
try {
|
|
129
|
+
if (method !== "PATCH") invalidConfiguration();
|
|
130
|
+
assertSafeStoredOAuthClient(
|
|
131
|
+
projectPinnedClientRecord(input, allowedScopes, true),
|
|
132
|
+
allowedScopes
|
|
133
|
+
);
|
|
134
|
+
} catch {
|
|
135
|
+
invalidClientProfile();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function invalidResourceProfile() {
|
|
139
|
+
throw new APIError("BAD_REQUEST", {
|
|
140
|
+
message: "AUTH_OAUTH_RESOURCE_PROFILE_INVALID"
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export function assertSafePinnedResourceProvisioning(method, body, allowedScopes) {
|
|
144
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) invalidResourceProfile();
|
|
145
|
+
const input = body;
|
|
146
|
+
try {
|
|
147
|
+
if (method !== "POST" && method !== "PATCH") invalidConfiguration();
|
|
148
|
+
const updating = method === "PATCH";
|
|
149
|
+
assertSafeStoredOAuthResource(
|
|
150
|
+
{
|
|
151
|
+
accessTokenTtl: input.accessTokenTtl,
|
|
152
|
+
allowedScopes: input.allowedScopes,
|
|
153
|
+
customClaims: input.customClaims,
|
|
154
|
+
// Disabling an existing resource is an intentional terminal transition.
|
|
155
|
+
// Every field that can make an enabled resource unsafe still uses the
|
|
156
|
+
// canonical stored-record predicate.
|
|
157
|
+
disabled: updating ? false : input.disabled,
|
|
158
|
+
dpopBoundAccessTokensRequired: input.dpopBoundAccessTokensRequired,
|
|
159
|
+
identifier: updating ? "https://provisioning.invalid/resource" : String(input.identifier),
|
|
160
|
+
name: updating ? "Provisioning resource" : input.name,
|
|
161
|
+
refreshTokenTtl: input.refreshTokenTtl,
|
|
162
|
+
signingAlgorithm: input.signingAlgorithm,
|
|
163
|
+
signingKeyId: input.signingKeyId
|
|
164
|
+
},
|
|
165
|
+
allowedScopes
|
|
166
|
+
);
|
|
167
|
+
} catch {
|
|
168
|
+
invalidResourceProfile();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OAuthLiveAccess } from './oauth-live-access.js';
|
|
2
|
+
import { type OAuthAccessTokenExpectations, type OAuthPrincipal } from './oauth-security.js';
|
|
3
|
+
export interface VerifyOAuthBearerTokenOptions extends OAuthAccessTokenExpectations {
|
|
4
|
+
jwksUrl: string;
|
|
5
|
+
}
|
|
6
|
+
export type BetterAuthMcpAccessVerifierOptions = Omit<VerifyOAuthBearerTokenOptions, 'audience' | 'issuer'> & {
|
|
7
|
+
/**
|
|
8
|
+
* Required request-local Better Auth authority check. The callback runs only on the server and
|
|
9
|
+
* must validate the current session, user, client, consent, and resource grant from canonical
|
|
10
|
+
* state. Provider-private session identity never enters the MCP access context.
|
|
11
|
+
*/
|
|
12
|
+
readonly validateLiveAccess: (access: OAuthLiveAccess) => Promise<boolean>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Uses the pinned provider's resource client for JOSE/JWKS processing, then
|
|
16
|
+
* applies BCN's stricter beta token-class and exact-binding checks. Live
|
|
17
|
+
* session, client, consent, membership, and operation authorization remain a
|
|
18
|
+
* separate Convex transaction and are deliberately not derived from claims.
|
|
19
|
+
*/
|
|
20
|
+
export declare function verifyOAuthBearerToken(token: string | undefined, options: VerifyOAuthBearerTokenOptions): Promise<OAuthPrincipal>;
|
|
21
|
+
/**
|
|
22
|
+
* Adapts BCN's strict Better Auth OAuth access-token profile to the provider-neutral MCP verifier
|
|
23
|
+
* contract without importing the MCP package or exposing provider-private session state.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createBetterAuthMcpAccessVerifier(options: BetterAuthMcpAccessVerifierOptions): Readonly<{
|
|
26
|
+
verifyAccessToken(token: string, expected: {
|
|
27
|
+
readonly issuer: string;
|
|
28
|
+
readonly resource: URL;
|
|
29
|
+
}): Promise<Readonly<{
|
|
30
|
+
access: Readonly<{
|
|
31
|
+
issuer: string;
|
|
32
|
+
subject: string;
|
|
33
|
+
clientId: string;
|
|
34
|
+
resource: string;
|
|
35
|
+
scopes: readonly string[];
|
|
36
|
+
}>;
|
|
37
|
+
expiresAt: number;
|
|
38
|
+
}>>;
|
|
39
|
+
}>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { oauthProviderResourceClient } from "@better-auth/oauth-provider/resource-client";
|
|
2
|
+
import {
|
|
3
|
+
OAuthSecurityError,
|
|
4
|
+
installUrlCanParseCompatibility,
|
|
5
|
+
prepareOAuthAccessTokenVerification
|
|
6
|
+
} from "./oauth-security.js";
|
|
7
|
+
const COMPACT_JWT_PATTERN = /^[\w-]+\.[\w-]+\.[\w-]+$/u;
|
|
8
|
+
const MAX_COMPACT_JWT_BYTES = 8192;
|
|
9
|
+
function invalidToken() {
|
|
10
|
+
throw new OAuthSecurityError("AUTH_OAUTH_TOKEN_INVALID");
|
|
11
|
+
}
|
|
12
|
+
function decodeVerifiedPayload(token) {
|
|
13
|
+
if (token.length > MAX_COMPACT_JWT_BYTES || !COMPACT_JWT_PATTERN.test(token)) invalidToken();
|
|
14
|
+
const encodedPayload = token.split(".")[1];
|
|
15
|
+
if (!encodedPayload || encodedPayload.length % 4 === 1) invalidToken();
|
|
16
|
+
try {
|
|
17
|
+
const base64 = encodedPayload.replaceAll("-", "+").replaceAll("_", "/");
|
|
18
|
+
const padded = `${base64}${"=".repeat((4 - base64.length % 4) % 4)}`;
|
|
19
|
+
const bytes = Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
20
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
21
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) invalidToken();
|
|
22
|
+
return value;
|
|
23
|
+
} catch {
|
|
24
|
+
invalidToken();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isSecureOAuthEndpoint(value) {
|
|
28
|
+
return value.protocol === "https:" || value.protocol === "http:" && (value.hostname === "127.0.0.1" || value.hostname === "localhost" || value.hostname === "[::1]");
|
|
29
|
+
}
|
|
30
|
+
function requireCanonicalJwksUrl(issuer, jwksUrl) {
|
|
31
|
+
let parsedIssuer;
|
|
32
|
+
let parsedJwks;
|
|
33
|
+
try {
|
|
34
|
+
parsedIssuer = new URL(issuer);
|
|
35
|
+
parsedJwks = new URL(jwksUrl);
|
|
36
|
+
} catch {
|
|
37
|
+
throw new OAuthSecurityError("AUTH_OAUTH_TOKEN_INVALID");
|
|
38
|
+
}
|
|
39
|
+
if (!isSecureOAuthEndpoint(parsedIssuer) || parsedIssuer.username || parsedIssuer.password || parsedIssuer.search || parsedIssuer.hash || !isSecureOAuthEndpoint(parsedJwks) || parsedJwks.href !== jwksUrl || parsedJwks.origin !== parsedIssuer.origin || parsedJwks.username || parsedJwks.password || parsedJwks.search || parsedJwks.hash || jwksUrl !== `${issuer.endsWith("/") ? issuer : `${issuer}/`}jwks`) {
|
|
40
|
+
throw new OAuthSecurityError("AUTH_OAUTH_TOKEN_INVALID");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function verifyOAuthBearerToken(token, options) {
|
|
44
|
+
installUrlCanParseCompatibility();
|
|
45
|
+
requireCanonicalJwksUrl(options.issuer, options.jwksUrl);
|
|
46
|
+
if (typeof token !== "string") invalidToken();
|
|
47
|
+
decodeVerifiedPayload(token);
|
|
48
|
+
const maxLifetimeSeconds = options.maxLifetimeSeconds ?? 600;
|
|
49
|
+
const verification = prepareOAuthAccessTokenVerification(options);
|
|
50
|
+
const verifyBearerToken = oauthProviderResourceClient().getActions().verifyBearerToken;
|
|
51
|
+
await verifyBearerToken(token, {
|
|
52
|
+
jwksUrl: options.jwksUrl,
|
|
53
|
+
verifyOptions: {
|
|
54
|
+
algorithms: ["RS256"],
|
|
55
|
+
audience: options.audience,
|
|
56
|
+
clockTolerance: 0,
|
|
57
|
+
currentDate: verification.currentDate,
|
|
58
|
+
issuer: options.issuer,
|
|
59
|
+
maxTokenAge: `${maxLifetimeSeconds}s`,
|
|
60
|
+
typ: "at+jwt"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return verification.assert(decodeVerifiedPayload(token));
|
|
64
|
+
}
|
|
65
|
+
export function createBetterAuthMcpAccessVerifier(options) {
|
|
66
|
+
if (typeof options.validateLiveAccess !== "function") {
|
|
67
|
+
throw new OAuthSecurityError("AUTH_OAUTH_CONFIG_INVALID");
|
|
68
|
+
}
|
|
69
|
+
const fixedOptions = Object.freeze({
|
|
70
|
+
allowedScopes: Object.freeze([...options.allowedScopes]),
|
|
71
|
+
jwksUrl: options.jwksUrl,
|
|
72
|
+
...options.clientId === void 0 ? {} : { clientId: options.clientId },
|
|
73
|
+
...options.maxLifetimeSeconds === void 0 ? {} : { maxLifetimeSeconds: options.maxLifetimeSeconds },
|
|
74
|
+
...options.requiredScopes === void 0 ? {} : { requiredScopes: Object.freeze([...options.requiredScopes]) },
|
|
75
|
+
...options.subject === void 0 ? {} : { subject: options.subject },
|
|
76
|
+
validateLiveAccess: options.validateLiveAccess
|
|
77
|
+
});
|
|
78
|
+
const { validateLiveAccess, ...verificationOptions } = fixedOptions;
|
|
79
|
+
return Object.freeze({
|
|
80
|
+
async verifyAccessToken(token, expected) {
|
|
81
|
+
if (!expected || typeof expected.issuer !== "string" || !(expected.resource instanceof URL) || !isSecureOAuthEndpoint(expected.resource) || expected.resource.username || expected.resource.password || expected.resource.search || expected.resource.hash) {
|
|
82
|
+
invalidToken();
|
|
83
|
+
}
|
|
84
|
+
const resource = expected.resource.href;
|
|
85
|
+
const principal = await verifyOAuthBearerToken(token, {
|
|
86
|
+
...verificationOptions,
|
|
87
|
+
audience: resource,
|
|
88
|
+
issuer: expected.issuer
|
|
89
|
+
});
|
|
90
|
+
let live = false;
|
|
91
|
+
try {
|
|
92
|
+
live = await validateLiveAccess(
|
|
93
|
+
Object.freeze({
|
|
94
|
+
clientId: principal.clientId,
|
|
95
|
+
issuer: expected.issuer,
|
|
96
|
+
resource,
|
|
97
|
+
scopes: Object.freeze([...principal.scopes]),
|
|
98
|
+
sessionId: principal.sessionId,
|
|
99
|
+
subject: principal.subject
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
} catch {
|
|
103
|
+
invalidToken();
|
|
104
|
+
}
|
|
105
|
+
if (live !== true) invalidToken();
|
|
106
|
+
return Object.freeze({
|
|
107
|
+
access: Object.freeze({
|
|
108
|
+
issuer: expected.issuer,
|
|
109
|
+
subject: principal.subject,
|
|
110
|
+
clientId: principal.clientId,
|
|
111
|
+
resource,
|
|
112
|
+
scopes: Object.freeze([...principal.scopes])
|
|
113
|
+
}),
|
|
114
|
+
expiresAt: principal.expiresAt
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { OAuthOptions, Scope } from '@better-auth/oauth-provider';
|
|
2
|
+
/**
|
|
3
|
+
* The exact Better Auth OAuth Provider profile accepted by this release.
|
|
4
|
+
*
|
|
5
|
+
* This is a pinned compatibility firewall, not a provider-neutral OAuth
|
|
6
|
+
* configuration abstraction. Runtime validation intentionally accepts only a
|
|
7
|
+
* strict subset of the exact installed provider's options.
|
|
8
|
+
*/
|
|
9
|
+
declare const PINNED_OAUTH_PROVIDER_KEYS: readonly ["accessTokenExpiresIn", "allowDynamicClientRegistration", "allowPublicClientPrelogin", "allowUnauthenticatedClientRegistration", "advertisedMetadata", "clientCredentialGrantDefaultScopes", "clientPrivileges", "clientRegistrationRequirePKCE", "codeExpiresIn", "consentPage", "customAccessTokenClaims", "customIdTokenClaims", "customTokenResponseFields", "customUserInfoClaims", "disableJwtPlugin", "dpop", "enforcePerClientResources", "extensions", "grantTypes", "loginPage", "m2mAccessTokenExpiresIn", "pairwiseSecret", "rateLimit", "requestUriResolver", "resourcePrivileges", "resources", "scopes", "storeClientSecret", "storeTokens", "validateInitialAccessToken"];
|
|
10
|
+
export type PinnedOAuthProviderProfile = Pick<OAuthOptions<Scope[]>, (typeof PINNED_OAUTH_PROVIDER_KEYS)[number]>;
|
|
11
|
+
export interface OAuthClientRecord extends Record<string, unknown> {
|
|
12
|
+
clientId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface OAuthResourceRecord extends Record<string, unknown> {
|
|
15
|
+
identifier: string;
|
|
16
|
+
}
|
|
17
|
+
export interface OAuthClientResourceRecord extends Record<string, unknown> {
|
|
18
|
+
clientId: string;
|
|
19
|
+
resourceId: string;
|
|
20
|
+
}
|
|
21
|
+
export interface OAuthAccessTokenExpectations {
|
|
22
|
+
allowedScopes: readonly string[];
|
|
23
|
+
audience: string;
|
|
24
|
+
clientId?: string;
|
|
25
|
+
issuer: string;
|
|
26
|
+
maxLifetimeSeconds?: number;
|
|
27
|
+
requiredScopes?: readonly string[];
|
|
28
|
+
subject?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface OAuthPrincipal {
|
|
31
|
+
clientId: string;
|
|
32
|
+
expiresAt: number;
|
|
33
|
+
scopes: readonly string[];
|
|
34
|
+
sessionId: string;
|
|
35
|
+
subject: string;
|
|
36
|
+
}
|
|
37
|
+
interface PrivilegeContext {
|
|
38
|
+
headers: Headers;
|
|
39
|
+
session?: unknown;
|
|
40
|
+
user?: unknown;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
interface HardenedOAuthCallbacks {
|
|
44
|
+
clientPrivileges: (context: PrivilegeContext) => Promise<boolean>;
|
|
45
|
+
customAccessTokenClaims: (info: unknown) => Promise<{
|
|
46
|
+
token_use: 'oauth-access';
|
|
47
|
+
}>;
|
|
48
|
+
resourcePrivileges: (context: PrivilegeContext) => Promise<boolean>;
|
|
49
|
+
}
|
|
50
|
+
export declare class OAuthSecurityError extends Error {
|
|
51
|
+
readonly code: string;
|
|
52
|
+
constructor(code: string);
|
|
53
|
+
}
|
|
54
|
+
interface UrlCanParseTarget {
|
|
55
|
+
canParse?: (input: string | URL, base?: string | URL) => boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Supply the one modern URL primitive absent from the manifest-pinned Convex
|
|
59
|
+
* isolate but called by @better-auth/oauth-provider@1.7.0-rc.2 while parsing
|
|
60
|
+
* RFC 8707 resources. Delete this helper and both call sites as soon as a
|
|
61
|
+
* reviewed dependency tuple supplies the primitive or removes those calls.
|
|
62
|
+
*/
|
|
63
|
+
export declare function installUrlCanParseCompatibility(target?: UrlCanParseTarget): void;
|
|
64
|
+
export declare function validateOAuthScopes(scopes: unknown): asserts scopes is readonly string[];
|
|
65
|
+
export declare function validateOAuthProviderProfile(options: PinnedOAuthProviderProfile): void;
|
|
66
|
+
export declare function hardenOAuthProviderCallbacks(options: PinnedOAuthProviderProfile): HardenedOAuthCallbacks;
|
|
67
|
+
export declare function validateResourceIdentifier(identifier: string): void;
|
|
68
|
+
export declare function validateOAuthRedirectUris(value: unknown): asserts value is readonly string[];
|
|
69
|
+
export declare function assertSafeStoredOAuthClient(client: OAuthClientRecord, allowedScopes: readonly string[]): void;
|
|
70
|
+
export declare function assertSafeStoredOAuthResource(resource: OAuthResourceRecord, allowedScopes: readonly string[]): void;
|
|
71
|
+
export declare function assertSafeStoredOAuthClientResource(link: OAuthClientResourceRecord, clientId: string, resourceId: string): void;
|
|
72
|
+
export declare function assertSingleParameters(parameters: URLSearchParams, singletonFields: readonly string[]): void;
|
|
73
|
+
export declare function requireSingleParameter(parameters: URLSearchParams, field: string): string;
|
|
74
|
+
export declare function parseBoundedFormRequest(request: Request, allowedFields: readonly string[], maxBytes?: number): Promise<URLSearchParams>;
|
|
75
|
+
export declare function parseBoundedFormBody(request: Request, maxBytes: number): Promise<URLSearchParams>;
|
|
76
|
+
export declare function projectOAuthAuthorizationServerMetadata(officialMetadata: unknown, issuer: string, scopes: readonly string[]): Readonly<Record<string, unknown>>;
|
|
77
|
+
export declare function assertOAuthAccessTokenClaims(payload: unknown, expectations: OAuthAccessTokenExpectations): OAuthPrincipal;
|
|
78
|
+
export declare function prepareOAuthAccessTokenVerification(expectations: OAuthAccessTokenExpectations): Readonly<{
|
|
79
|
+
currentDate: Date;
|
|
80
|
+
assert(payload: unknown): OAuthPrincipal;
|
|
81
|
+
}>;
|
|
82
|
+
export {};
|