@better-auth/oauth-provider 1.5.0-beta.2 → 1.5.0-beta.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/LICENSE.md CHANGED
@@ -1,17 +1,20 @@
1
1
  The MIT License (MIT)
2
2
  Copyright (c) 2024 - present, Bereket Engida
3
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:
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the Software), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is furnished to do so,
9
+ subject to the following conditions:
9
10
 
10
- The above copyright notice and this permission notice shall be included in all copies or
11
- substantial portions of the Software.
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
12
13
 
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.
14
+ THE SOFTWARE IS PROVIDED AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ DEALINGS IN THE SOFTWARE.
@@ -1,4 +1,4 @@
1
- import { a as getJwtPlugin, f as handleMcpErrors, o as getOAuthProviderPlugin } from "./utils-CSiY9oUk.mjs";
1
+ import { a as getJwtPlugin, o as getOAuthProviderPlugin, p as handleMcpErrors } from "./utils-CAYiYjbw.mjs";
2
2
  import { verifyAccessToken } from "better-auth/oauth2";
3
3
  import { APIError } from "better-call";
4
4
  import { BetterAuthError } from "@better-auth/core/error";
@@ -6,16 +6,27 @@ import { logger } from "@better-auth/core/env";
6
6
 
7
7
  //#region src/client-resource.ts
8
8
  const oauthProviderResourceClient = (auth) => {
9
- const oauthProviderOptions = (auth ? getOAuthProviderPlugin(auth) : void 0)?.options;
10
- const jwtPluginOptions = (auth && !oauthProviderOptions?.disableJwtPlugin ? getJwtPlugin(auth) : void 0)?.options;
9
+ let oauthProviderPlugin;
10
+ const getOauthProviderPlugin = async () => {
11
+ if (!oauthProviderPlugin) oauthProviderPlugin = auth ? getOAuthProviderPlugin(await auth.$context) : void 0;
12
+ return oauthProviderPlugin;
13
+ };
14
+ let jwtPlugin;
15
+ const getJwtPluginOptions = async () => {
16
+ if (!jwtPlugin) jwtPlugin = auth && !(await getOauthProviderPlugin())?.options?.disableJwtPlugin ? getJwtPlugin(await auth.$context) : void 0;
17
+ return jwtPlugin?.options;
18
+ };
19
+ const getAuthorizationServer = async () => {
20
+ return (await getJwtPluginOptions())?.jwt?.issuer ?? authServerBaseUrl;
21
+ };
11
22
  const authServerBaseUrl = auth?.options.baseURL;
12
23
  const authServerBasePath = auth?.options.basePath;
13
- const authorizationServer = jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
14
24
  return {
15
25
  id: "oauth-provider-resource-client",
16
26
  getActions() {
17
27
  return {
18
28
  verifyAccessToken: (async (token, opts) => {
29
+ const jwtPluginOptions = await getJwtPluginOptions();
19
30
  const audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;
20
31
  const issuer = opts?.verifyOptions?.issuer ?? jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
21
32
  if (!audience) throw Error("please define opts.verifyOptions.audience");
@@ -43,6 +54,7 @@ const oauthProviderResourceClient = (auth) => {
43
54
  }),
44
55
  getProtectedResourceMetadata: (async (overrides, opts) => {
45
56
  const resource = overrides?.resource ?? authServerBaseUrl;
57
+ const oauthProviderOptions = (await getOauthProviderPlugin())?.options;
46
58
  if (!resource) throw Error("missing required resource");
47
59
  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
60
  if (overrides?.scopes_supported) {
@@ -60,6 +72,7 @@ const oauthProviderResourceClient = (auth) => {
60
72
  if (!allValidScopes.has(sc)) throw new BetterAuthError(`Unsupported scope ${sc}. If external, please add to "externalScopes"`);
61
73
  }
62
74
  }
75
+ const authorizationServer = await getAuthorizationServer();
63
76
  return {
64
77
  resource,
65
78
  authorization_servers: authorizationServer ? [authorizationServer] : void 0,
package/dist/client.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./oauth-BrFoF22H.mjs";
2
- import { t as oauthProvider } from "./oauth-C1OnEiU-.mjs";
2
+ import { t as oauthProvider } from "./oauth-BW67CKnu.mjs";
3
3
  import * as _better_fetch_fetch0 from "@better-fetch/fetch";
4
4
 
5
5
  //#region src/client.d.ts
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
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-BrFoF22H.mjs";
2
- import { t as oauthProvider } from "./oauth-C1OnEiU-.mjs";
2
+ import { t as oauthProvider } from "./oauth-BW67CKnu.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as getJwtPlugin, c as parsePrompt, d as validateClientCredentials, i as getClient, l as storeClientSecret, n as decryptStoredClientSecret, p as mcpHandler, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as storeToken } from "./utils-CSiY9oUk.mjs";
1
+ import { a as getJwtPlugin, c as parseClientMetadata, d as storeToken, f as validateClientCredentials, i as getClient, l as parsePrompt, m as mcpHandler, n as decryptStoredClientSecret, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as storeClientSecret } from "./utils-CAYiYjbw.mjs";
2
2
  import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
3
3
  import { APIError } from "better-call";
4
4
  import { BetterAuthError } from "@better-auth/core/error";
@@ -516,7 +516,7 @@ async function createJwtAccessToken(ctx, opts, user, client, audience, scopes, r
516
516
  scopes,
517
517
  resource: ctx.body.resource,
518
518
  referenceId,
519
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
519
+ metadata: parseClientMetadata(client.metadata)
520
520
  }) : {};
521
521
  const jwtPluginOptions = getJwtPlugin(ctx.context).options;
522
522
  return signJWT(ctx, {
@@ -547,7 +547,7 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId)
547
547
  const customClaims = opts.customIdTokenClaims ? await opts.customIdTokenClaims({
548
548
  user,
549
549
  scopes,
550
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
550
+ metadata: parseClientMetadata(client.metadata)
551
551
  }) : {};
552
552
  const jwtPluginOptions = opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context).options;
553
553
  const payload = {
@@ -857,7 +857,7 @@ async function handleClientCredentialsGrant(ctx, opts) {
857
857
  const customClaims = opts.customAccessTokenClaims ? await opts.customAccessTokenClaims({
858
858
  scopes: requestedScopes,
859
859
  resource: ctx.body.resource,
860
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
860
+ metadata: parseClientMetadata(client.metadata)
861
861
  }) : {};
862
862
  const accessToken = audience && !opts.disableJwtPlugin ? await signJWT(ctx, {
863
863
  options: jwtPluginOptions,
@@ -1069,7 +1069,7 @@ async function validateOpaqueAccessToken(ctx, opts, token, clientId) {
1069
1069
  user,
1070
1070
  scopes: accessToken.scopes,
1071
1071
  referenceId: accessToken?.referenceId,
1072
- metadata: client?.metadata ? JSON.parse(client.metadata) : void 0
1072
+ metadata: parseClientMetadata(client?.metadata)
1073
1073
  }) : {};
1074
1074
  const jwtPluginOptions = (opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context))?.options;
1075
1075
  return {
@@ -1420,14 +1420,19 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
1420
1420
  * @returns
1421
1421
  */
1422
1422
  function oauthToSchema(input) {
1423
- const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks: _jwks, jwks_uri: _jwksUri, software_id: softwareId, software_version: softwareVersion, software_statement: softwareStatement, redirect_uris: redirectUris, post_logout_redirect_uris: postLogoutRedirectUris, token_endpoint_auth_method: tokenEndpointAuthMethod, grant_types: grantTypes, response_types: responseTypes, public: _public, type, disabled, skip_consent: skipConsent, enable_end_session: enableEndSession, reference_id: referenceId, ...rest } = input;
1423
+ const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks: _jwks, jwks_uri: _jwksUri, software_id: softwareId, software_version: softwareVersion, software_statement: softwareStatement, redirect_uris: redirectUris, post_logout_redirect_uris: postLogoutRedirectUris, token_endpoint_auth_method: tokenEndpointAuthMethod, grant_types: grantTypes, response_types: responseTypes, public: _public, type, disabled, skip_consent: skipConsent, enable_end_session: enableEndSession, reference_id: referenceId, metadata: inputMetadata, ...rest } = input;
1424
1424
  const expiresAt = _expiresAt ? /* @__PURE__ */ new Date(_expiresAt * 1e3) : void 0;
1425
1425
  const createdAt = _createdAt ? /* @__PURE__ */ new Date(_createdAt * 1e3) : void 0;
1426
+ const scopes = _scope?.split(" ");
1427
+ const metadataObj = {
1428
+ ...rest && Object.keys(rest).length ? rest : {},
1429
+ ...inputMetadata && typeof inputMetadata === "object" ? inputMetadata : {}
1430
+ };
1426
1431
  return {
1427
1432
  clientId,
1428
1433
  clientSecret,
1429
1434
  disabled,
1430
- scopes: _scope?.split(" "),
1435
+ scopes,
1431
1436
  userId,
1432
1437
  createdAt,
1433
1438
  expiresAt,
@@ -1450,7 +1455,7 @@ function oauthToSchema(input) {
1450
1455
  skipConsent,
1451
1456
  enableEndSession,
1452
1457
  referenceId,
1453
- metadata: rest && Object.keys(rest).length ? JSON.stringify(rest) : void 0
1458
+ metadata: Object.keys(metadataObj).length ? JSON.stringify(metadataObj) : void 0
1454
1459
  };
1455
1460
  }
1456
1461
  /**
@@ -1465,7 +1470,7 @@ function schemaToOAuth(input) {
1465
1470
  const _createdAt = createdAt ? Math.round(createdAt.getTime() / 1e3) : void 0;
1466
1471
  const _scopes = scopes?.join(" ");
1467
1472
  return {
1468
- ...metadata ? JSON.parse(metadata) : void 0,
1473
+ ...parseClientMetadata(metadata),
1469
1474
  client_id: clientId,
1470
1475
  client_secret: clientSecret ?? void 0,
1471
1476
  client_secret_expires_at: clientSecret ? _expiresAt ?? 0 : void 0,
@@ -2857,12 +2862,12 @@ const oauthProvider = (options) => {
2857
2862
  if (opts.disableJwtPlugin && (opts.storeClientSecret === "hashed" || typeof opts.storeClientSecret === "object" && "hash" in opts.storeClientSecret)) throw new BetterAuthError("unable to store hashed secrets because id tokens will be signed with secret");
2858
2863
  if (!opts.disableJwtPlugin && (opts.storeClientSecret === "encrypted" || typeof opts.storeClientSecret === "object" && ("encrypt" in opts.storeClientSecret || "decrypt" in opts.storeClientSecret))) throw new BetterAuthError("encryption method not recommended, please use 'hashed' or the 'hash' function");
2859
2864
  return {
2860
- id: "oauthProvider",
2865
+ id: "oauth-provider",
2861
2866
  options: opts,
2862
2867
  init: (ctx) => {
2863
2868
  if (ctx.options.session && !ctx.options.session.storeSessionInDatabase) throw new BetterAuthError("OAuth Provider requires `session.storeSessionInDatabase: true` when using secondaryStorage");
2864
2869
  if (!opts.disableJwtPlugin) {
2865
- const issuer = getJwtPlugin(ctx).options?.jwt?.issuer ?? ctx.baseURL;
2870
+ const issuer = (getJwtPlugin(ctx)?.options)?.jwt?.issuer ?? ctx.baseURL;
2866
2871
  const issuerPath = new URL(issuer).pathname;
2867
2872
  if (!opts.silenceWarnings?.oauthAuthServerConfig && !(ctx.options.basePath === "/" && issuerPath === "/")) logger.warn(`Please ensure '/.well-known/oauth-authorization-server${issuerPath === "/" ? "" : issuerPath}' exists. Upon completion, clear with silenceWarnings.oauthAuthServerConfig.`);
2868
2873
  if (!opts.silenceWarnings?.openidConfig && ctx.options.basePath !== issuerPath && opts.scopes?.includes("openid")) logger.warn(`Please ensure '${issuerPath}${issuerPath.endsWith("/") ? "" : "/"}.well-known/openid-configuration' exists. Upon completion, clear with silenceWarnings.openidConfig.`);
@@ -2915,7 +2920,7 @@ const oauthProvider = (options) => {
2915
2920
  metadata: { SERVER_ONLY: true }
2916
2921
  }, async (ctx) => {
2917
2922
  if (opts.scopes && opts.scopes.includes("openid")) return oidcServerMetadata(ctx, opts);
2918
- else return authServerMetadata(ctx, opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context).options, { scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes });
2923
+ else return authServerMetadata(ctx, opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context)?.options, { scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes });
2919
2924
  }),
2920
2925
  getOpenIdConfig: createAuthEndpoint("/.well-known/openid-configuration", {
2921
2926
  method: "GET",
@@ -7,7 +7,13 @@ import * as jose0 from "jose";
7
7
  import * as better_auth0 from "better-auth";
8
8
 
9
9
  //#region src/oauth.d.ts
10
-
10
+ declare module "@better-auth/core" {
11
+ interface BetterAuthPluginRegistry<Auth, Context> {
12
+ "oauth-provider": {
13
+ creator: typeof oauthProvider;
14
+ };
15
+ }
16
+ }
11
17
  /**
12
18
  * oAuth 2.1 provider plugin for Better Auth.
13
19
  *
@@ -16,10 +22,8 @@ import * as better_auth0 from "better-auth";
16
22
  * @returns A Better Auth plugin.
17
23
  */
18
24
  declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
19
- id: "oauthProvider";
20
- options: O & {
21
- claims?: string[];
22
- };
25
+ id: "oauth-provider";
26
+ options: NoInfer<O>;
23
27
  init: (ctx: better_auth0.AuthContext) => void;
24
28
  hooks: {
25
29
  before: {
@@ -80,14 +80,14 @@ var TTLCache = class {
80
80
  * @internal
81
81
  */
82
82
  const getOAuthProviderPlugin = (ctx) => {
83
- return ctx.options.plugins?.find((plugin) => plugin.id === "oauthProvider");
83
+ return ctx.getPlugin("oauth-provider");
84
84
  };
85
85
  /**
86
86
  * Gets the JWT Plugin
87
87
  * @internal
88
88
  */
89
89
  const getJwtPlugin = (ctx) => {
90
- const plugin = ctx.options.plugins?.find((plugin$1) => plugin$1.id === "jwt");
90
+ const plugin = ctx.getPlugin("jwt");
91
91
  if (!plugin) throw new BetterAuthError("jwt_config", "jwt plugin not found");
92
92
  return plugin;
93
93
  };
@@ -256,6 +256,16 @@ async function validateClientCredentials(ctx, options, clientId, clientSecret, s
256
256
  return client;
257
257
  }
258
258
  /**
259
+ * Parse client metadata that may be stored as JSON string or already parsed object.
260
+ * Handles database adapters that auto-parse JSON columns.
261
+ *
262
+ * @internal
263
+ */
264
+ function parseClientMetadata(metadata) {
265
+ if (!metadata) return void 0;
266
+ return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
267
+ }
268
+ /**
259
269
  * Parse space-separated prompt string into a set of prompts
260
270
  *
261
271
  * @param prompt
@@ -283,4 +293,4 @@ function deleteFromPrompt(query, prompt) {
283
293
  }
284
294
 
285
295
  //#endregion
286
- export { getJwtPlugin as a, parsePrompt as c, validateClientCredentials as d, handleMcpErrors as f, getClient as i, storeClientSecret as l, decryptStoredClientSecret as n, getOAuthProviderPlugin as o, mcpHandler as p, deleteFromPrompt as r, getStoredToken as s, basicToClientCredentials as t, storeToken as u };
296
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/oauth-provider",
3
- "version": "1.5.0-beta.2",
3
+ "version": "1.5.0-beta.3",
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.24.2",
43
43
  "listhen": "^1.9.0",
44
44
  "tsdown": "^0.17.2",
45
- "@better-auth/core": "1.5.0-beta.2",
46
- "better-auth": "1.5.0-beta.2"
45
+ "@better-auth/core": "1.5.0-beta.3",
46
+ "better-auth": "1.5.0-beta.3"
47
47
  },
48
48
  "dependencies": {
49
49
  "jose": "^6.1.0",
@@ -52,9 +52,9 @@
52
52
  "peerDependencies": {
53
53
  "@better-auth/utils": "0.3.0",
54
54
  "@better-fetch/fetch": "1.1.21",
55
- "better-call": "1.1.7",
56
- "@better-auth/core": "1.5.0-beta.2",
57
- "better-auth": "1.5.0-beta.2"
55
+ "better-call": "1.1.8",
56
+ "@better-auth/core": "1.5.0-beta.3",
57
+ "better-auth": "1.5.0-beta.3"
58
58
  },
59
59
  "files": [
60
60
  "dist"