@robelest/convex-auth 0.0.3-preview → 0.0.3-preview.3
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.cjs +15 -15
- package/dist/client/index.d.ts +40 -12
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +73 -12
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -2
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +1 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/{portalBridge.d.ts → bridge.d.ts} +2 -2
- package/dist/component/bridge.d.ts.map +1 -0
- package/dist/component/{portalBridge.js → bridge.js} +2 -2
- package/dist/component/bridge.js.map +1 -0
- package/dist/component/index.d.ts +11 -4
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/index.js +8 -2
- package/dist/component/index.js.map +1 -1
- package/dist/component/public.d.ts +24 -17
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +23 -4
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +11 -7
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +4 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/providers/anonymous.d.ts +3 -0
- package/dist/providers/anonymous.d.ts.map +1 -1
- package/dist/providers/anonymous.js +3 -0
- package/dist/providers/anonymous.js.map +1 -1
- package/dist/providers/credentials.d.ts +3 -0
- package/dist/providers/credentials.d.ts.map +1 -1
- package/dist/providers/credentials.js +3 -0
- package/dist/providers/credentials.js.map +1 -1
- package/dist/providers/email.d.ts +3 -0
- package/dist/providers/email.d.ts.map +1 -1
- package/dist/providers/email.js +3 -0
- package/dist/providers/email.js.map +1 -1
- package/dist/providers/passkey.d.ts +7 -1
- package/dist/providers/passkey.d.ts.map +1 -1
- package/dist/providers/passkey.js +7 -1
- package/dist/providers/passkey.js.map +1 -1
- package/dist/providers/password.d.ts +3 -0
- package/dist/providers/password.d.ts.map +1 -1
- package/dist/providers/password.js +3 -0
- package/dist/providers/password.js.map +1 -1
- package/dist/providers/phone.d.ts +3 -0
- package/dist/providers/phone.d.ts.map +1 -1
- package/dist/providers/phone.js +3 -0
- package/dist/providers/phone.js.map +1 -1
- package/dist/providers/totp.d.ts +8 -0
- package/dist/providers/totp.d.ts.map +1 -1
- package/dist/providers/totp.js +8 -0
- package/dist/providers/totp.js.map +1 -1
- package/dist/server/{convex-auth.d.ts → auth.d.ts} +226 -36
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/{convex-auth.js → auth.js} +287 -111
- package/dist/server/auth.js.map +1 -0
- package/dist/server/errors.d.ts +148 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/errors.js +179 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/implementation/index.d.ts +170 -48
- package/dist/server/implementation/index.d.ts.map +1 -1
- package/dist/server/implementation/index.js +383 -167
- package/dist/server/implementation/index.js.map +1 -1
- package/dist/server/implementation/{apiKey.d.ts → keys.d.ts} +1 -1
- package/dist/server/implementation/keys.d.ts.map +1 -0
- package/dist/server/implementation/{apiKey.js → keys.js} +4 -5
- package/dist/server/implementation/keys.js.map +1 -0
- package/dist/server/implementation/mutations/{modifyAccount.d.ts → account.d.ts} +3 -3
- package/dist/server/implementation/mutations/account.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{modifyAccount.js → account.js} +4 -3
- package/dist/server/implementation/mutations/account.js.map +1 -0
- package/dist/server/implementation/mutations/{createVerificationCode.d.ts → code.d.ts} +1 -1
- package/dist/server/implementation/mutations/code.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{createVerificationCode.js → code.js} +2 -2
- package/dist/server/implementation/mutations/code.js.map +1 -0
- package/dist/server/implementation/mutations/index.d.ts +33 -33
- package/dist/server/implementation/mutations/index.d.ts.map +1 -1
- package/dist/server/implementation/mutations/index.js +22 -22
- package/dist/server/implementation/mutations/index.js.map +1 -1
- package/dist/server/implementation/mutations/{invalidateSessions.d.ts → invalidate.d.ts} +1 -1
- package/dist/server/implementation/mutations/invalidate.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{invalidateSessions.js → invalidate.js} +2 -2
- package/dist/server/implementation/mutations/invalidate.js.map +1 -0
- package/dist/server/implementation/mutations/{userOAuth.d.ts → oauth.d.ts} +3 -3
- package/dist/server/implementation/mutations/oauth.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{userOAuth.js → oauth.js} +4 -3
- package/dist/server/implementation/mutations/oauth.js.map +1 -0
- package/dist/server/implementation/mutations/{refreshSession.d.ts → refresh.d.ts} +1 -1
- package/dist/server/implementation/mutations/refresh.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{refreshSession.js → refresh.js} +3 -3
- package/dist/server/implementation/mutations/refresh.js.map +1 -0
- package/dist/server/implementation/mutations/{createAccountFromCredentials.d.ts → register.d.ts} +4 -4
- package/dist/server/implementation/mutations/register.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{createAccountFromCredentials.js → register.js} +4 -3
- package/dist/server/implementation/mutations/register.js.map +1 -0
- package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.d.ts → retrieve.d.ts} +3 -3
- package/dist/server/implementation/mutations/retrieve.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.js → retrieve.js} +3 -3
- package/dist/server/implementation/mutations/retrieve.js.map +1 -0
- package/dist/server/implementation/mutations/{verifierSignature.d.ts → signature.d.ts} +1 -1
- package/dist/server/implementation/mutations/signature.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{verifierSignature.js → signature.js} +4 -3
- package/dist/server/implementation/mutations/signature.js.map +1 -0
- package/dist/server/implementation/mutations/{signIn.d.ts → signin.d.ts} +1 -1
- package/dist/server/implementation/mutations/{signIn.d.ts.map → signin.d.ts.map} +1 -1
- package/dist/server/implementation/mutations/{signIn.js → signin.js} +2 -2
- package/dist/server/implementation/mutations/{signIn.js.map → signin.js.map} +1 -1
- package/dist/server/implementation/mutations/{signOut.d.ts → signout.d.ts} +1 -1
- package/dist/server/implementation/mutations/{signOut.d.ts.map → signout.d.ts.map} +1 -1
- package/dist/server/implementation/mutations/{signOut.js → signout.js} +2 -2
- package/dist/server/implementation/mutations/{signOut.js.map → signout.js.map} +1 -1
- package/dist/server/implementation/mutations/{storeRef.d.ts → store.d.ts} +1 -1
- package/dist/server/implementation/mutations/store.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{storeRef.js → store.js} +1 -1
- package/dist/server/implementation/mutations/store.js.map +1 -0
- package/dist/server/implementation/mutations/verifier.js +1 -1
- package/dist/server/implementation/mutations/verifier.js.map +1 -1
- package/dist/server/implementation/mutations/{verifyCodeAndSignIn.d.ts → verify.d.ts} +1 -1
- package/dist/server/implementation/mutations/verify.d.ts.map +1 -0
- package/dist/server/implementation/mutations/{verifyCodeAndSignIn.js → verify.js} +3 -3
- package/dist/server/implementation/mutations/verify.js.map +1 -0
- package/dist/server/implementation/passkey.d.ts.map +1 -1
- package/dist/server/implementation/passkey.js +47 -55
- package/dist/server/implementation/passkey.js.map +1 -1
- package/dist/server/implementation/provider.d.ts.map +1 -1
- package/dist/server/implementation/provider.js +5 -4
- package/dist/server/implementation/provider.js.map +1 -1
- package/dist/server/implementation/{rateLimit.d.ts → ratelimit.d.ts} +1 -1
- package/dist/server/implementation/{rateLimit.d.ts.map → ratelimit.d.ts.map} +1 -1
- package/dist/server/implementation/{rateLimit.js → ratelimit.js} +1 -1
- package/dist/server/implementation/{rateLimit.js.map → ratelimit.js.map} +1 -1
- package/dist/server/implementation/redirects.d.ts.map +1 -1
- package/dist/server/implementation/redirects.js +2 -1
- package/dist/server/implementation/redirects.js.map +1 -1
- package/dist/server/implementation/{refreshTokens.d.ts → refresh.d.ts} +1 -1
- package/dist/server/implementation/refresh.d.ts.map +1 -0
- package/dist/server/implementation/{refreshTokens.js → refresh.js} +3 -2
- package/dist/server/implementation/refresh.js.map +1 -0
- package/dist/server/implementation/sessions.js +1 -1
- package/dist/server/implementation/sessions.js.map +1 -1
- package/dist/server/implementation/{signIn.d.ts → signin.d.ts} +1 -1
- package/dist/server/implementation/{signIn.d.ts.map → signin.d.ts.map} +1 -1
- package/dist/server/implementation/{signIn.js → signin.js} +12 -8
- package/dist/server/implementation/signin.js.map +1 -0
- package/dist/server/implementation/totp.d.ts.map +1 -1
- package/dist/server/implementation/totp.js +29 -29
- package/dist/server/implementation/totp.js.map +1 -1
- package/dist/server/implementation/types.d.ts +131 -1
- package/dist/server/implementation/types.d.ts.map +1 -1
- package/dist/server/implementation/types.js +65 -1
- package/dist/server/implementation/types.js.map +1 -1
- package/dist/server/implementation/users.d.ts.map +1 -1
- package/dist/server/implementation/users.js +3 -2
- package/dist/server/implementation/users.js.map +1 -1
- package/dist/server/index.d.ts +131 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +117 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/oauth/{authorizationUrl.d.ts → authorization.d.ts} +1 -1
- package/dist/server/oauth/authorization.d.ts.map +1 -0
- package/dist/server/oauth/{authorizationUrl.js → authorization.js} +4 -3
- package/dist/server/oauth/authorization.js.map +1 -0
- package/dist/server/oauth/callback.d.ts.map +1 -1
- package/dist/server/oauth/callback.js +7 -6
- package/dist/server/oauth/callback.js.map +1 -1
- package/dist/server/oauth/checks.d.ts.map +1 -1
- package/dist/server/oauth/checks.js +2 -1
- package/dist/server/oauth/checks.js.map +1 -1
- package/dist/server/oauth/{convexAuth.d.ts → helpers.d.ts} +1 -1
- package/dist/server/oauth/helpers.d.ts.map +1 -0
- package/dist/server/oauth/{convexAuth.js → helpers.js} +6 -5
- package/dist/server/oauth/helpers.js.map +1 -0
- package/dist/server/oauth/lib/utils/{customFetch.d.ts → fetch.d.ts} +1 -1
- package/dist/server/oauth/lib/utils/fetch.d.ts.map +1 -0
- package/dist/server/oauth/lib/utils/{customFetch.js → fetch.js} +1 -1
- package/dist/server/oauth/lib/utils/fetch.js.map +1 -0
- package/dist/server/{provider_utils.d.ts → providers.d.ts} +1 -1
- package/dist/server/providers.d.ts.map +1 -0
- package/dist/server/{provider_utils.js → providers.js} +1 -1
- package/dist/server/providers.js.map +1 -0
- package/dist/server/{email-templates.d.ts → templates.d.ts} +8 -1
- package/dist/server/templates.d.ts.map +1 -0
- package/dist/server/{portal-email.js → templates.js} +74 -3
- package/dist/server/templates.js.map +1 -0
- package/dist/server/types.d.ts +88 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils.d.ts.map +1 -1
- package/dist/server/utils.js +2 -1
- package/dist/server/utils.js.map +1 -1
- package/dist/server/version.d.ts +1 -1
- package/dist/server/version.d.ts.map +1 -1
- package/dist/server/version.js +1 -1
- package/dist/server/version.js.map +1 -1
- package/package.json +5 -1
- package/src/cli/index.ts +5 -5
- package/src/cli/{portal-link.ts → link.ts} +1 -1
- package/src/cli/utils.ts +1 -1
- package/src/client/index.ts +102 -17
- package/src/component/_generated/api.ts +2 -2
- package/src/component/_generated/component.ts +1 -1
- package/src/component/{portalBridge.ts → bridge.ts} +2 -2
- package/src/component/index.ts +10 -2
- package/src/component/public.ts +25 -4
- package/src/component/schema.ts +4 -1
- package/src/providers/anonymous.ts +3 -0
- package/src/providers/credentials.ts +3 -0
- package/src/providers/email.ts +3 -0
- package/src/providers/passkey.ts +8 -1
- package/src/providers/password.ts +3 -0
- package/src/providers/phone.ts +3 -0
- package/src/providers/totp.ts +9 -0
- package/src/server/auth.ts +969 -0
- package/src/server/errors.ts +275 -0
- package/src/server/implementation/index.ts +370 -88
- package/src/server/implementation/{apiKey.ts → keys.ts} +7 -6
- package/src/server/implementation/mutations/{modifyAccount.ts → account.ts} +3 -4
- package/src/server/implementation/mutations/{createVerificationCode.ts → code.ts} +1 -1
- package/src/server/implementation/mutations/index.ts +22 -22
- package/src/server/implementation/mutations/{invalidateSessions.ts → invalidate.ts} +1 -1
- package/src/server/implementation/mutations/{userOAuth.ts → oauth.ts} +3 -2
- package/src/server/implementation/mutations/{refreshSession.ts → refresh.ts} +2 -2
- package/src/server/implementation/mutations/{createAccountFromCredentials.ts → register.ts} +3 -2
- package/src/server/implementation/mutations/{retrieveAccountWithCredentials.ts → retrieve.ts} +2 -2
- package/src/server/implementation/mutations/{verifierSignature.ts → signature.ts} +3 -2
- package/src/server/implementation/mutations/{signIn.ts → signin.ts} +1 -1
- package/src/server/implementation/mutations/{signOut.ts → signout.ts} +1 -1
- package/src/server/implementation/mutations/verifier.ts +1 -1
- package/src/server/implementation/mutations/{verifyCodeAndSignIn.ts → verify.ts} +2 -2
- package/src/server/implementation/passkey.ts +86 -116
- package/src/server/implementation/provider.ts +5 -8
- package/src/server/implementation/redirects.ts +2 -3
- package/src/server/implementation/{refreshTokens.ts → refresh.ts} +2 -1
- package/src/server/implementation/sessions.ts +1 -1
- package/src/server/implementation/{signIn.ts → signin.ts} +13 -11
- package/src/server/implementation/totp.ts +60 -84
- package/src/server/implementation/types.ts +316 -1
- package/src/server/implementation/users.ts +4 -7
- package/src/server/index.ts +142 -3
- package/src/server/oauth/{authorizationUrl.ts → authorization.ts} +3 -2
- package/src/server/oauth/callback.ts +7 -6
- package/src/server/oauth/checks.ts +3 -1
- package/src/server/oauth/{convexAuth.ts → helpers.ts} +8 -5
- package/src/server/{portal-email.ts → templates.ts} +78 -2
- package/src/server/types.ts +133 -4
- package/src/server/utils.ts +3 -1
- package/src/server/version.ts +1 -1
- package/dist/component/portalBridge.d.ts.map +0 -1
- package/dist/component/portalBridge.js.map +0 -1
- package/dist/server/convex-auth.d.ts.map +0 -1
- package/dist/server/convex-auth.js.map +0 -1
- package/dist/server/convex_types.d.ts +0 -17
- package/dist/server/convex_types.d.ts.map +0 -1
- package/dist/server/convex_types.js +0 -2
- package/dist/server/convex_types.js.map +0 -1
- package/dist/server/email-templates.d.ts.map +0 -1
- package/dist/server/email-templates.js +0 -74
- package/dist/server/email-templates.js.map +0 -1
- package/dist/server/implementation/apiKey.d.ts.map +0 -1
- package/dist/server/implementation/apiKey.js.map +0 -1
- package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +0 -1
- package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +0 -1
- package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +0 -1
- package/dist/server/implementation/mutations/createVerificationCode.js.map +0 -1
- package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +0 -1
- package/dist/server/implementation/mutations/invalidateSessions.js.map +0 -1
- package/dist/server/implementation/mutations/modifyAccount.d.ts.map +0 -1
- package/dist/server/implementation/mutations/modifyAccount.js.map +0 -1
- package/dist/server/implementation/mutations/refreshSession.d.ts.map +0 -1
- package/dist/server/implementation/mutations/refreshSession.js.map +0 -1
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +0 -1
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +0 -1
- package/dist/server/implementation/mutations/storeRef.d.ts.map +0 -1
- package/dist/server/implementation/mutations/storeRef.js.map +0 -1
- package/dist/server/implementation/mutations/userOAuth.d.ts.map +0 -1
- package/dist/server/implementation/mutations/userOAuth.js.map +0 -1
- package/dist/server/implementation/mutations/verifierSignature.d.ts.map +0 -1
- package/dist/server/implementation/mutations/verifierSignature.js.map +0 -1
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +0 -1
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +0 -1
- package/dist/server/implementation/refreshTokens.d.ts.map +0 -1
- package/dist/server/implementation/refreshTokens.js.map +0 -1
- package/dist/server/implementation/signIn.js.map +0 -1
- package/dist/server/oauth/authorizationUrl.d.ts.map +0 -1
- package/dist/server/oauth/authorizationUrl.js.map +0 -1
- package/dist/server/oauth/convexAuth.d.ts.map +0 -1
- package/dist/server/oauth/convexAuth.js.map +0 -1
- package/dist/server/oauth/lib/utils/customFetch.d.ts.map +0 -1
- package/dist/server/oauth/lib/utils/customFetch.js.map +0 -1
- package/dist/server/portal-email.d.ts +0 -19
- package/dist/server/portal-email.d.ts.map +0 -1
- package/dist/server/portal-email.js.map +0 -1
- package/dist/server/provider_utils.d.ts.map +0 -1
- package/dist/server/provider_utils.js.map +0 -1
- package/src/server/convex-auth.ts +0 -602
- package/src/server/convex_types.ts +0 -55
- package/src/server/email-templates.ts +0 -77
- /package/src/cli/{generateKeys.ts → keys.ts} +0 -0
- /package/src/cli/{portal-upload.ts → upload.ts} +0 -0
- /package/src/server/implementation/mutations/{storeRef.ts → store.ts} +0 -0
- /package/src/server/implementation/{rateLimit.ts → ratelimit.ts} +0 -0
- /package/src/server/oauth/lib/utils/{customFetch.ts → fetch.ts} +0 -0
- /package/src/server/{provider_utils.ts → providers.ts} +0 -0
|
@@ -9,20 +9,28 @@ import {
|
|
|
9
9
|
internalMutationGeneric,
|
|
10
10
|
} from "convex/server";
|
|
11
11
|
import { ConvexError, GenericId, v } from "convex/values";
|
|
12
|
+
import { throwAuthError, isAuthError } from "../errors.js";
|
|
12
13
|
import { parse as parseCookies, serialize as serializeCookie } from "cookie";
|
|
13
14
|
import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
|
|
14
|
-
import { FunctionReferenceFromExport } from "../
|
|
15
|
+
import { FunctionReferenceFromExport } from "../types.js";
|
|
15
16
|
import {
|
|
16
17
|
configDefaults,
|
|
17
18
|
listAvailableProviders,
|
|
18
19
|
materializeProvider,
|
|
19
|
-
} from "../
|
|
20
|
+
} from "../providers.js";
|
|
20
21
|
import {
|
|
21
22
|
AuthProviderConfig,
|
|
22
23
|
ConvexAuthConfig,
|
|
24
|
+
CorsConfig,
|
|
25
|
+
HttpKeyContext,
|
|
23
26
|
} from "../types.js";
|
|
24
27
|
import { requireEnv } from "../utils.js";
|
|
25
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
ActionCtx,
|
|
30
|
+
MutationCtx,
|
|
31
|
+
Tokens,
|
|
32
|
+
KeyDoc,
|
|
33
|
+
} from "./types.js";
|
|
26
34
|
export { Doc, Tokens } from "./types.js";
|
|
27
35
|
import {
|
|
28
36
|
LOG_LEVELS,
|
|
@@ -42,7 +50,7 @@ import {
|
|
|
42
50
|
storeArgs,
|
|
43
51
|
storeImpl,
|
|
44
52
|
} from "./mutations/index.js";
|
|
45
|
-
import { signInImpl } from "./
|
|
53
|
+
import { signInImpl } from "./signin.js";
|
|
46
54
|
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
|
|
47
55
|
import {
|
|
48
56
|
generateApiKey,
|
|
@@ -50,12 +58,12 @@ import {
|
|
|
50
58
|
buildScopeChecker,
|
|
51
59
|
validateScopes,
|
|
52
60
|
checkKeyRateLimit,
|
|
53
|
-
} from "./
|
|
54
|
-
import { getAuthorizationUrl } from "../oauth/
|
|
61
|
+
} from "./keys.js";
|
|
62
|
+
import { getAuthorizationUrl } from "../oauth/authorization.js";
|
|
55
63
|
import {
|
|
56
64
|
defaultCookiesOptions,
|
|
57
65
|
oAuthConfigToInternalProvider,
|
|
58
|
-
} from "../oauth/
|
|
66
|
+
} from "../oauth/helpers.js";
|
|
59
67
|
import { handleOAuth } from "../oauth/callback.js";
|
|
60
68
|
|
|
61
69
|
/**
|
|
@@ -95,7 +103,7 @@ export type SignOutAction = FunctionReferenceFromExport<
|
|
|
95
103
|
* `convex/auth.ts` file.
|
|
96
104
|
*/
|
|
97
105
|
export function Auth(config_: ConvexAuthConfig) {
|
|
98
|
-
const config = configDefaults(config_
|
|
106
|
+
const config = configDefaults(config_);
|
|
99
107
|
const hasOAuth = config.providers.some(
|
|
100
108
|
(provider) => provider.type === "oauth" || provider.type === "oidc",
|
|
101
109
|
);
|
|
@@ -113,11 +121,11 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
113
121
|
) => {
|
|
114
122
|
const provider = getProvider(id, allowExtraProviders);
|
|
115
123
|
if (provider === undefined) {
|
|
116
|
-
const
|
|
124
|
+
const detail =
|
|
117
125
|
`Provider \`${id}\` is not configured, ` +
|
|
118
126
|
`available providers are ${listAvailableProviders(config, allowExtraProviders)}.`;
|
|
119
|
-
logWithLevel(LOG_LEVELS.ERROR,
|
|
120
|
-
|
|
127
|
+
logWithLevel(LOG_LEVELS.ERROR, detail);
|
|
128
|
+
throwAuthError("PROVIDER_NOT_CONFIGURED", detail, { provider: id });
|
|
121
129
|
}
|
|
122
130
|
return provider;
|
|
123
131
|
};
|
|
@@ -146,6 +154,9 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
146
154
|
/**
|
|
147
155
|
* Get the current user's ID from the auth context, or `null` if
|
|
148
156
|
* not signed in.
|
|
157
|
+
*
|
|
158
|
+
* @param ctx - Any Convex context with an `auth` field (query, mutation, or action).
|
|
159
|
+
* @returns The user's `Id<"user">`, or `null` when unauthenticated.
|
|
149
160
|
*/
|
|
150
161
|
current: async (ctx: { auth: Auth }) => {
|
|
151
162
|
const identity = await ctx.auth.getUserIdentity();
|
|
@@ -158,24 +169,35 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
158
169
|
/**
|
|
159
170
|
* Get the current user's ID, or throw if not signed in.
|
|
160
171
|
* Use this when authentication is required.
|
|
172
|
+
*
|
|
173
|
+
* @param ctx - Any Convex context with an `auth` field.
|
|
174
|
+
* @returns The user's `Id<"user">`.
|
|
175
|
+
* @throws `ConvexError` with code `NOT_SIGNED_IN` when unauthenticated.
|
|
161
176
|
*/
|
|
162
177
|
require: async (ctx: { auth: Auth }) => {
|
|
163
178
|
const identity = await ctx.auth.getUserIdentity();
|
|
164
179
|
if (identity === null) {
|
|
165
|
-
|
|
180
|
+
throwAuthError("NOT_SIGNED_IN");
|
|
166
181
|
}
|
|
167
182
|
const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
|
|
168
183
|
return userId as GenericId<"user">;
|
|
169
184
|
},
|
|
170
185
|
/**
|
|
171
186
|
* Retrieve a user document by their ID.
|
|
187
|
+
*
|
|
188
|
+
* @param ctx - Convex context with `runQuery`.
|
|
189
|
+
* @param userId - The user document ID.
|
|
190
|
+
* @returns The user document, or `null` if not found.
|
|
172
191
|
*/
|
|
173
192
|
get: async (ctx: ComponentReadCtx, userId: string) => {
|
|
174
193
|
return await ctx.runQuery(config.component.public.userGetById, { userId });
|
|
175
194
|
},
|
|
176
195
|
/**
|
|
177
196
|
* Get the currently signed-in user's document, or `null` if not
|
|
178
|
-
* signed in. Convenience
|
|
197
|
+
* signed in. Convenience combining `current()` + `get()`.
|
|
198
|
+
*
|
|
199
|
+
* @param ctx - Convex context with `auth` and `runQuery`.
|
|
200
|
+
* @returns The user document, or `null` when unauthenticated.
|
|
179
201
|
*/
|
|
180
202
|
viewer: async (ctx: ComponentAuthReadCtx) => {
|
|
181
203
|
const userId = await auth.user.current(ctx);
|
|
@@ -184,6 +206,23 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
184
206
|
}
|
|
185
207
|
return await ctx.runQuery(config.component.public.userGetById, { userId });
|
|
186
208
|
},
|
|
209
|
+
/**
|
|
210
|
+
* Update a user document with partial data.
|
|
211
|
+
*
|
|
212
|
+
* @param ctx - Convex context with `runMutation`.
|
|
213
|
+
* @param userId - The user document ID.
|
|
214
|
+
* @param data - Partial data to merge into the user document.
|
|
215
|
+
*/
|
|
216
|
+
patch: async (
|
|
217
|
+
ctx: ComponentCtx,
|
|
218
|
+
userId: string,
|
|
219
|
+
data: Record<string, unknown>,
|
|
220
|
+
) => {
|
|
221
|
+
await ctx.runMutation(config.component.public.userPatch, {
|
|
222
|
+
userId,
|
|
223
|
+
data,
|
|
224
|
+
});
|
|
225
|
+
},
|
|
187
226
|
/**
|
|
188
227
|
* Query a user's group memberships.
|
|
189
228
|
*/
|
|
@@ -215,6 +254,9 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
215
254
|
/**
|
|
216
255
|
* Get the current session ID from the auth context, or `null` if
|
|
217
256
|
* not signed in.
|
|
257
|
+
*
|
|
258
|
+
* @param ctx - Any Convex context with an `auth` field.
|
|
259
|
+
* @returns The session's `Id<"session">`, or `null` when unauthenticated.
|
|
218
260
|
*/
|
|
219
261
|
current: async (ctx: { auth: Auth }) => {
|
|
220
262
|
const identity = await ctx.auth.getUserIdentity();
|
|
@@ -226,6 +268,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
226
268
|
},
|
|
227
269
|
/**
|
|
228
270
|
* Invalidate sessions for a user, optionally preserving specific sessions.
|
|
271
|
+
*
|
|
272
|
+
* @param ctx - Convex action context.
|
|
273
|
+
* @param args.userId - The user whose sessions to invalidate.
|
|
274
|
+
* @param args.except - Session IDs to preserve (e.g. the current session).
|
|
229
275
|
*/
|
|
230
276
|
invalidate: async <DataModel extends GenericDataModel>(
|
|
231
277
|
ctx: GenericActionCtx<DataModel>,
|
|
@@ -241,16 +287,25 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
241
287
|
account: {
|
|
242
288
|
/**
|
|
243
289
|
* Create an account and user for a credentials provider.
|
|
290
|
+
*
|
|
291
|
+
* @param ctx - Convex action context.
|
|
292
|
+
* @param args - Provider ID, account credentials, profile data, and link flags.
|
|
293
|
+
* @returns `{ account, user }` — the created account and user documents.
|
|
244
294
|
*/
|
|
245
295
|
create: async <DataModel extends GenericDataModel>(
|
|
246
296
|
ctx: GenericActionCtx<DataModel>,
|
|
247
297
|
args: CreateAccountArgs,
|
|
248
298
|
) => {
|
|
249
299
|
const actionCtx = ctx as unknown as ActionCtx;
|
|
250
|
-
return await callCreateAccountFromCredentials(actionCtx, args
|
|
300
|
+
return await callCreateAccountFromCredentials(actionCtx, args);
|
|
251
301
|
},
|
|
252
302
|
/**
|
|
253
303
|
* Retrieve an account and user for a credentials provider.
|
|
304
|
+
*
|
|
305
|
+
* @param ctx - Convex action context.
|
|
306
|
+
* @param args - Provider ID and account credentials (id, optional secret).
|
|
307
|
+
* @returns `{ account, user }` — the matched account and user documents.
|
|
308
|
+
* @throws `ConvexError` with code `ACCOUNT_NOT_FOUND` when no match exists.
|
|
254
309
|
*/
|
|
255
310
|
get: async <DataModel extends GenericDataModel>(
|
|
256
311
|
ctx: GenericActionCtx<DataModel>,
|
|
@@ -259,12 +314,15 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
259
314
|
const actionCtx = ctx as unknown as ActionCtx;
|
|
260
315
|
const result = await callRetreiveAccountWithCredentials(actionCtx, args);
|
|
261
316
|
if (typeof result === "string") {
|
|
262
|
-
|
|
317
|
+
throwAuthError("ACCOUNT_NOT_FOUND", result);
|
|
263
318
|
}
|
|
264
319
|
return result;
|
|
265
320
|
},
|
|
266
321
|
/**
|
|
267
|
-
* Update credentials for an existing account.
|
|
322
|
+
* Update credentials (secret) for an existing account.
|
|
323
|
+
*
|
|
324
|
+
* @param ctx - Convex action context.
|
|
325
|
+
* @param args - Provider ID and new account credentials (id + secret).
|
|
268
326
|
*/
|
|
269
327
|
updateCredentials: async <DataModel extends GenericDataModel>(
|
|
270
328
|
ctx: GenericActionCtx<DataModel>,
|
|
@@ -277,6 +335,11 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
277
335
|
provider: {
|
|
278
336
|
/**
|
|
279
337
|
* Sign in via another provider, typically from a credentials flow.
|
|
338
|
+
*
|
|
339
|
+
* @param ctx - Convex action context.
|
|
340
|
+
* @param provider - The provider config to sign in with.
|
|
341
|
+
* @param args - Optional account ID and params.
|
|
342
|
+
* @returns `{ userId, sessionId }` on success, or `null`.
|
|
280
343
|
*/
|
|
281
344
|
signIn: async <DataModel extends GenericDataModel>(
|
|
282
345
|
ctx: GenericActionCtx<DataModel>,
|
|
@@ -289,7 +352,8 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
289
352
|
const result = await signInImpl(
|
|
290
353
|
enrichCtx(ctx),
|
|
291
354
|
materializeProvider(provider),
|
|
292
|
-
|
|
355
|
+
// params type widened: Record<string, unknown> → Record<string, any>
|
|
356
|
+
args as { accountId?: GenericId<"account">; params?: Record<string, any> },
|
|
293
357
|
{
|
|
294
358
|
generateTokens: false,
|
|
295
359
|
allowExtraProviders: true,
|
|
@@ -326,6 +390,7 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
326
390
|
data: {
|
|
327
391
|
name: string;
|
|
328
392
|
slug?: string;
|
|
393
|
+
type?: string;
|
|
329
394
|
parentGroupId?: string;
|
|
330
395
|
extend?: Record<string, unknown>;
|
|
331
396
|
},
|
|
@@ -345,8 +410,12 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
345
410
|
* List groups. When `parentGroupId` is provided, returns children of
|
|
346
411
|
* that group. When omitted, returns root-level groups (no parent).
|
|
347
412
|
*/
|
|
348
|
-
list: async (
|
|
413
|
+
list: async (
|
|
414
|
+
ctx: ComponentReadCtx,
|
|
415
|
+
opts?: { type?: string; parentGroupId?: string },
|
|
416
|
+
) => {
|
|
349
417
|
return await ctx.runQuery(config.component.public.groupList, {
|
|
418
|
+
type: opts?.type,
|
|
350
419
|
parentGroupId: opts?.parentGroupId,
|
|
351
420
|
});
|
|
352
421
|
},
|
|
@@ -514,15 +583,17 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
514
583
|
* the timestamp. If the invite has a group, the caller is responsible
|
|
515
584
|
* for creating the member record via `auth.group.member.add` in the
|
|
516
585
|
* same Convex mutation for transactional safety.
|
|
517
|
-
*
|
|
518
|
-
* @throws ConvexError with code `INVITE_NOT_FOUND` when the invite does
|
|
519
|
-
* not exist.
|
|
520
|
-
* @throws ConvexError with code `INVITE_NOT_PENDING` when the invite is
|
|
521
|
-
* not in `pending` status.
|
|
522
586
|
*
|
|
587
|
+
* @param ctx - Convex context with `runMutation`.
|
|
588
|
+
* @param inviteId - The invite document ID.
|
|
589
|
+
* @param acceptedByUserId - User accepting the invite (recorded for audit).
|
|
590
|
+
* @throws `ConvexError` with code `INVITE_NOT_FOUND` when the invite does not exist.
|
|
591
|
+
* @throws `ConvexError` with code `INVITE_NOT_PENDING` when the invite is not in `pending` status.
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
523
594
|
* ```ts
|
|
524
|
-
|
|
525
|
-
|
|
595
|
+
* export const acceptInvite = mutation({
|
|
596
|
+
* args: { inviteId: v.string() },
|
|
526
597
|
* handler: async (ctx, { inviteId }) => {
|
|
527
598
|
* const userId = await auth.user.require(ctx);
|
|
528
599
|
* const invite = await auth.invite.get(ctx, inviteId);
|
|
@@ -546,14 +617,14 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
546
617
|
...(acceptedByUserId ? { acceptedByUserId } : {}),
|
|
547
618
|
});
|
|
548
619
|
},
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
620
|
+
/**
|
|
621
|
+
* Revoke a pending invitation.
|
|
622
|
+
*
|
|
623
|
+
* @param ctx - Convex context with `runMutation`.
|
|
624
|
+
* @param inviteId - The invite document ID.
|
|
625
|
+
* @throws `ConvexError` with code `INVITE_NOT_FOUND` when the invite does not exist.
|
|
626
|
+
* @throws `ConvexError` with code `INVITE_NOT_PENDING` when the invite is not in `pending` status.
|
|
627
|
+
*/
|
|
557
628
|
revoke: async (ctx: ComponentCtx, inviteId: string) => {
|
|
558
629
|
await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
|
|
559
630
|
},
|
|
@@ -685,10 +756,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
685
756
|
|
|
686
757
|
const { raw, hashedKey, displayPrefix } = await generateApiKey(prefix);
|
|
687
758
|
|
|
688
|
-
const keyId = await ctx.runMutation(
|
|
759
|
+
const keyId = (await ctx.runMutation(
|
|
689
760
|
config.component.public.keyInsert,
|
|
690
761
|
{
|
|
691
|
-
userId: opts.userId
|
|
762
|
+
userId: opts.userId,
|
|
692
763
|
prefix: displayPrefix,
|
|
693
764
|
hashedKey,
|
|
694
765
|
name: opts.name,
|
|
@@ -696,9 +767,9 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
696
767
|
rateLimit: opts.rateLimit ?? config.apiKeys?.defaultRateLimit,
|
|
697
768
|
expiresAt: opts.expiresAt,
|
|
698
769
|
},
|
|
699
|
-
);
|
|
770
|
+
)) as string;
|
|
700
771
|
|
|
701
|
-
return { keyId
|
|
772
|
+
return { keyId, raw };
|
|
702
773
|
},
|
|
703
774
|
|
|
704
775
|
/**
|
|
@@ -719,22 +790,22 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
719
790
|
}> => {
|
|
720
791
|
const hashedKey = await hashApiKey(rawKey);
|
|
721
792
|
|
|
722
|
-
const key = await ctx.runQuery(
|
|
793
|
+
const key = (await ctx.runQuery(
|
|
723
794
|
config.component.public.keyGetByHashedKey,
|
|
724
795
|
{ hashedKey },
|
|
725
|
-
);
|
|
796
|
+
)) as KeyDoc | null;
|
|
726
797
|
if (!key) {
|
|
727
|
-
|
|
798
|
+
throwAuthError("INVALID_API_KEY");
|
|
728
799
|
}
|
|
729
800
|
if (key.revoked) {
|
|
730
|
-
|
|
801
|
+
throwAuthError("API_KEY_REVOKED");
|
|
731
802
|
}
|
|
732
803
|
if (key.expiresAt && key.expiresAt < Date.now()) {
|
|
733
|
-
|
|
804
|
+
throwAuthError("API_KEY_EXPIRED");
|
|
734
805
|
}
|
|
735
806
|
|
|
736
807
|
// Check per-key rate limit
|
|
737
|
-
const patchData: Record<string,
|
|
808
|
+
const patchData: Record<string, unknown> = { lastUsedAt: Date.now() };
|
|
738
809
|
|
|
739
810
|
if (key.rateLimit) {
|
|
740
811
|
const { limited, newState } = checkKeyRateLimit(
|
|
@@ -742,7 +813,7 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
742
813
|
key.rateLimitState ?? undefined,
|
|
743
814
|
);
|
|
744
815
|
if (limited) {
|
|
745
|
-
|
|
816
|
+
throwAuthError("API_KEY_RATE_LIMITED");
|
|
746
817
|
}
|
|
747
818
|
patchData.rateLimitState = newState;
|
|
748
819
|
}
|
|
@@ -754,8 +825,8 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
754
825
|
});
|
|
755
826
|
|
|
756
827
|
return {
|
|
757
|
-
userId: key.userId
|
|
758
|
-
keyId: key._id
|
|
828
|
+
userId: key.userId,
|
|
829
|
+
keyId: key._id,
|
|
759
830
|
scopes: buildScopeChecker(key.scopes),
|
|
760
831
|
};
|
|
761
832
|
},
|
|
@@ -764,22 +835,22 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
764
835
|
* List all API keys for a user.
|
|
765
836
|
* Never includes the raw key — only the display prefix.
|
|
766
837
|
*/
|
|
767
|
-
list: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
|
|
768
|
-
return await ctx.runQuery(
|
|
838
|
+
list: async (ctx: ComponentReadCtx, opts: { userId: string }): Promise<KeyDoc[]> => {
|
|
839
|
+
return (await ctx.runQuery(
|
|
769
840
|
config.component.public.keyListByUserId,
|
|
770
|
-
{ userId: opts.userId
|
|
771
|
-
);
|
|
841
|
+
{ userId: opts.userId },
|
|
842
|
+
)) as KeyDoc[];
|
|
772
843
|
},
|
|
773
844
|
|
|
774
845
|
/**
|
|
775
846
|
* Get a single API key by its document ID.
|
|
776
847
|
* Returns `null` if not found.
|
|
777
848
|
*/
|
|
778
|
-
get: async (ctx: ComponentReadCtx, keyId: string) => {
|
|
779
|
-
return await ctx.runQuery(
|
|
849
|
+
get: async (ctx: ComponentReadCtx, keyId: string): Promise<KeyDoc | null> => {
|
|
850
|
+
return (await ctx.runQuery(
|
|
780
851
|
config.component.public.keyGetById,
|
|
781
|
-
{ keyId
|
|
782
|
-
);
|
|
852
|
+
{ keyId },
|
|
853
|
+
)) as KeyDoc | null;
|
|
783
854
|
},
|
|
784
855
|
|
|
785
856
|
/**
|
|
@@ -798,7 +869,7 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
798
869
|
validateScopes(data.scopes, config.apiKeys?.scopes);
|
|
799
870
|
}
|
|
800
871
|
await ctx.runMutation(config.component.public.keyPatch, {
|
|
801
|
-
keyId
|
|
872
|
+
keyId,
|
|
802
873
|
data,
|
|
803
874
|
});
|
|
804
875
|
},
|
|
@@ -809,7 +880,7 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
809
880
|
*/
|
|
810
881
|
revoke: async (ctx: ComponentCtx, keyId: string) => {
|
|
811
882
|
await ctx.runMutation(config.component.public.keyPatch, {
|
|
812
|
-
keyId
|
|
883
|
+
keyId,
|
|
813
884
|
data: { revoked: true },
|
|
814
885
|
});
|
|
815
886
|
},
|
|
@@ -819,37 +890,41 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
819
890
|
*/
|
|
820
891
|
remove: async (ctx: ComponentCtx, keyId: string) => {
|
|
821
892
|
await ctx.runMutation(config.component.public.keyDelete, {
|
|
822
|
-
keyId
|
|
893
|
+
keyId,
|
|
823
894
|
});
|
|
824
895
|
},
|
|
825
896
|
},
|
|
826
897
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
* ```ts
|
|
830
|
-
* import { httpRouter } from "convex/server";
|
|
831
|
-
* import { auth } from "./auth.js";
|
|
832
|
-
*
|
|
833
|
-
* const http = httpRouter();
|
|
834
|
-
*
|
|
835
|
-
* auth.addHttpRoutes(http);
|
|
836
|
-
*
|
|
837
|
-
* export default http;
|
|
838
|
-
* ```
|
|
839
|
-
*
|
|
840
|
-
* The following routes are handled always:
|
|
841
|
-
*
|
|
842
|
-
* - `/.well-known/openid-configuration`
|
|
843
|
-
* - `/.well-known/jwks.json`
|
|
844
|
-
*
|
|
845
|
-
* The following routes are handled if OAuth is configured:
|
|
846
|
-
*
|
|
847
|
-
* - `/api/auth/signin/*`
|
|
848
|
-
* - `/api/auth/callback/*`
|
|
849
|
-
*
|
|
850
|
-
* @param http your HTTP router
|
|
898
|
+
* HTTP namespace — route registration and Bearer-authenticated endpoints.
|
|
851
899
|
*/
|
|
852
|
-
|
|
900
|
+
http: {
|
|
901
|
+
/**
|
|
902
|
+
* Register core HTTP routes for JWT verification and OAuth sign-in.
|
|
903
|
+
*
|
|
904
|
+
* ```ts
|
|
905
|
+
* import { httpRouter } from "convex/server";
|
|
906
|
+
* import { auth } from "./auth.js";
|
|
907
|
+
*
|
|
908
|
+
* const http = httpRouter();
|
|
909
|
+
*
|
|
910
|
+
* auth.http.add(http);
|
|
911
|
+
*
|
|
912
|
+
* export default http;
|
|
913
|
+
* ```
|
|
914
|
+
*
|
|
915
|
+
* The following routes are handled always:
|
|
916
|
+
*
|
|
917
|
+
* - `/.well-known/openid-configuration`
|
|
918
|
+
* - `/.well-known/jwks.json`
|
|
919
|
+
*
|
|
920
|
+
* The following routes are handled if OAuth is configured:
|
|
921
|
+
*
|
|
922
|
+
* - `/api/auth/signin/*`
|
|
923
|
+
* - `/api/auth/callback/*`
|
|
924
|
+
*
|
|
925
|
+
* @param http your HTTP router
|
|
926
|
+
*/
|
|
927
|
+
add: (http: HttpRouter) => {
|
|
853
928
|
http.route({
|
|
854
929
|
path: "/.well-known/openid-configuration",
|
|
855
930
|
method: "GET",
|
|
@@ -899,11 +974,11 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
899
974
|
const pathParts = url.pathname.split("/");
|
|
900
975
|
const providerId = pathParts.at(-1)!;
|
|
901
976
|
if (providerId === null) {
|
|
902
|
-
|
|
977
|
+
throwAuthError("OAUTH_MISSING_PROVIDER");
|
|
903
978
|
}
|
|
904
979
|
const verifier = url.searchParams.get("code");
|
|
905
980
|
if (verifier === null) {
|
|
906
|
-
|
|
981
|
+
throwAuthError("OAUTH_MISSING_VERIFIER");
|
|
907
982
|
}
|
|
908
983
|
const provider = getProviderOrThrow(
|
|
909
984
|
providerId,
|
|
@@ -992,8 +1067,10 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
992
1067
|
);
|
|
993
1068
|
|
|
994
1069
|
if (typeof id !== "string") {
|
|
995
|
-
|
|
1070
|
+
throwAuthError(
|
|
1071
|
+
"OAUTH_INVALID_PROFILE",
|
|
996
1072
|
`The profile method of the ${providerId} config must return a string ID`,
|
|
1073
|
+
{ provider: providerId },
|
|
997
1074
|
);
|
|
998
1075
|
}
|
|
999
1076
|
|
|
@@ -1035,6 +1112,203 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
1035
1112
|
});
|
|
1036
1113
|
}
|
|
1037
1114
|
},
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Wrap an HTTP action handler with Bearer token authentication.
|
|
1118
|
+
*
|
|
1119
|
+
* Extracts the `Authorization: Bearer <key>` header, verifies the
|
|
1120
|
+
* API key via `auth.key.verify()`, and injects `ctx.key` with the
|
|
1121
|
+
* verified key info. Returns structured JSON error responses for
|
|
1122
|
+
* missing/invalid/revoked/expired/rate-limited keys.
|
|
1123
|
+
*
|
|
1124
|
+
* If the handler returns a plain object, it is auto-wrapped in a
|
|
1125
|
+
* `200 JSON` response. If it returns a `Response`, CORS headers
|
|
1126
|
+
* are merged and the response is passed through.
|
|
1127
|
+
*
|
|
1128
|
+
* ```ts
|
|
1129
|
+
* const handler = auth.http.action(async (ctx, request) => {
|
|
1130
|
+
* const data = await ctx.runQuery(api.data.get, { userId: ctx.key.userId });
|
|
1131
|
+
* return { data };
|
|
1132
|
+
* });
|
|
1133
|
+
* http.route({ path: "/api/data", method: "GET", handler });
|
|
1134
|
+
* ```
|
|
1135
|
+
*
|
|
1136
|
+
* @param handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
1137
|
+
* @param options.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
1138
|
+
* @param options.cors - CORS config; defaults to permissive (`*`).
|
|
1139
|
+
*/
|
|
1140
|
+
action: (
|
|
1141
|
+
handler: (
|
|
1142
|
+
ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,
|
|
1143
|
+
request: Request,
|
|
1144
|
+
) => Promise<Response | Record<string, unknown>>,
|
|
1145
|
+
options?: {
|
|
1146
|
+
scope?: { resource: string; action: string };
|
|
1147
|
+
cors?: CorsConfig;
|
|
1148
|
+
},
|
|
1149
|
+
) => {
|
|
1150
|
+
const corsConfig = options?.cors ?? {};
|
|
1151
|
+
const corsHeaders: Record<string, string> = {
|
|
1152
|
+
"Access-Control-Allow-Origin": corsConfig.origin ?? "*",
|
|
1153
|
+
"Access-Control-Allow-Methods":
|
|
1154
|
+
corsConfig.methods ?? "GET,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
1155
|
+
"Access-Control-Allow-Headers":
|
|
1156
|
+
corsConfig.headers ?? "Content-Type,Authorization",
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
const jsonError = (
|
|
1160
|
+
status: number,
|
|
1161
|
+
code: string,
|
|
1162
|
+
message: string,
|
|
1163
|
+
) =>
|
|
1164
|
+
new Response(JSON.stringify({ error: message, code }), {
|
|
1165
|
+
status,
|
|
1166
|
+
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
return httpActionGeneric(async (genericCtx, request) => {
|
|
1170
|
+
const ctx = genericCtx as unknown as GenericActionCtx<GenericDataModel>;
|
|
1171
|
+
|
|
1172
|
+
try {
|
|
1173
|
+
// 1. Extract Bearer token
|
|
1174
|
+
const authHeader = request.headers.get("Authorization");
|
|
1175
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
1176
|
+
return jsonError(
|
|
1177
|
+
401,
|
|
1178
|
+
"MISSING_BEARER_TOKEN",
|
|
1179
|
+
"Missing or malformed Authorization: Bearer header.",
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
const rawKey = authHeader.slice(7);
|
|
1183
|
+
|
|
1184
|
+
// 2. Verify API key
|
|
1185
|
+
let keyResult: { userId: string; keyId: string; scopes: import("../types.js").ScopeChecker };
|
|
1186
|
+
try {
|
|
1187
|
+
keyResult = await auth.key.verify(ctx, rawKey);
|
|
1188
|
+
} catch (error: unknown) {
|
|
1189
|
+
if (isAuthError(error)) {
|
|
1190
|
+
const { code, message } = error.data as { code: string; message: string };
|
|
1191
|
+
return jsonError(403, code, message);
|
|
1192
|
+
}
|
|
1193
|
+
throw error;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// 3. Optional scope check
|
|
1197
|
+
if (options?.scope) {
|
|
1198
|
+
if (!keyResult.scopes.can(options.scope.resource, options.scope.action)) {
|
|
1199
|
+
return jsonError(
|
|
1200
|
+
403,
|
|
1201
|
+
"SCOPE_CHECK_FAILED",
|
|
1202
|
+
"This API key does not have the required permissions.",
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// 4. Enrich context with key info
|
|
1208
|
+
const enrichedCtx = Object.assign(ctx, {
|
|
1209
|
+
key: {
|
|
1210
|
+
userId: keyResult.userId,
|
|
1211
|
+
keyId: keyResult.keyId,
|
|
1212
|
+
scopes: keyResult.scopes,
|
|
1213
|
+
},
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
// 5. Call handler
|
|
1217
|
+
const result = await handler(enrichedCtx, request);
|
|
1218
|
+
|
|
1219
|
+
// 6. Auto-wrap plain objects as JSON responses
|
|
1220
|
+
if (result instanceof Response) {
|
|
1221
|
+
// Merge CORS headers into existing response
|
|
1222
|
+
const headers = new Headers(result.headers);
|
|
1223
|
+
for (const [k, val] of Object.entries(corsHeaders)) {
|
|
1224
|
+
if (!headers.has(k)) headers.set(k, val);
|
|
1225
|
+
}
|
|
1226
|
+
return new Response(result.body, {
|
|
1227
|
+
status: result.status,
|
|
1228
|
+
statusText: result.statusText,
|
|
1229
|
+
headers,
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return new Response(JSON.stringify(result), {
|
|
1234
|
+
status: 200,
|
|
1235
|
+
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
1236
|
+
});
|
|
1237
|
+
} catch (error: unknown) {
|
|
1238
|
+
logError(error);
|
|
1239
|
+
return jsonError(500, "INTERNAL_ERROR", "An unexpected error occurred.");
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
},
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Register a Bearer-authenticated route **and** its OPTIONS preflight
|
|
1246
|
+
* in a single call.
|
|
1247
|
+
*
|
|
1248
|
+
* ```ts
|
|
1249
|
+
* auth.http.route(http, {
|
|
1250
|
+
* path: "/api/messages",
|
|
1251
|
+
* method: "POST",
|
|
1252
|
+
* handler: async (ctx, request) => {
|
|
1253
|
+
* const { body } = await request.json();
|
|
1254
|
+
* await ctx.runMutation(internal.messages.sendAsUser, {
|
|
1255
|
+
* userId: ctx.key.userId,
|
|
1256
|
+
* body,
|
|
1257
|
+
* });
|
|
1258
|
+
* return { success: true };
|
|
1259
|
+
* },
|
|
1260
|
+
* });
|
|
1261
|
+
* ```
|
|
1262
|
+
*
|
|
1263
|
+
* @param http - The Convex HTTP router.
|
|
1264
|
+
* @param routeConfig.path - The URL path to match.
|
|
1265
|
+
* @param routeConfig.method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
1266
|
+
* @param routeConfig.handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
|
|
1267
|
+
* @param routeConfig.scope - Optional scope check; returns 403 if the key lacks permission.
|
|
1268
|
+
* @param routeConfig.cors - CORS config; defaults to permissive (`*`).
|
|
1269
|
+
*/
|
|
1270
|
+
route: (
|
|
1271
|
+
http: HttpRouter,
|
|
1272
|
+
routeConfig: {
|
|
1273
|
+
path: string;
|
|
1274
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
1275
|
+
handler: (
|
|
1276
|
+
ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,
|
|
1277
|
+
request: Request,
|
|
1278
|
+
) => Promise<Response | Record<string, unknown>>;
|
|
1279
|
+
scope?: { resource: string; action: string };
|
|
1280
|
+
cors?: CorsConfig;
|
|
1281
|
+
},
|
|
1282
|
+
) => {
|
|
1283
|
+
const corsConfig = routeConfig.cors ?? {};
|
|
1284
|
+
const corsHeaders: Record<string, string> = {
|
|
1285
|
+
"Access-Control-Allow-Origin": corsConfig.origin ?? "*",
|
|
1286
|
+
"Access-Control-Allow-Methods":
|
|
1287
|
+
corsConfig.methods ?? "GET,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
1288
|
+
"Access-Control-Allow-Headers":
|
|
1289
|
+
corsConfig.headers ?? "Content-Type,Authorization",
|
|
1290
|
+
};
|
|
1291
|
+
|
|
1292
|
+
// Register OPTIONS preflight
|
|
1293
|
+
http.route({
|
|
1294
|
+
path: routeConfig.path,
|
|
1295
|
+
method: "OPTIONS",
|
|
1296
|
+
handler: httpActionGeneric(async () => {
|
|
1297
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
1298
|
+
}),
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
// Register the main route with Bearer auth wrapping
|
|
1302
|
+
http.route({
|
|
1303
|
+
path: routeConfig.path,
|
|
1304
|
+
method: routeConfig.method,
|
|
1305
|
+
handler: auth.http.action(routeConfig.handler, {
|
|
1306
|
+
scope: routeConfig.scope,
|
|
1307
|
+
cors: routeConfig.cors,
|
|
1308
|
+
}),
|
|
1309
|
+
});
|
|
1310
|
+
},
|
|
1311
|
+
},
|
|
1038
1312
|
};
|
|
1039
1313
|
const enrichCtx = <DataModel extends GenericDataModel>(
|
|
1040
1314
|
ctx: GenericActionCtx<DataModel>,
|
|
@@ -1106,7 +1380,7 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
1106
1380
|
return { totpSetup: { uri: result.uri, secret: result.secret, totpId: result.totpId }, verifier: result.verifier };
|
|
1107
1381
|
default: {
|
|
1108
1382
|
const _typecheck: never = result;
|
|
1109
|
-
|
|
1383
|
+
throwAuthError("INTERNAL_ERROR", `Unexpected result from signIn, ${String(result)}`);
|
|
1110
1384
|
}
|
|
1111
1385
|
}
|
|
1112
1386
|
},
|
|
@@ -1143,10 +1417,18 @@ function convertErrorsToResponse(
|
|
|
1143
1417
|
try {
|
|
1144
1418
|
return await action(ctx, request);
|
|
1145
1419
|
} catch (error) {
|
|
1146
|
-
if (error
|
|
1420
|
+
if (isAuthError(error)) {
|
|
1421
|
+
return new Response(
|
|
1422
|
+
JSON.stringify({ code: error.data.code, message: error.data.message }),
|
|
1423
|
+
{
|
|
1424
|
+
status: errorStatusCode,
|
|
1425
|
+
headers: { "Content-Type": "application/json" },
|
|
1426
|
+
},
|
|
1427
|
+
);
|
|
1428
|
+
} else if (error instanceof ConvexError) {
|
|
1147
1429
|
return new Response(null, {
|
|
1148
1430
|
status: errorStatusCode,
|
|
1149
|
-
statusText: error.data,
|
|
1431
|
+
statusText: typeof error.data === "string" ? error.data : "Error",
|
|
1150
1432
|
});
|
|
1151
1433
|
} else {
|
|
1152
1434
|
logError(error);
|