@better-auth/oauth-provider 1.4.18 → 1.4.19
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/client-resource.d.mts +1 -1
- package/dist/client-resource.mjs +1 -1
- package/dist/client.d.mts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +228 -126
- package/dist/index.mjs.map +1 -1
- package/dist/{oauth-BrFoF22H.d.mts → oauth-1jow-gRL.d.mts} +77 -1
- package/dist/{oauth-BxSSTB3p.d.mts → oauth-DC4oauK7.d.mts} +29 -4
- package/dist/{utils-DnfreTWo.mjs → utils-CThxvHKd.mjs} +9 -3
- package/dist/utils-CThxvHKd.mjs.map +1 -0
- package/package.json +5 -5
- package/dist/utils-DnfreTWo.mjs.map +0 -1
|
@@ -869,6 +869,74 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
|
|
|
869
869
|
* @default false
|
|
870
870
|
*/
|
|
871
871
|
disableJwtPlugin?: boolean;
|
|
872
|
+
/**
|
|
873
|
+
* Rate limit configuration for OAuth endpoints.
|
|
874
|
+
*
|
|
875
|
+
* Each endpoint can be configured with a `window` (in seconds) and `max` requests.
|
|
876
|
+
* Set to `false` to disable rate limiting for a specific endpoint.
|
|
877
|
+
*
|
|
878
|
+
* @default
|
|
879
|
+
* ```ts
|
|
880
|
+
* {
|
|
881
|
+
* token: { window: 60, max: 20 },
|
|
882
|
+
* authorize: { window: 60, max: 30 },
|
|
883
|
+
* introspect: { window: 60, max: 100 },
|
|
884
|
+
* revoke: { window: 60, max: 30 },
|
|
885
|
+
* register: { window: 60, max: 5 },
|
|
886
|
+
* userinfo: { window: 60, max: 60 },
|
|
887
|
+
* }
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
rateLimit?: {
|
|
891
|
+
/**
|
|
892
|
+
* Rate limit for /oauth2/token endpoint
|
|
893
|
+
* @default { window: 60, max: 20 }
|
|
894
|
+
*/
|
|
895
|
+
token?: {
|
|
896
|
+
window: number;
|
|
897
|
+
max: number;
|
|
898
|
+
} | false;
|
|
899
|
+
/**
|
|
900
|
+
* Rate limit for /oauth2/authorize endpoint
|
|
901
|
+
* @default { window: 60, max: 30 }
|
|
902
|
+
*/
|
|
903
|
+
authorize?: {
|
|
904
|
+
window: number;
|
|
905
|
+
max: number;
|
|
906
|
+
} | false;
|
|
907
|
+
/**
|
|
908
|
+
* Rate limit for /oauth2/introspect endpoint
|
|
909
|
+
* @default { window: 60, max: 100 }
|
|
910
|
+
*/
|
|
911
|
+
introspect?: {
|
|
912
|
+
window: number;
|
|
913
|
+
max: number;
|
|
914
|
+
} | false;
|
|
915
|
+
/**
|
|
916
|
+
* Rate limit for /oauth2/revoke endpoint
|
|
917
|
+
* @default { window: 60, max: 30 }
|
|
918
|
+
*/
|
|
919
|
+
revoke?: {
|
|
920
|
+
window: number;
|
|
921
|
+
max: number;
|
|
922
|
+
} | false;
|
|
923
|
+
/**
|
|
924
|
+
* Rate limit for /oauth2/register endpoint
|
|
925
|
+
* @default { window: 60, max: 5 }
|
|
926
|
+
*/
|
|
927
|
+
register?: {
|
|
928
|
+
window: number;
|
|
929
|
+
max: number;
|
|
930
|
+
} | false;
|
|
931
|
+
/**
|
|
932
|
+
* Rate limit for /oauth2/userinfo endpoint
|
|
933
|
+
* @default { window: 60, max: 60 }
|
|
934
|
+
*/
|
|
935
|
+
userinfo?: {
|
|
936
|
+
window: number;
|
|
937
|
+
max: number;
|
|
938
|
+
} | false;
|
|
939
|
+
};
|
|
872
940
|
}
|
|
873
941
|
interface OAuthAuthorizationQuery {
|
|
874
942
|
/**
|
|
@@ -1331,6 +1399,14 @@ interface AuthServerMetadata {
|
|
|
1331
1399
|
* @default ["S256"]
|
|
1332
1400
|
*/
|
|
1333
1401
|
code_challenge_methods_supported: "S256"[];
|
|
1402
|
+
/**
|
|
1403
|
+
* Boolean value specifying whether the authorization server provides
|
|
1404
|
+
* the iss parameter in the authorization response (RFC 9207)
|
|
1405
|
+
*
|
|
1406
|
+
* @see https://datatracker.ietf.org/doc/html/rfc9207
|
|
1407
|
+
* @default true
|
|
1408
|
+
*/
|
|
1409
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
1334
1410
|
}
|
|
1335
1411
|
/**
|
|
1336
1412
|
* Metadata returned by the openid-configuration endpoint:
|
|
@@ -1480,4 +1556,4 @@ interface ResourceServerMetadata {
|
|
|
1480
1556
|
}
|
|
1481
1557
|
//#endregion
|
|
1482
1558
|
export { Awaitable as _, ResourceServerMetadata as a, OAuthConsent as c, OAuthRefreshToken as d, Prompt as f, VerificationValue as g, StoreTokenType as h, OIDCMetadata as i, OAuthOpaqueAccessToken as l, Scope as m, GrantType as n, AuthorizePrompt as o, SchemaClient as p, OAuthClient as r, OAuthAuthorizationQuery as s, AuthServerMetadata as t, OAuthOptions as u };
|
|
1483
|
-
//# sourceMappingURL=oauth-
|
|
1559
|
+
//# sourceMappingURL=oauth-1jow-gRL.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-
|
|
1
|
+
import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-1jow-gRL.mjs";
|
|
2
2
|
import * as better_call0 from "better-call";
|
|
3
3
|
import "@better-auth/core/context";
|
|
4
4
|
import * as z from "zod";
|
|
@@ -215,7 +215,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
215
215
|
};
|
|
216
216
|
}, {
|
|
217
217
|
redirect: boolean;
|
|
218
|
-
|
|
218
|
+
url: string;
|
|
219
219
|
}>;
|
|
220
220
|
oauth2Continue: better_call0.StrictEndpoint<"/oauth2/continue", {
|
|
221
221
|
method: "POST";
|
|
@@ -275,7 +275,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
275
275
|
};
|
|
276
276
|
}, {
|
|
277
277
|
redirect: boolean;
|
|
278
|
-
|
|
278
|
+
url: string;
|
|
279
279
|
}>;
|
|
280
280
|
oauth2Token: better_call0.StrictEndpoint<"/oauth2/token", {
|
|
281
281
|
method: "POST";
|
|
@@ -2001,7 +2001,32 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
2001
2001
|
};
|
|
2002
2002
|
};
|
|
2003
2003
|
};
|
|
2004
|
+
rateLimit: ({
|
|
2005
|
+
pathMatcher: (path: string) => path is "/oauth2/token";
|
|
2006
|
+
window: number;
|
|
2007
|
+
max: number;
|
|
2008
|
+
} | {
|
|
2009
|
+
pathMatcher: (path: string) => path is "/oauth2/authorize";
|
|
2010
|
+
window: number;
|
|
2011
|
+
max: number;
|
|
2012
|
+
} | {
|
|
2013
|
+
pathMatcher: (path: string) => path is "/oauth2/introspect";
|
|
2014
|
+
window: number;
|
|
2015
|
+
max: number;
|
|
2016
|
+
} | {
|
|
2017
|
+
pathMatcher: (path: string) => path is "/oauth2/revoke";
|
|
2018
|
+
window: number;
|
|
2019
|
+
max: number;
|
|
2020
|
+
} | {
|
|
2021
|
+
pathMatcher: (path: string) => path is "/oauth2/register";
|
|
2022
|
+
window: number;
|
|
2023
|
+
max: number;
|
|
2024
|
+
} | {
|
|
2025
|
+
pathMatcher: (path: string) => path is "/oauth2/userinfo";
|
|
2026
|
+
window: number;
|
|
2027
|
+
max: number;
|
|
2028
|
+
})[];
|
|
2004
2029
|
};
|
|
2005
2030
|
//#endregion
|
|
2006
2031
|
export { oauthProvider as t };
|
|
2007
|
-
//# sourceMappingURL=oauth-
|
|
2032
|
+
//# sourceMappingURL=oauth-DC4oauK7.d.mts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { verifyAccessToken } from "better-auth/oauth2";
|
|
2
2
|
import { APIError } from "better-call";
|
|
3
|
+
import { constantTimeEqual, symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto";
|
|
3
4
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
4
5
|
import { base64, base64Url } from "@better-auth/utils/base64";
|
|
5
6
|
import { createHash } from "@better-auth/utils/hash";
|
|
6
|
-
import { constantTimeEqual, symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto";
|
|
7
7
|
|
|
8
8
|
//#region src/mcp.ts
|
|
9
9
|
/**
|
|
@@ -150,7 +150,13 @@ async function verifyStoredClientSecret(ctx, opts, storedClientSecret, clientSec
|
|
|
150
150
|
const hashedClientSecret = clientSecret ? await storageMethod.hash(clientSecret) : void 0;
|
|
151
151
|
return !!hashedClientSecret && constantTimeEqual(hashedClientSecret, storedClientSecret);
|
|
152
152
|
}
|
|
153
|
-
else if (storageMethod === "encrypted"
|
|
153
|
+
else if (storageMethod === "encrypted") try {
|
|
154
|
+
const decryptedClientSecret = await decryptStoredClientSecret(ctx, storageMethod, storedClientSecret);
|
|
155
|
+
return !!clientSecret && constantTimeEqual(decryptedClientSecret, clientSecret);
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
else if (typeof storageMethod === "object" && "decrypt" in storageMethod) {
|
|
154
160
|
const decryptedClientSecret = await decryptStoredClientSecret(ctx, storageMethod, storedClientSecret);
|
|
155
161
|
return !!clientSecret && constantTimeEqual(decryptedClientSecret, clientSecret);
|
|
156
162
|
}
|
|
@@ -294,4 +300,4 @@ function deleteFromPrompt(query, prompt) {
|
|
|
294
300
|
|
|
295
301
|
//#endregion
|
|
296
302
|
export { getJwtPlugin as a, parseClientMetadata as c, storeToken as d, validateClientCredentials as f, getClient as i, parsePrompt as l, mcpHandler as m, decryptStoredClientSecret as n, getOAuthProviderPlugin as o, handleMcpErrors as p, deleteFromPrompt as r, getStoredToken as s, basicToClientCredentials as t, storeClientSecret as u };
|
|
297
|
-
//# sourceMappingURL=utils-
|
|
303
|
+
//# sourceMappingURL=utils-CThxvHKd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-CThxvHKd.mjs","names":["audiencePath: string"],"sources":["../src/mcp.ts","../src/utils/index.ts"],"sourcesContent":["import { verifyAccessToken } from \"better-auth/oauth2\";\nimport { APIError } from \"better-call\";\nimport type { JWTPayload } from \"jose\";\nimport type { Awaitable } from \"./types/helpers\";\n\n/**\n * A request middleware handler that checks and responds with\n * a WWW-Authenticate header for unauthenticated responses.\n *\n * @external\n */\nexport const mcpHandler = (\n\t/** Resource is the same url as the audience */\n\tverifyOptions: Parameters<typeof verifyAccessToken>[1],\n\thandler: (req: Request, jwt: JWTPayload) => Awaitable<Response>,\n\topts?: {\n\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\tresourceMetadataMappings: Record<string, string>;\n\t},\n) => {\n\treturn async (req: Request) => {\n\t\tconst authorization = req.headers?.get(\"authorization\") ?? undefined;\n\t\tconst accessToken = authorization?.startsWith(\"Bearer \")\n\t\t\t? authorization.replace(\"Bearer \", \"\")\n\t\t\t: authorization;\n\t\ttry {\n\t\t\tif (!accessToken?.length) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"missing authorization header\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst token = await verifyAccessToken(accessToken, verifyOptions);\n\t\t\treturn handler(req, token);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\thandleMcpErrors(error, verifyOptions.verifyOptions.audience, opts);\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof APIError) {\n\t\t\t\t\treturn new Response(err.message, {\n\t\t\t\t\t\t...err,\n\t\t\t\t\t\tstatus: err.statusCode,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tthrow new Error(String(err));\n\t\t\t}\n\t\t\tthrow new Error(String(error));\n\t\t}\n\t};\n};\n\n/**\n * The following handles all MCP errors and API errors\n *\n * @internal\n */\nexport function handleMcpErrors(\n\terror: unknown,\n\tresource: string | string[],\n\topts?: {\n\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\tresourceMetadataMappings?: Record<string, string>;\n\t},\n) {\n\tif (error instanceof APIError && error.status === \"UNAUTHORIZED\") {\n\t\tconst _resources = Array.isArray(resource) ? resource : [resource];\n\t\tconst wwwAuthenticateValue = _resources\n\t\t\t.map((v) => {\n\t\t\t\tlet audiencePath: string;\n\t\t\t\tif (URL.canParse?.(v)) {\n\t\t\t\t\tconst url = new URL(v);\n\t\t\t\t\taudiencePath = url.pathname.endsWith(\"/\")\n\t\t\t\t\t\t? url.pathname.slice(0, -1)\n\t\t\t\t\t\t: url.pathname;\n\t\t\t\t\treturn `Bearer resource_metadata=\"${url.origin}/.well-known/oauth-protected-resource${\n\t\t\t\t\t\taudiencePath\n\t\t\t\t\t}\"`;\n\t\t\t\t} else {\n\t\t\t\t\tconst resourceMetadata = opts?.resourceMetadataMappings?.[v];\n\t\t\t\t\tif (!resourceMetadata) {\n\t\t\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\t\tmessage: `missing resource_metadata mapping for ${v}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn `Bearer resource_metadata=${resourceMetadata}`;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.join(\", \");\n\t\tthrow new APIError(\n\t\t\t\"UNAUTHORIZED\",\n\t\t\t{\n\t\t\t\tmessage: error.message,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"WWW-Authenticate\": wwwAuthenticateValue,\n\t\t\t},\n\t\t);\n\t} else if (error instanceof Error) {\n\t\tthrow error;\n\t} else {\n\t\tthrow new Error(error as unknown as string);\n\t}\n}\n","import type { AuthContext, GenericEndpointContext } from \"@better-auth/core\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport { base64, base64Url } from \"@better-auth/utils/base64\";\nimport { createHash } from \"@better-auth/utils/hash\";\nimport {\n\tconstantTimeEqual,\n\tsymmetricDecrypt,\n\tsymmetricEncrypt,\n} from \"better-auth/crypto\";\nimport type { jwt } from \"better-auth/plugins\";\nimport type { Auth } from \"better-auth/types\";\nimport { APIError } from \"better-call\";\nimport type { oauthProvider } from \"..\";\nimport type {\n\tOAuthOptions,\n\tPrompt,\n\tSchemaClient,\n\tScope,\n\tStoreTokenType,\n} from \"../types\";\n\nclass TTLCache<K, V extends { expiresAt?: Date }> {\n\tprivate cache = new Map<K, V>();\n\tconstructor() {}\n\n\tset(key: K, value: V) {\n\t\tthis.cache.set(key, value);\n\t}\n\n\tget(key: K): V | undefined {\n\t\tconst entry = this.cache.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt && entry.expiresAt < new Date()) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry;\n\t}\n}\n\n/**\n * Gets the oAuth Provider Plugin\n * @internal\n */\nexport const getOAuthProviderPlugin = (ctx: AuthContext | Auth) => {\n\treturn ctx.options.plugins?.find(\n\t\t(plugin) => plugin.id === \"oauthProvider\",\n\t) as ReturnType<typeof oauthProvider>;\n};\n\n/**\n * Gets the JWT Plugin\n * @internal\n */\nexport const getJwtPlugin = (ctx: AuthContext): ReturnType<typeof jwt> => {\n\tconst plugin = ctx.getPlugin<ReturnType<typeof jwt>>(\"jwt\");\n\tif (!plugin) {\n\t\tthrow new BetterAuthError(\"jwt_config\", \"jwt plugin not found\");\n\t}\n\treturn plugin;\n};\n\nconst cachedTrustedClients = new TTLCache<string, SchemaClient<Scope[]>>();\n\n/**\n * Get a client by ID, checking trusted clients first, then database\n */\nexport async function getClient(\n\tctx: GenericEndpointContext,\n\toptions: OAuthOptions<Scope[]>,\n\tclientId: string,\n) {\n\tconst trustedClient = cachedTrustedClients.get(clientId);\n\tif (trustedClient) {\n\t\treturn Object.assign({}, trustedClient);\n\t}\n\n\tconst dbClient = await ctx.context.adapter.findOne<SchemaClient<Scope[]>>({\n\t\tmodel: options.schema?.oauthClient?.modelName ?? \"oauthClient\",\n\t\twhere: [{ field: \"clientId\", value: clientId }],\n\t});\n\n\tif (dbClient && options.cachedTrustedClients?.has(clientId)) {\n\t\tcachedTrustedClients.set(clientId, Object.assign({}, dbClient));\n\t}\n\n\treturn dbClient;\n}\n\n/**\n * Default client secret hasher using SHA-256\n *\n * @internal\n */\nconst defaultHasher = async (value: string) => {\n\tconst hash = await createHash(\"SHA-256\").digest(\n\t\tnew TextEncoder().encode(value),\n\t);\n\tconst hashed = base64Url.encode(new Uint8Array(hash), {\n\t\tpadding: false,\n\t});\n\treturn hashed;\n};\n\n/**\n * Decrypts a storedClientSecret for signing\n *\n * @internal\n */\nexport async function decryptStoredClientSecret(\n\tctx: GenericEndpointContext,\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeClientSecret\"],\n\tstoredClientSecret: string,\n) {\n\tif (storageMethod === \"encrypted\") {\n\t\treturn await symmetricDecrypt({\n\t\t\tkey: ctx.context.secret,\n\t\t\tdata: storedClientSecret,\n\t\t});\n\t} else if (typeof storageMethod === \"object\" && \"decrypt\" in storageMethod) {\n\t\treturn await storageMethod.decrypt(storedClientSecret);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported decryption storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Verify stored client secret against provided client secret\n *\n * @internal\n */\nasync function verifyStoredClientSecret(\n\tctx: GenericEndpointContext,\n\topts: OAuthOptions<Scope[]>,\n\tstoredClientSecret: string,\n\tclientSecret?: string,\n): Promise<boolean> {\n\tconst storageMethod =\n\t\topts.storeClientSecret ?? (opts.disableJwtPlugin ? \"encrypted\" : \"hashed\");\n\n\tif (clientSecret && opts.prefix?.clientSecret) {\n\t\tif (clientSecret.startsWith(opts.prefix?.clientSecret)) {\n\t\t\tclientSecret = clientSecret.replace(opts.prefix.clientSecret, \"\");\n\t\t} else {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\terror_description: \"invalid client_secret\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (storageMethod === \"hashed\") {\n\t\tconst hashedClientSecret = clientSecret\n\t\t\t? await defaultHasher(clientSecret)\n\t\t\t: undefined;\n\t\treturn (\n\t\t\t!!hashedClientSecret &&\n\t\t\tconstantTimeEqual(hashedClientSecret, storedClientSecret)\n\t\t);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\tif (storageMethod.verify) {\n\t\t\treturn (\n\t\t\t\t!!clientSecret &&\n\t\t\t\t(await storageMethod.verify(clientSecret, storedClientSecret))\n\t\t\t);\n\t\t} else {\n\t\t\tconst hashedClientSecret = clientSecret\n\t\t\t\t? await storageMethod.hash(clientSecret)\n\t\t\t\t: undefined;\n\t\t\treturn (\n\t\t\t\t!!hashedClientSecret &&\n\t\t\t\tconstantTimeEqual(hashedClientSecret, storedClientSecret)\n\t\t\t);\n\t\t}\n\t} else if (storageMethod === \"encrypted\") {\n\t\ttry {\n\t\t\tconst decryptedClientSecret = await decryptStoredClientSecret(\n\t\t\t\tctx,\n\t\t\t\tstorageMethod,\n\t\t\t\tstoredClientSecret,\n\t\t\t);\n\t\t\treturn (\n\t\t\t\t!!clientSecret && constantTimeEqual(decryptedClientSecret, clientSecret)\n\t\t\t);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t} else if (typeof storageMethod === \"object\" && \"decrypt\" in storageMethod) {\n\t\tconst decryptedClientSecret = await decryptStoredClientSecret(\n\t\t\tctx,\n\t\t\tstorageMethod,\n\t\t\tstoredClientSecret,\n\t\t);\n\t\treturn (\n\t\t\t!!clientSecret && constantTimeEqual(decryptedClientSecret, clientSecret)\n\t\t);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported verify storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Store client secret according to the configured storage method\n *\n * @internal\n */\nexport async function storeClientSecret(\n\tctx: GenericEndpointContext,\n\topts: OAuthOptions<Scope[]>,\n\tclientSecret: string,\n) {\n\tconst storageMethod =\n\t\topts.storeClientSecret ?? (opts.disableJwtPlugin ? \"encrypted\" : \"hashed\");\n\n\tif (storageMethod === \"encrypted\") {\n\t\treturn await symmetricEncrypt({\n\t\t\tkey: ctx.context.secret,\n\t\t\tdata: clientSecret,\n\t\t});\n\t} else if (storageMethod === \"hashed\") {\n\t\treturn await defaultHasher(clientSecret);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\treturn await storageMethod.hash(clientSecret);\n\t} else if (typeof storageMethod === \"object\" && \"encrypt\" in storageMethod) {\n\t\treturn await storageMethod.encrypt(clientSecret);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported storeClientSecret type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Stores a token value (ie opaque tokens, refresh tokens, transaction tokens, verification codes)\n * on the database in a secure hashed format.\n *\n * @internal\n */\nexport async function storeToken(\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeTokens\"] = \"hashed\",\n\ttoken: string,\n\ttype: StoreTokenType,\n) {\n\tif (storageMethod === \"hashed\") {\n\t\treturn await defaultHasher(token);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\treturn await storageMethod.hash(token, type);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`storeToken: unsupported storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Gets a hashed token value to find on the database.\n *\n * @internal\n */\nexport async function getStoredToken(\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeTokens\"] = \"hashed\",\n\ttoken: string,\n\ttype: StoreTokenType,\n) {\n\tif (storageMethod === \"hashed\") {\n\t\tconst hashedToken = await defaultHasher(token);\n\t\treturn hashedToken;\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\tconst hashedToken = await storageMethod.hash(token, type);\n\t\treturn hashedToken;\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`getStoredToken: unsupported storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Converts a BASIC authorization header\n * into its client_id and client_secret representation\n *\n * @internal\n */\nexport function basicToClientCredentials(authorization: string) {\n\tif (authorization.startsWith(\"Basic \")) {\n\t\tconst encoded = authorization.replace(\"Basic \", \"\");\n\t\tconst decoded = new TextDecoder().decode(base64.decode(encoded));\n\t\tif (!decoded.includes(\":\")) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t\tconst [id, secret] = decoded.split(\":\", 2);\n\t\tif (!id || !secret) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tclient_id: id,\n\t\t\tclient_secret: secret,\n\t\t};\n\t}\n}\n\n/**\n * Validates client credentials failing on mismatches\n * and incorrectly provided information\n *\n * @internal\n */\nexport async function validateClientCredentials(\n\tctx: GenericEndpointContext,\n\toptions: OAuthOptions<Scope[]>,\n\tclientId: string,\n\tclientSecret?: string, // optional because required if client is confidential or this value is defined\n\tscopes?: string[], // checks requested scopes against allowed scopes\n) {\n\tconst client = await getClient(ctx, options, clientId);\n\tif (!client) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"missing client\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\tif (client.disabled) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"client is disabled\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Require secret for confidential clients\n\tif (!client.public && !clientSecret) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"client secret must be provided\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Secret should not be received\n\tif (clientSecret && !client.clientSecret) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"public client, client secret should not be received\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Compare Secrets when secret is provided\n\tif (\n\t\tclientSecret &&\n\t\t!(await verifyStoredClientSecret(\n\t\t\tctx,\n\t\t\toptions,\n\t\t\tclient.clientSecret!,\n\t\t\tclientSecret,\n\t\t))\n\t) {\n\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\terror_description: \"invalid client_secret\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// If scopes set, check against client allowed scopes\n\tif (scopes && client.scopes) {\n\t\tconst validScopes = new Set(client.scopes);\n\t\tfor (const sc of scopes) {\n\t\t\tif (!validScopes.has(sc)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror_description: `client does not allow scope ${sc}`,\n\t\t\t\t\terror: \"invalid_scope\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn client;\n}\n\n/**\n * Parse client metadata that may be stored as JSON string or already parsed object.\n * Handles database adapters that auto-parse JSON columns.\n *\n * @internal\n */\nexport function parseClientMetadata(\n\tmetadata: string | object | undefined,\n): object | undefined {\n\tif (!metadata) return undefined;\n\treturn typeof metadata === \"string\" ? JSON.parse(metadata) : metadata;\n}\n\n/**\n * Parse space-separated prompt string into a set of prompts\n *\n * @param prompt\n */\nexport function parsePrompt(prompt: string) {\n\tconst prompts = prompt.split(\" \").map((p) => p.trim());\n\tconst set = new Set<Prompt>();\n\tfor (const p of prompts) {\n\t\tif (\n\t\t\tp === \"login\" ||\n\t\t\tp === \"consent\" ||\n\t\t\tp === \"create\" ||\n\t\t\tp === \"select_account\" ||\n\t\t\tp === \"none\"\n\t\t) {\n\t\t\tset.add(p);\n\t\t}\n\t}\n\treturn new Set(set);\n}\n\n/**\n * Deletes a prompt value\n *\n * @param ctx\n * @param prompt - the prompt value to delete\n */\nexport function deleteFromPrompt(query: URLSearchParams, prompt: Prompt) {\n\tconst prompts = query.get(\"prompt\")?.split(\" \");\n\tconst foundPrompt = prompts?.findIndex((v) => v === prompt) ?? -1;\n\tif (foundPrompt >= 0) {\n\t\tprompts?.splice(foundPrompt, 1);\n\t\tprompts?.length\n\t\t\t? query.set(\"prompt\", prompts.join(\" \"))\n\t\t\t: query.delete(\"prompt\");\n\t}\n\treturn Object.fromEntries(query);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/oauth-provider",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "An oauth provider plugin for Better Auth",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
43
43
|
"listhen": "^1.9.0",
|
|
44
44
|
"tsdown": "^0.17.2",
|
|
45
|
-
"@better-auth/core": "1.4.
|
|
46
|
-
"better-auth": "1.4.
|
|
45
|
+
"@better-auth/core": "1.4.19",
|
|
46
|
+
"better-auth": "1.4.19"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"jose": "^6.1.0",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"@better-auth/utils": "0.3.0",
|
|
54
54
|
"@better-fetch/fetch": "1.1.21",
|
|
55
55
|
"better-call": "1.1.8",
|
|
56
|
-
"@better-auth/core": "1.4.
|
|
57
|
-
"better-auth": "1.4.
|
|
56
|
+
"@better-auth/core": "1.4.19",
|
|
57
|
+
"better-auth": "1.4.19"
|
|
58
58
|
},
|
|
59
59
|
"files": [
|
|
60
60
|
"dist"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils-DnfreTWo.mjs","names":["audiencePath: string"],"sources":["../src/mcp.ts","../src/utils/index.ts"],"sourcesContent":["import { verifyAccessToken } from \"better-auth/oauth2\";\nimport { APIError } from \"better-call\";\nimport type { JWTPayload } from \"jose\";\nimport type { Awaitable } from \"./types/helpers\";\n\n/**\n * A request middleware handler that checks and responds with\n * a WWW-Authenticate header for unauthenticated responses.\n *\n * @external\n */\nexport const mcpHandler = (\n\t/** Resource is the same url as the audience */\n\tverifyOptions: Parameters<typeof verifyAccessToken>[1],\n\thandler: (req: Request, jwt: JWTPayload) => Awaitable<Response>,\n\topts?: {\n\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\tresourceMetadataMappings: Record<string, string>;\n\t},\n) => {\n\treturn async (req: Request) => {\n\t\tconst authorization = req.headers?.get(\"authorization\") ?? undefined;\n\t\tconst accessToken = authorization?.startsWith(\"Bearer \")\n\t\t\t? authorization.replace(\"Bearer \", \"\")\n\t\t\t: authorization;\n\t\ttry {\n\t\t\tif (!accessToken?.length) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"missing authorization header\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst token = await verifyAccessToken(accessToken, verifyOptions);\n\t\t\treturn handler(req, token);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\thandleMcpErrors(error, verifyOptions.verifyOptions.audience, opts);\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof APIError) {\n\t\t\t\t\treturn new Response(err.message, {\n\t\t\t\t\t\t...err,\n\t\t\t\t\t\tstatus: err.statusCode,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tthrow new Error(String(err));\n\t\t\t}\n\t\t\tthrow new Error(String(error));\n\t\t}\n\t};\n};\n\n/**\n * The following handles all MCP errors and API errors\n *\n * @internal\n */\nexport function handleMcpErrors(\n\terror: unknown,\n\tresource: string | string[],\n\topts?: {\n\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\tresourceMetadataMappings?: Record<string, string>;\n\t},\n) {\n\tif (error instanceof APIError && error.status === \"UNAUTHORIZED\") {\n\t\tconst _resources = Array.isArray(resource) ? resource : [resource];\n\t\tconst wwwAuthenticateValue = _resources\n\t\t\t.map((v) => {\n\t\t\t\tlet audiencePath: string;\n\t\t\t\tif (URL.canParse?.(v)) {\n\t\t\t\t\tconst url = new URL(v);\n\t\t\t\t\taudiencePath = url.pathname.endsWith(\"/\")\n\t\t\t\t\t\t? url.pathname.slice(0, -1)\n\t\t\t\t\t\t: url.pathname;\n\t\t\t\t\treturn `Bearer resource_metadata=\"${url.origin}/.well-known/oauth-protected-resource${\n\t\t\t\t\t\taudiencePath\n\t\t\t\t\t}\"`;\n\t\t\t\t} else {\n\t\t\t\t\tconst resourceMetadata = opts?.resourceMetadataMappings?.[v];\n\t\t\t\t\tif (!resourceMetadata) {\n\t\t\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\t\tmessage: `missing resource_metadata mapping for ${v}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn `Bearer resource_metadata=${resourceMetadata}`;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.join(\", \");\n\t\tthrow new APIError(\n\t\t\t\"UNAUTHORIZED\",\n\t\t\t{\n\t\t\t\tmessage: error.message,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"WWW-Authenticate\": wwwAuthenticateValue,\n\t\t\t},\n\t\t);\n\t} else if (error instanceof Error) {\n\t\tthrow error;\n\t} else {\n\t\tthrow new Error(error as unknown as string);\n\t}\n}\n","import type { AuthContext, GenericEndpointContext } from \"@better-auth/core\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport { base64, base64Url } from \"@better-auth/utils/base64\";\nimport { createHash } from \"@better-auth/utils/hash\";\nimport {\n\tconstantTimeEqual,\n\tsymmetricDecrypt,\n\tsymmetricEncrypt,\n} from \"better-auth/crypto\";\nimport type { jwt } from \"better-auth/plugins\";\nimport type { Auth } from \"better-auth/types\";\nimport { APIError } from \"better-call\";\nimport type { oauthProvider } from \"..\";\nimport type {\n\tOAuthOptions,\n\tPrompt,\n\tSchemaClient,\n\tScope,\n\tStoreTokenType,\n} from \"../types\";\n\nclass TTLCache<K, V extends { expiresAt?: Date }> {\n\tprivate cache = new Map<K, V>();\n\tconstructor() {}\n\n\tset(key: K, value: V) {\n\t\tthis.cache.set(key, value);\n\t}\n\n\tget(key: K): V | undefined {\n\t\tconst entry = this.cache.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt && entry.expiresAt < new Date()) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry;\n\t}\n}\n\n/**\n * Gets the oAuth Provider Plugin\n * @internal\n */\nexport const getOAuthProviderPlugin = (ctx: AuthContext | Auth) => {\n\treturn ctx.options.plugins?.find(\n\t\t(plugin) => plugin.id === \"oauthProvider\",\n\t) as ReturnType<typeof oauthProvider>;\n};\n\n/**\n * Gets the JWT Plugin\n * @internal\n */\nexport const getJwtPlugin = (ctx: AuthContext): ReturnType<typeof jwt> => {\n\tconst plugin = ctx.getPlugin<ReturnType<typeof jwt>>(\"jwt\");\n\tif (!plugin) {\n\t\tthrow new BetterAuthError(\"jwt_config\", \"jwt plugin not found\");\n\t}\n\treturn plugin;\n};\n\nconst cachedTrustedClients = new TTLCache<string, SchemaClient<Scope[]>>();\n\n/**\n * Get a client by ID, checking trusted clients first, then database\n */\nexport async function getClient(\n\tctx: GenericEndpointContext,\n\toptions: OAuthOptions<Scope[]>,\n\tclientId: string,\n) {\n\tconst trustedClient = cachedTrustedClients.get(clientId);\n\tif (trustedClient) {\n\t\treturn Object.assign({}, trustedClient);\n\t}\n\n\tconst dbClient = await ctx.context.adapter.findOne<SchemaClient<Scope[]>>({\n\t\tmodel: options.schema?.oauthClient?.modelName ?? \"oauthClient\",\n\t\twhere: [{ field: \"clientId\", value: clientId }],\n\t});\n\n\tif (dbClient && options.cachedTrustedClients?.has(clientId)) {\n\t\tcachedTrustedClients.set(clientId, Object.assign({}, dbClient));\n\t}\n\n\treturn dbClient;\n}\n\n/**\n * Default client secret hasher using SHA-256\n *\n * @internal\n */\nconst defaultHasher = async (value: string) => {\n\tconst hash = await createHash(\"SHA-256\").digest(\n\t\tnew TextEncoder().encode(value),\n\t);\n\tconst hashed = base64Url.encode(new Uint8Array(hash), {\n\t\tpadding: false,\n\t});\n\treturn hashed;\n};\n\n/**\n * Decrypts a storedClientSecret for signing\n *\n * @internal\n */\nexport async function decryptStoredClientSecret(\n\tctx: GenericEndpointContext,\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeClientSecret\"],\n\tstoredClientSecret: string,\n) {\n\tif (storageMethod === \"encrypted\") {\n\t\treturn await symmetricDecrypt({\n\t\t\tkey: ctx.context.secret,\n\t\t\tdata: storedClientSecret,\n\t\t});\n\t} else if (typeof storageMethod === \"object\" && \"decrypt\" in storageMethod) {\n\t\treturn await storageMethod.decrypt(storedClientSecret);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported decryption storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Verify stored client secret against provided client secret\n *\n * @internal\n */\nasync function verifyStoredClientSecret(\n\tctx: GenericEndpointContext,\n\topts: OAuthOptions<Scope[]>,\n\tstoredClientSecret: string,\n\tclientSecret?: string,\n): Promise<boolean> {\n\tconst storageMethod =\n\t\topts.storeClientSecret ?? (opts.disableJwtPlugin ? \"encrypted\" : \"hashed\");\n\n\tif (clientSecret && opts.prefix?.clientSecret) {\n\t\tif (clientSecret.startsWith(opts.prefix?.clientSecret)) {\n\t\t\tclientSecret = clientSecret.replace(opts.prefix.clientSecret, \"\");\n\t\t} else {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\terror_description: \"invalid client_secret\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (storageMethod === \"hashed\") {\n\t\tconst hashedClientSecret = clientSecret\n\t\t\t? await defaultHasher(clientSecret)\n\t\t\t: undefined;\n\t\treturn (\n\t\t\t!!hashedClientSecret &&\n\t\t\tconstantTimeEqual(hashedClientSecret, storedClientSecret)\n\t\t);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\tif (storageMethod.verify) {\n\t\t\treturn (\n\t\t\t\t!!clientSecret &&\n\t\t\t\t(await storageMethod.verify(clientSecret, storedClientSecret))\n\t\t\t);\n\t\t} else {\n\t\t\tconst hashedClientSecret = clientSecret\n\t\t\t\t? await storageMethod.hash(clientSecret)\n\t\t\t\t: undefined;\n\t\t\treturn (\n\t\t\t\t!!hashedClientSecret &&\n\t\t\t\tconstantTimeEqual(hashedClientSecret, storedClientSecret)\n\t\t\t);\n\t\t}\n\t} else if (\n\t\tstorageMethod === \"encrypted\" ||\n\t\t(typeof storageMethod === \"object\" && \"decrypt\" in storageMethod)\n\t) {\n\t\tconst decryptedClientSecret = await decryptStoredClientSecret(\n\t\t\tctx,\n\t\t\tstorageMethod,\n\t\t\tstoredClientSecret,\n\t\t);\n\t\treturn (\n\t\t\t!!clientSecret && constantTimeEqual(decryptedClientSecret, clientSecret)\n\t\t);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported verify storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Store client secret according to the configured storage method\n *\n * @internal\n */\nexport async function storeClientSecret(\n\tctx: GenericEndpointContext,\n\topts: OAuthOptions<Scope[]>,\n\tclientSecret: string,\n) {\n\tconst storageMethod =\n\t\topts.storeClientSecret ?? (opts.disableJwtPlugin ? \"encrypted\" : \"hashed\");\n\n\tif (storageMethod === \"encrypted\") {\n\t\treturn await symmetricEncrypt({\n\t\t\tkey: ctx.context.secret,\n\t\t\tdata: clientSecret,\n\t\t});\n\t} else if (storageMethod === \"hashed\") {\n\t\treturn await defaultHasher(clientSecret);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\treturn await storageMethod.hash(clientSecret);\n\t} else if (typeof storageMethod === \"object\" && \"encrypt\" in storageMethod) {\n\t\treturn await storageMethod.encrypt(clientSecret);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`Unsupported storeClientSecret type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Stores a token value (ie opaque tokens, refresh tokens, transaction tokens, verification codes)\n * on the database in a secure hashed format.\n *\n * @internal\n */\nexport async function storeToken(\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeTokens\"] = \"hashed\",\n\ttoken: string,\n\ttype: StoreTokenType,\n) {\n\tif (storageMethod === \"hashed\") {\n\t\treturn await defaultHasher(token);\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\treturn await storageMethod.hash(token, type);\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`storeToken: unsupported storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Gets a hashed token value to find on the database.\n *\n * @internal\n */\nexport async function getStoredToken(\n\tstorageMethod: OAuthOptions<Scope[]>[\"storeTokens\"] = \"hashed\",\n\ttoken: string,\n\ttype: StoreTokenType,\n) {\n\tif (storageMethod === \"hashed\") {\n\t\tconst hashedToken = await defaultHasher(token);\n\t\treturn hashedToken;\n\t} else if (typeof storageMethod === \"object\" && \"hash\" in storageMethod) {\n\t\tconst hashedToken = await storageMethod.hash(token, type);\n\t\treturn hashedToken;\n\t}\n\n\tthrow new BetterAuthError(\n\t\t`getStoredToken: unsupported storageMethod type '${storageMethod}'`,\n\t);\n}\n\n/**\n * Converts a BASIC authorization header\n * into its client_id and client_secret representation\n *\n * @internal\n */\nexport function basicToClientCredentials(authorization: string) {\n\tif (authorization.startsWith(\"Basic \")) {\n\t\tconst encoded = authorization.replace(\"Basic \", \"\");\n\t\tconst decoded = new TextDecoder().decode(base64.decode(encoded));\n\t\tif (!decoded.includes(\":\")) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t\tconst [id, secret] = decoded.split(\":\", 2);\n\t\tif (!id || !secret) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\terror: \"invalid_client\",\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tclient_id: id,\n\t\t\tclient_secret: secret,\n\t\t};\n\t}\n}\n\n/**\n * Validates client credentials failing on mismatches\n * and incorrectly provided information\n *\n * @internal\n */\nexport async function validateClientCredentials(\n\tctx: GenericEndpointContext,\n\toptions: OAuthOptions<Scope[]>,\n\tclientId: string,\n\tclientSecret?: string, // optional because required if client is confidential or this value is defined\n\tscopes?: string[], // checks requested scopes against allowed scopes\n) {\n\tconst client = await getClient(ctx, options, clientId);\n\tif (!client) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"missing client\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\tif (client.disabled) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"client is disabled\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Require secret for confidential clients\n\tif (!client.public && !clientSecret) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"client secret must be provided\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Secret should not be received\n\tif (clientSecret && !client.clientSecret) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\terror_description: \"public client, client secret should not be received\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// Compare Secrets when secret is provided\n\tif (\n\t\tclientSecret &&\n\t\t!(await verifyStoredClientSecret(\n\t\t\tctx,\n\t\t\toptions,\n\t\t\tclient.clientSecret!,\n\t\t\tclientSecret,\n\t\t))\n\t) {\n\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\terror_description: \"invalid client_secret\",\n\t\t\terror: \"invalid_client\",\n\t\t});\n\t}\n\n\t// If scopes set, check against client allowed scopes\n\tif (scopes && client.scopes) {\n\t\tconst validScopes = new Set(client.scopes);\n\t\tfor (const sc of scopes) {\n\t\t\tif (!validScopes.has(sc)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror_description: `client does not allow scope ${sc}`,\n\t\t\t\t\terror: \"invalid_scope\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn client;\n}\n\n/**\n * Parse client metadata that may be stored as JSON string or already parsed object.\n * Handles database adapters that auto-parse JSON columns.\n *\n * @internal\n */\nexport function parseClientMetadata(\n\tmetadata: string | object | undefined,\n): object | undefined {\n\tif (!metadata) return undefined;\n\treturn typeof metadata === \"string\" ? JSON.parse(metadata) : metadata;\n}\n\n/**\n * Parse space-separated prompt string into a set of prompts\n *\n * @param prompt\n */\nexport function parsePrompt(prompt: string) {\n\tconst prompts = prompt.split(\" \").map((p) => p.trim());\n\tconst set = new Set<Prompt>();\n\tfor (const p of prompts) {\n\t\tif (\n\t\t\tp === \"login\" ||\n\t\t\tp === \"consent\" ||\n\t\t\tp === \"create\" ||\n\t\t\tp === \"select_account\" ||\n\t\t\tp === \"none\"\n\t\t) {\n\t\t\tset.add(p);\n\t\t}\n\t}\n\treturn new Set(set);\n}\n\n/**\n * Deletes a prompt value\n *\n * @param ctx\n * @param prompt - the prompt value to delete\n */\nexport function deleteFromPrompt(query: URLSearchParams, prompt: Prompt) {\n\tconst prompts = query.get(\"prompt\")?.split(\" \");\n\tconst foundPrompt = prompts?.findIndex((v) => v === prompt) ?? -1;\n\tif (foundPrompt >= 0) {\n\t\tprompts?.splice(foundPrompt, 1);\n\t\tprompts?.length\n\t\t\t? query.set(\"prompt\", prompts.join(\" \"))\n\t\t\t: query.delete(\"prompt\");\n\t}\n\treturn Object.fromEntries(query);\n}\n"],"mappings":"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"}
|