@better-auth/oauth-provider 1.5.0-beta.1 → 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 ResourceServerMetadata } from "./oauth-kjs13QN6.mjs";
1
+ import { a as ResourceServerMetadata } from "./oauth-BrFoF22H.mjs";
2
2
  import { JWTPayload, JWTVerifyOptions } from "jose";
3
3
  import { Auth } from "better-auth/types";
4
4
 
@@ -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,21 +6,32 @@ 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");
22
33
  if (!issuer) throw Error("please define opts.verifyOptions.issuer");
23
- const jwksUrl = opts?.jwksUrl ?? jwtPluginOptions?.jwks?.remoteUrl ?? (authServerBaseUrl ? `${authServerBaseUrl + (authServerBasePath ?? "")}/jwks` : void 0);
34
+ const jwksUrl = opts?.jwksUrl ?? jwtPluginOptions?.jwks?.remoteUrl ?? (authServerBaseUrl ? `${authServerBaseUrl + (authServerBasePath ?? "")}${jwtPluginOptions?.jwks?.jwksPath ?? "/jwks"}` : void 0);
24
35
  const introspectUrl = opts?.remoteVerify?.introspectUrl ?? (authServerBaseUrl ? `${authServerBaseUrl}${authServerBasePath ?? ""}/oauth2/introspect` : void 0);
25
36
  try {
26
37
  if (!token?.length) throw new APIError("UNAUTHORIZED", { message: "missing authorization header" });
@@ -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
- import "./oauth-kjs13QN6.mjs";
2
- import { t as oauthProvider } from "./oauth-Cex7QJsW.mjs";
1
+ import "./oauth-BrFoF22H.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,7 +1,7 @@
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";
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-BW67CKnu.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
- import { JWSAlgorithms, JwtOptions } from "better-auth/plugins/jwt";
4
+ import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
6
6
  import { GenericEndpointContext } from "@better-auth/core";
7
7
 
package/dist/index.mjs CHANGED
@@ -1,8 +1,7 @@
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";
5
- import { createHash } from "@better-auth/utils/hash";
6
5
  import { constantTimeEqual, generateRandomString, makeSignature } from "better-auth/crypto";
7
6
  import { defineRequestState } from "@better-auth/core/context";
8
7
  import { logger } from "@better-auth/core/env";
@@ -10,11 +9,8 @@ import { APIError as APIError$1, createAuthEndpoint, createAuthMiddleware, getOA
10
9
  import { parseSetCookieHeader } from "better-auth/cookies";
11
10
  import { mergeSchema } from "better-auth/db";
12
11
  import * as z from "zod";
13
- import { signJWT, toExpJWT } from "better-auth/plugins/jwt";
12
+ import { signJWT, toExpJWT } from "better-auth/plugins";
14
13
  import { SignJWT, compactVerify, createLocalJWKSet, decodeJwt } from "jose";
15
- import "@better-auth/utils";
16
- import "@better-auth/utils/hex";
17
- import { createRandomStringGenerator } from "@better-auth/utils/random";
18
14
 
19
15
  //#region src/metadata.ts
20
16
  function authServerMetadata(ctx, opts, overrides) {
@@ -24,7 +20,7 @@ function authServerMetadata(ctx, opts, overrides) {
24
20
  issuer: opts?.jwt?.issuer ?? baseURL,
25
21
  authorization_endpoint: `${baseURL}/oauth2/authorize`,
26
22
  token_endpoint: `${baseURL}/oauth2/token`,
27
- jwks_uri: overrides?.jwt_disabled ? void 0 : opts?.jwks?.remoteUrl ?? `${baseURL}/jwks`,
23
+ jwks_uri: overrides?.jwt_disabled ? void 0 : opts?.jwks?.remoteUrl ?? `${baseURL}${opts?.jwks?.jwksPath ?? "/jwks"}`,
28
24
  registration_endpoint: `${baseURL}/oauth2/register`,
29
25
  introspection_endpoint: `${baseURL}/oauth2/introspect`,
30
26
  revocation_endpoint: `${baseURL}/oauth2/revoke`,
@@ -520,7 +516,7 @@ async function createJwtAccessToken(ctx, opts, user, client, audience, scopes, r
520
516
  scopes,
521
517
  resource: ctx.body.resource,
522
518
  referenceId,
523
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
519
+ metadata: parseClientMetadata(client.metadata)
524
520
  }) : {};
525
521
  const jwtPluginOptions = getJwtPlugin(ctx.context).options;
526
522
  return signJWT(ctx, {
@@ -551,7 +547,7 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId)
551
547
  const customClaims = opts.customIdTokenClaims ? await opts.customIdTokenClaims({
552
548
  user,
553
549
  scopes,
554
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
550
+ metadata: parseClientMetadata(client.metadata)
555
551
  }) : {};
556
552
  const jwtPluginOptions = opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context).options;
557
553
  const payload = {
@@ -861,7 +857,7 @@ async function handleClientCredentialsGrant(ctx, opts) {
861
857
  const customClaims = opts.customAccessTokenClaims ? await opts.customAccessTokenClaims({
862
858
  scopes: requestedScopes,
863
859
  resource: ctx.body.resource,
864
- metadata: client.metadata ? JSON.parse(client.metadata) : void 0
860
+ metadata: parseClientMetadata(client.metadata)
865
861
  }) : {};
866
862
  const accessToken = audience && !opts.disableJwtPlugin ? await signJWT(ctx, {
867
863
  options: jwtPluginOptions,
@@ -1073,7 +1069,7 @@ async function validateOpaqueAccessToken(ctx, opts, token, clientId) {
1073
1069
  user,
1074
1070
  scopes: accessToken.scopes,
1075
1071
  referenceId: accessToken?.referenceId,
1076
- metadata: client?.metadata ? JSON.parse(client.metadata) : void 0
1072
+ metadata: parseClientMetadata(client?.metadata)
1077
1073
  }) : {};
1078
1074
  const jwtPluginOptions = (opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context))?.options;
1079
1075
  return {
@@ -1223,7 +1219,7 @@ async function rpInitiatedLogoutEndpoint(ctx, opts) {
1223
1219
  const { id_token_hint, client_id, post_logout_redirect_uri, state } = ctx.query;
1224
1220
  const baseURL = ctx.context.baseURL;
1225
1221
  const jwtPluginOptions = (opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context))?.options;
1226
- const jwksUrl = jwtPluginOptions?.jwks?.remoteUrl ?? `${baseURL}/jwks`;
1222
+ const jwksUrl = jwtPluginOptions?.jwks?.remoteUrl ?? `${baseURL}${jwtPluginOptions?.jwks?.jwksPath ?? "/jwks"}`;
1227
1223
  let clientId = client_id;
1228
1224
  if (!clientId) {
1229
1225
  let decoded;
@@ -1323,152 +1319,6 @@ async function rpInitiatedLogoutEndpoint(ctx, opts) {
1323
1319
  }
1324
1320
  }
1325
1321
 
1326
- //#endregion
1327
- //#region ../better-auth/src/crypto/jwt.ts
1328
- const info = new Uint8Array([
1329
- 66,
1330
- 101,
1331
- 116,
1332
- 116,
1333
- 101,
1334
- 114,
1335
- 65,
1336
- 117,
1337
- 116,
1338
- 104,
1339
- 46,
1340
- 106,
1341
- 115,
1342
- 32,
1343
- 71,
1344
- 101,
1345
- 110,
1346
- 101,
1347
- 114,
1348
- 97,
1349
- 116,
1350
- 101,
1351
- 100,
1352
- 32,
1353
- 69,
1354
- 110,
1355
- 99,
1356
- 114,
1357
- 121,
1358
- 112,
1359
- 116,
1360
- 105,
1361
- 111,
1362
- 110,
1363
- 32,
1364
- 75,
1365
- 101,
1366
- 121
1367
- ]);
1368
-
1369
- //#endregion
1370
- //#region ../better-auth/src/crypto/random.ts
1371
- const generateRandomString$1 = createRandomStringGenerator("a-z", "0-9", "A-Z", "-_");
1372
-
1373
- //#endregion
1374
- //#region ../better-auth/src/utils/time.ts
1375
- const SEC = 1e3;
1376
- const MIN = SEC * 60;
1377
- const HOUR = MIN * 60;
1378
- const DAY = HOUR * 24;
1379
- const WEEK = DAY * 7;
1380
- const MONTH = DAY * 30;
1381
- const YEAR = DAY * 365.25;
1382
- const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)(?: (ago|from now))?$/i;
1383
- function parse(value) {
1384
- const match = REGEX.exec(value);
1385
- if (!match || match[4] && match[1]) throw new TypeError(`Invalid time string format: "${value}". Use formats like "7d", "30m", "1 hour", etc.`);
1386
- const n = parseFloat(match[2]);
1387
- const unit = match[3].toLowerCase();
1388
- let result;
1389
- switch (unit) {
1390
- case "years":
1391
- case "year":
1392
- case "yrs":
1393
- case "yr":
1394
- case "y":
1395
- result = n * YEAR;
1396
- break;
1397
- case "months":
1398
- case "month":
1399
- case "mo":
1400
- result = n * MONTH;
1401
- break;
1402
- case "weeks":
1403
- case "week":
1404
- case "w":
1405
- result = n * WEEK;
1406
- break;
1407
- case "days":
1408
- case "day":
1409
- case "d":
1410
- result = n * DAY;
1411
- break;
1412
- case "hours":
1413
- case "hour":
1414
- case "hrs":
1415
- case "hr":
1416
- case "h":
1417
- result = n * HOUR;
1418
- break;
1419
- case "minutes":
1420
- case "minute":
1421
- case "mins":
1422
- case "min":
1423
- case "m":
1424
- result = n * MIN;
1425
- break;
1426
- case "seconds":
1427
- case "second":
1428
- case "secs":
1429
- case "sec":
1430
- case "s":
1431
- result = n * SEC;
1432
- break;
1433
- default: throw new TypeError(`Unknown time unit: "${unit}"`);
1434
- }
1435
- if (match[1] === "-" || match[4] === "ago") return -result;
1436
- return result;
1437
- }
1438
- /**
1439
- * Parse a time string and return the value in seconds.
1440
- *
1441
- * @param value - A time string like "7d", "30m", "1 hour", "2 hours ago"
1442
- * @returns The parsed value in seconds (rounded)
1443
- * @throws TypeError if the string format is invalid
1444
- *
1445
- * @example
1446
- * sec("1d") // 86400
1447
- * sec("2 hours") // 7200
1448
- * sec("-30s") // -30
1449
- * sec("2 hours ago") // -7200
1450
- */
1451
- function sec(value) {
1452
- return Math.round(parse(value) / 1e3);
1453
- }
1454
-
1455
- //#endregion
1456
- //#region ../better-auth/src/plugins/jwt/utils.ts
1457
- /**
1458
- * Converts an expirationTime to ISO seconds expiration time (the format of JWT exp)
1459
- *
1460
- * See https://github.com/panva/jose/blob/main/src/lib/jwt_claims_set.ts#L245
1461
- *
1462
- * @param expirationTime - see options.jwt.expirationTime
1463
- * @param iat - the iat time to consolidate on
1464
- * @returns
1465
- */
1466
- function toExpJWT$1(expirationTime, iat) {
1467
- if (typeof expirationTime === "number") return expirationTime;
1468
- else if (expirationTime instanceof Date) return Math.floor(expirationTime.getTime() / 1e3);
1469
- else return iat + sec(expirationTime);
1470
- }
1471
-
1472
1322
  //#endregion
1473
1323
  //#region src/register.ts
1474
1324
  async function registerEndpoint(ctx, opts) {
@@ -1540,7 +1390,7 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
1540
1390
  disabled: void 0,
1541
1391
  jwks: void 0,
1542
1392
  jwks_uri: void 0,
1543
- client_secret_expires_at: storedClientSecret ? settings.isRegister && opts?.clientRegistrationClientSecretExpiration ? toExpJWT$1(opts.clientRegistrationClientSecretExpiration, iat) : 0 : void 0,
1393
+ client_secret_expires_at: storedClientSecret ? settings.isRegister && opts?.clientRegistrationClientSecretExpiration ? toExpJWT(opts.clientRegistrationClientSecretExpiration, iat) : 0 : void 0,
1544
1394
  client_id: clientId,
1545
1395
  client_secret: storedClientSecret,
1546
1396
  client_id_issued_at: iat,
@@ -1570,14 +1420,19 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
1570
1420
  * @returns
1571
1421
  */
1572
1422
  function oauthToSchema(input) {
1573
- 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;
1574
1424
  const expiresAt = _expiresAt ? /* @__PURE__ */ new Date(_expiresAt * 1e3) : void 0;
1575
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
+ };
1576
1431
  return {
1577
1432
  clientId,
1578
1433
  clientSecret,
1579
1434
  disabled,
1580
- scopes: _scope?.split(" "),
1435
+ scopes,
1581
1436
  userId,
1582
1437
  createdAt,
1583
1438
  expiresAt,
@@ -1600,7 +1455,7 @@ function oauthToSchema(input) {
1600
1455
  skipConsent,
1601
1456
  enableEndSession,
1602
1457
  referenceId,
1603
- metadata: rest && Object.keys(rest).length ? JSON.stringify(rest) : void 0
1458
+ metadata: Object.keys(metadataObj).length ? JSON.stringify(metadataObj) : void 0
1604
1459
  };
1605
1460
  }
1606
1461
  /**
@@ -1615,7 +1470,7 @@ function schemaToOAuth(input) {
1615
1470
  const _createdAt = createdAt ? Math.round(createdAt.getTime() / 1e3) : void 0;
1616
1471
  const _scopes = scopes?.join(" ");
1617
1472
  return {
1618
- ...metadata ? JSON.parse(metadata) : void 0,
1473
+ ...parseClientMetadata(metadata),
1619
1474
  client_id: clientId,
1620
1475
  client_secret: clientSecret ?? void 0,
1621
1476
  client_secret_expires_at: clientSecret ? _expiresAt ?? 0 : void 0,
@@ -3007,11 +2862,12 @@ const oauthProvider = (options) => {
3007
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");
3008
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");
3009
2864
  return {
3010
- id: "oauthProvider",
2865
+ id: "oauth-provider",
3011
2866
  options: opts,
3012
2867
  init: (ctx) => {
2868
+ if (ctx.options.session && !ctx.options.session.storeSessionInDatabase) throw new BetterAuthError("OAuth Provider requires `session.storeSessionInDatabase: true` when using secondaryStorage");
3013
2869
  if (!opts.disableJwtPlugin) {
3014
- const issuer = getJwtPlugin(ctx).options?.jwt?.issuer ?? ctx.baseURL;
2870
+ const issuer = (getJwtPlugin(ctx)?.options)?.jwt?.issuer ?? ctx.baseURL;
3015
2871
  const issuerPath = new URL(issuer).pathname;
3016
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.`);
3017
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.`);
@@ -3064,7 +2920,7 @@ const oauthProvider = (options) => {
3064
2920
  metadata: { SERVER_ONLY: true }
3065
2921
  }, async (ctx) => {
3066
2922
  if (opts.scopes && opts.scopes.includes("openid")) return oidcServerMetadata(ctx, opts);
3067
- 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 });
3068
2924
  }),
3069
2925
  getOpenIdConfig: createAuthEndpoint("/.well-known/openid-configuration", {
3070
2926
  method: "GET",
@@ -1,13 +1,19 @@
1
- import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-kjs13QN6.mjs";
1
+ import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-BrFoF22H.mjs";
2
2
  import * as better_call0 from "better-call";
3
3
  import "@better-auth/core/context";
4
4
  import * as z from "zod";
5
+ import * as better_auth_plugins0 from "better-auth/plugins";
5
6
  import * as jose0 from "jose";
6
7
  import * as better_auth0 from "better-auth";
7
- import * as better_auth_plugins0 from "better-auth/plugins";
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_auth_plugins0 from "better-auth/plugins";
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: {
@@ -1,4 +1,4 @@
1
- import { JWSAlgorithms } from "better-auth/plugins/jwt";
1
+ import { JWSAlgorithms } from "better-auth/plugins";
2
2
  import { JWTPayload } from "jose";
3
3
  import { InferOptionSchema, Session, User } from "better-auth/types";
4
4
  import { LiteralString } from "@better-auth/core";
@@ -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.1",
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.1",
46
- "better-auth": "1.5.0-beta.1"
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.1",
57
- "better-auth": "1.5.0-beta.1"
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"