@better-auth/oauth-provider 1.4.8-beta.7

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.md ADDED
@@ -0,0 +1,17 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2024 - present, Bereket Engida
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5
+ and associated documentation files (the "Software"), to deal in the Software without restriction,
6
+ including without limitation the rights to use, copy, modify, merge, publish, distribute,
7
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
8
+ is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or
11
+ substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14
+ BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ import { a as ResourceServerMetadata } from "./oauth-kjs13QN6.mjs";
2
+ import { JWTPayload, JWTVerifyOptions } from "jose";
3
+ import { Auth } from "better-auth/types";
4
+
5
+ //#region src/client-resource.d.ts
6
+ declare const oauthProviderResourceClient: <T extends Auth | undefined>(auth?: T) => {
7
+ id: "oauth-provider-resource-client";
8
+ getActions(): {
9
+ /**
10
+ * Performs verification of an access token for your APIs. Can perform
11
+ * local verification using `jwksUrl` by default. Can also be configured
12
+ * for remote introspection using `remoteVerify` if a confidential client
13
+ * is set up for this API.
14
+ *
15
+ * The optional auth parameter can fill known values automatically.
16
+ */
17
+ verifyAccessToken: VerifyAccessTokenOutput<T>;
18
+ /**
19
+ * An authorization server does not typically publish
20
+ * the `/.well-known/oauth-protected-resource` themselves.
21
+ * Thus, we provide a client-only endpoint to help set up
22
+ * your protected resource metadata.
23
+ *
24
+ * The optional auth parameter can fill known values automatically.
25
+ *
26
+ * @see https://datatracker.ietf.org/doc/html/rfc8414#section-2
27
+ */
28
+ getProtectedResourceMetadata: ProtectedResourceMetadataOutput<T>;
29
+ };
30
+ };
31
+ interface VerifyAccessTokenRemote {
32
+ /** Full url of the introspect endpoint. Should end with `/oauth2/introspect` */
33
+ introspectUrl: string;
34
+ /** Client Secret */
35
+ clientId: string;
36
+ /** Client Secret */
37
+ clientSecret: string;
38
+ /**
39
+ * Forces remote verification of a token.
40
+ * This ensures attached session (if applicable)
41
+ * is also still active.
42
+ */
43
+ force?: boolean;
44
+ }
45
+ type VerifyAccessTokenOutput<T> = T extends Auth ? (token: string | undefined, opts?: VerifyAccessTokenAuthOpts) => Promise<JWTPayload> : (token: string | undefined, opts: VerifyAccessTokenNoAuthOpts) => Promise<JWTPayload>;
46
+ type VerifyAccessTokenAuthOpts = {
47
+ verifyOptions?: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience">>;
48
+ scopes?: string[];
49
+ jwksUrl?: string;
50
+ remoteVerify?: VerifyAccessTokenRemote;
51
+ /** Maps non-url (ie urn, client) resources to resource_metadata */
52
+ resourceMetadataMappings?: Record<string, string>;
53
+ };
54
+ type VerifyAccessTokenNoAuthOpts = {
55
+ verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
56
+ scopes?: string[];
57
+ jwksUrl: string;
58
+ remoteVerify?: VerifyAccessTokenRemote;
59
+ /** Maps non-url (ie urn, client) resources to resource_metadata */
60
+ resourceMetadataMappings?: Record<string, string>;
61
+ } | {
62
+ verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
63
+ scopes?: string[];
64
+ jwksUrl?: string;
65
+ remoteVerify: VerifyAccessTokenRemote;
66
+ /** Maps non-url (ie urn, client) resources to resource_metadata */
67
+ resourceMetadataMappings?: Record<string, string>;
68
+ };
69
+ type ProtectedResourceMetadataOutput<T> = T extends Auth ? (overrides?: Partial<ResourceServerMetadata>, opts?: {
70
+ silenceWarnings?: {
71
+ oidcScopes?: boolean;
72
+ };
73
+ externalScopes?: string[];
74
+ }) => Promise<ResourceServerMetadata> : (overrides: ResourceServerMetadata, opts?: {
75
+ silenceWarnings?: {
76
+ oidcScopes?: boolean;
77
+ };
78
+ externalScopes?: string[];
79
+ }) => Promise<ResourceServerMetadata>;
80
+ //#endregion
81
+ export { VerifyAccessTokenRemote, oauthProviderResourceClient };
@@ -0,0 +1,75 @@
1
+ import { a as getJwtPlugin, f as handleMcpErrors, o as getOAuthProviderPlugin } from "./utils-CSiY9oUk.mjs";
2
+ import { verifyAccessToken } from "better-auth/oauth2";
3
+ import { APIError } from "better-call";
4
+ import { BetterAuthError } from "@better-auth/core/error";
5
+ import { logger } from "@better-auth/core/env";
6
+
7
+ //#region src/client-resource.ts
8
+ const oauthProviderResourceClient = (auth) => {
9
+ const oauthProviderOptions = (auth ? getOAuthProviderPlugin(auth) : void 0)?.options;
10
+ const jwtPluginOptions = (auth && !oauthProviderOptions?.disableJwtPlugin ? getJwtPlugin(auth) : void 0)?.options;
11
+ const authServerBaseUrl = auth?.options.baseURL;
12
+ const authServerBasePath = auth?.options.basePath;
13
+ const authorizationServer = jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
14
+ return {
15
+ id: "oauth-provider-resource-client",
16
+ getActions() {
17
+ return {
18
+ verifyAccessToken: (async (token, opts) => {
19
+ const audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;
20
+ const issuer = opts?.verifyOptions?.issuer ?? jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
21
+ if (!audience) throw Error("please define opts.verifyOptions.audience");
22
+ if (!issuer) throw Error("please define opts.verifyOptions.issuer");
23
+ const jwksUrl = opts?.jwksUrl ?? jwtPluginOptions?.jwks?.remoteUrl ?? (authServerBaseUrl ? `${authServerBaseUrl + (authServerBasePath ?? "")}/jwks` : void 0);
24
+ const introspectUrl = opts?.remoteVerify?.introspectUrl ?? (authServerBaseUrl ? `${authServerBaseUrl}${authServerBasePath ?? ""}/oauth2/introspect` : void 0);
25
+ try {
26
+ if (!token?.length) throw new APIError("UNAUTHORIZED", { message: "missing authorization header" });
27
+ return await verifyAccessToken(token, {
28
+ ...opts,
29
+ jwksUrl,
30
+ verifyOptions: {
31
+ ...opts?.verifyOptions,
32
+ audience,
33
+ issuer
34
+ },
35
+ remoteVerify: opts?.remoteVerify && introspectUrl ? {
36
+ ...opts.remoteVerify,
37
+ introspectUrl
38
+ } : void 0
39
+ });
40
+ } catch (error) {
41
+ throw handleMcpErrors(error, audience, { resourceMetadataMappings: opts?.resourceMetadataMappings });
42
+ }
43
+ }),
44
+ getProtectedResourceMetadata: (async (overrides, opts) => {
45
+ const resource = overrides?.resource ?? authServerBaseUrl;
46
+ if (!resource) throw Error("missing required resource");
47
+ if (oauthProviderOptions?.scopes && opts?.externalScopes && (overrides?.authorization_servers?.length ?? 0) <= 1) throw new BetterAuthError("external scopes should not be provided with one authorization server");
48
+ if (overrides?.scopes_supported) {
49
+ const allValidScopes = new Set([...oauthProviderOptions?.scopes ?? [], ...opts?.externalScopes ?? []]);
50
+ for (const sc of overrides.scopes_supported) {
51
+ if (sc === "openid") throw new BetterAuthError("Only the Auth Server should utilize the openid scope");
52
+ if ([
53
+ "profile",
54
+ "email",
55
+ "phone",
56
+ "address"
57
+ ].includes(sc)) {
58
+ if (!opts?.silenceWarnings?.oidcScopes) logger.warn(`"${sc}" is typically restricted for the authorization server, a resource server typically shouldn't handle this scope`);
59
+ }
60
+ if (!allValidScopes.has(sc)) throw new BetterAuthError(`Unsupported scope ${sc}. If external, please add to "externalScopes"`);
61
+ }
62
+ }
63
+ return {
64
+ resource,
65
+ authorization_servers: authorizationServer ? [authorizationServer] : void 0,
66
+ ...overrides
67
+ };
68
+ })
69
+ };
70
+ }
71
+ };
72
+ };
73
+
74
+ //#endregion
75
+ export { oauthProviderResourceClient };
@@ -0,0 +1,19 @@
1
+ import "./oauth-kjs13QN6.mjs";
2
+ import { t as oauthProvider } from "./oauth-Cex7QJsW.mjs";
3
+ import * as _better_fetch_fetch0 from "@better-fetch/fetch";
4
+
5
+ //#region src/client.d.ts
6
+ declare const oauthProviderClient: () => {
7
+ id: "oauth-provider-client";
8
+ fetchPlugins: {
9
+ id: string;
10
+ name: string;
11
+ description: string;
12
+ hooks: {
13
+ onRequest<T extends Record<string, any>>(ctx: _better_fetch_fetch0.RequestContext<T>): Promise<void>;
14
+ };
15
+ }[];
16
+ $InferServerPlugin: ReturnType<typeof oauthProvider>;
17
+ };
18
+ //#endregion
19
+ export { oauthProviderClient };
@@ -0,0 +1,38 @@
1
+ import { safeJSONParse } from "@better-auth/core/utils";
2
+
3
+ //#region src/client.ts
4
+ function parseSignedQuery(search) {
5
+ const params = new URLSearchParams(search);
6
+ if (params.has("sig")) {
7
+ const signedParams = new URLSearchParams();
8
+ for (const [key, value] of params.entries()) {
9
+ signedParams.append(key, value);
10
+ if (key === "sig") break;
11
+ }
12
+ return signedParams.toString();
13
+ }
14
+ }
15
+ const oauthProviderClient = () => {
16
+ return {
17
+ id: "oauth-provider-client",
18
+ fetchPlugins: [{
19
+ id: "oauth-provider-signin",
20
+ name: "oauth-provider-signin",
21
+ description: "Adds the current page query to oauth requests",
22
+ hooks: { async onRequest(ctx) {
23
+ const headers = ctx.headers;
24
+ const body = typeof ctx.body === "string" ? headers.get("content-type") === "application/x-www-form-urlencoded" ? Object.fromEntries(new URLSearchParams(ctx.body)) : safeJSONParse(ctx.body ?? "{}") : ctx.body;
25
+ if (body?.oauth_query) return;
26
+ const pathname = typeof ctx.url === "string" ? new URL(ctx.url).pathname : ctx.url.pathname;
27
+ if (pathname.endsWith("/sign-in/email") || pathname.endsWith("/sign-in/social") || pathname.endsWith("/sign-in/oauth2") || pathname.endsWith("/oauth2/consent") || pathname.endsWith("/oauth2/continue")) ctx.body = JSON.stringify({
28
+ ...body,
29
+ oauth_query: typeof window !== "undefined" ? parseSignedQuery(window?.location?.search) : void 0
30
+ });
31
+ } }
32
+ }],
33
+ $InferServerPlugin: {}
34
+ };
35
+ };
36
+
37
+ //#endregion
38
+ export { oauthProviderClient };
@@ -0,0 +1,65 @@
1
+ import { _ as Awaitable, a as ResourceServerMetadata, c as OAuthConsent, d as OAuthRefreshToken, f as Prompt, g as VerificationValue, h as StoreTokenType, i as OIDCMetadata, l as OAuthOpaqueAccessToken, m as Scope, n as GrantType, o as AuthorizePrompt, p as SchemaClient, r as OAuthClient, s as OAuthAuthorizationQuery, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-kjs13QN6.mjs";
2
+ import { t as oauthProvider } from "./oauth-Cex7QJsW.mjs";
3
+ import { verifyAccessToken } from "better-auth/oauth2";
4
+ import { JWSAlgorithms, JwtOptions } from "better-auth/plugins/jwt";
5
+ import { JWTPayload } from "jose";
6
+ import { GenericEndpointContext } from "@better-auth/core";
7
+
8
+ //#region src/mcp.d.ts
9
+
10
+ /**
11
+ * A request middleware handler that checks and responds with
12
+ * a WWW-Authenticate header for unauthenticated responses.
13
+ *
14
+ * @external
15
+ */
16
+ declare const mcpHandler: (/** Resource is the same url as the audience */
17
+ verifyOptions: Parameters<typeof verifyAccessToken>[1], handler: (req: Request, jwt: JWTPayload) => Awaitable<Response>, opts?: {
18
+ /** Maps non-url (ie urn, client) resources to resource_metadata */
19
+ resourceMetadataMappings: Record<string, string>;
20
+ }) => (req: Request) => Promise<Response>;
21
+ //#endregion
22
+ //#region src/metadata.d.ts
23
+ declare function authServerMetadata(ctx: GenericEndpointContext, opts?: JwtOptions, overrides?: {
24
+ scopes_supported?: AuthServerMetadata["scopes_supported"];
25
+ public_client_supported?: boolean;
26
+ grant_types_supported?: GrantType[];
27
+ jwt_disabled?: boolean;
28
+ }): AuthServerMetadata;
29
+ declare function oidcServerMetadata(ctx: GenericEndpointContext, opts: OAuthOptions<Scope[]> & {
30
+ claims?: string[];
31
+ }): Omit<OIDCMetadata, "id_token_signing_alg_values_supported"> & {
32
+ id_token_signing_alg_values_supported: JWSAlgorithms[] | ["HS256"];
33
+ };
34
+ /**
35
+ * Provides an exportable `/.well-known/oauth-authorization-server`.
36
+ *
37
+ * Useful when basePath prevents the endpoint from being located at the root
38
+ * and must be provided manually.
39
+ *
40
+ * @external
41
+ */
42
+ declare const oauthProviderAuthServerMetadata: <Auth extends {
43
+ api: {
44
+ getOAuthServerConfig: (...args: any) => any;
45
+ };
46
+ }>(auth: Auth, opts?: {
47
+ headers?: HeadersInit;
48
+ }) => (_request: Request) => Promise<Response>;
49
+ /**
50
+ * Provides an exportable `/.well-known/openid-configuration`.
51
+ *
52
+ * Useful when basePath prevents the endpoint from being located at the root
53
+ * and must be provided manually.
54
+ *
55
+ * @external
56
+ */
57
+ declare const oauthProviderOpenIdConfigMetadata: <Auth extends {
58
+ api: {
59
+ getOpenIdConfig: (...args: any) => any;
60
+ };
61
+ }>(auth: Auth, opts?: {
62
+ headers?: HeadersInit;
63
+ }) => (_request: Request) => Promise<Response>;
64
+ //#endregion
65
+ export { AuthServerMetadata, AuthorizePrompt, OAuthAuthorizationQuery, OAuthClient, OAuthConsent, OAuthOpaqueAccessToken, OAuthOptions, OAuthRefreshToken, OIDCMetadata, Prompt, ResourceServerMetadata, SchemaClient, Scope, StoreTokenType, VerificationValue, authServerMetadata, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oidcServerMetadata };