@better-auth/oauth-provider 1.6.4 → 1.6.6

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.
@@ -1,5 +1,5 @@
1
1
  import { a as getJwtPlugin, o as getOAuthProviderPlugin, y as handleMcpErrors } from "./utils-B9Pj9EPf.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-Cvnm3JjE.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-A7ZA9idU.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { APIError } from "better-call";
5
5
  import { logger } from "@better-auth/core/env";
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as PACKAGE_VERSION } from "./version-Cvnm3JjE.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-A7ZA9idU.mjs";
2
2
  import { safeJSONParse } from "@better-auth/core/utils/json";
3
3
  //#region src/client.ts
4
4
  function parseSignedQuery(search) {
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { _ as validateClientCredentials, a as getJwtPlugin, b as mcpHandler, c as isPKCERequired, d as parsePrompt, f as resolveSessionAuthTime, g as storeToken, h as storeClientSecret, i as getClient, l as normalizeTimestampValue, m as searchParamsToQuery, n as decryptStoredClientSecret, p as resolveSubjectIdentifier, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as parseClientMetadata, v as verifyOAuthQueryParams } from "./utils-B9Pj9EPf.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-Cvnm3JjE.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-A7ZA9idU.mjs";
3
3
  import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
4
4
  import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
5
5
  import { APIError as APIError$1 } from "better-call";
6
6
  import { isBrowserFetchRequest } from "@better-auth/core/utils/fetch-metadata";
7
+ import { isLoopbackHost, isLoopbackIP } from "@better-auth/core/utils/host";
7
8
  import { generateRandomString, makeSignature } from "better-auth/crypto";
8
9
  import { defineRequestState } from "@better-auth/core/context";
9
10
  import { logger } from "@better-auth/core/env";
@@ -159,9 +160,6 @@ const DANGEROUS_SCHEMES = [
159
160
  "data:",
160
161
  "vbscript:"
161
162
  ];
162
- function isLocalhost(hostname) {
163
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.endsWith(".localhost");
164
- }
165
163
  /**
166
164
  * Runtime schema for OAuthAuthorizationQuery.
167
165
  * Uses passthrough to tolerate fields added by future extensions (PAR, FPA, etc.)
@@ -200,7 +198,7 @@ const verificationValueSchema = z.object({
200
198
  /**
201
199
  * Reusable URL validation for OAuth redirect URIs.
202
200
  * - Blocks dangerous schemes (javascript:, data:, vbscript:)
203
- * - For http/https: requires HTTPS (HTTP allowed only for localhost)
201
+ * - For http/https: requires HTTPS (HTTP allowed only for loopback hosts: 127.0.0.0/8, [::1], *.localhost per RFC 6761)
204
202
  * - Allows custom schemes for mobile apps (e.g., myapp://callback)
205
203
  */
206
204
  const SafeUrlSchema = z.url().superRefine((val, ctx) => {
@@ -220,12 +218,10 @@ const SafeUrlSchema = z.url().superRefine((val, ctx) => {
220
218
  });
221
219
  return;
222
220
  }
223
- if (u.protocol === "http:" || u.protocol === "https:") {
224
- if (u.protocol === "http:" && !isLocalhost(u.hostname)) ctx.addIssue({
225
- code: "custom",
226
- message: "Redirect URI must use HTTPS (HTTP allowed only for localhost)"
227
- });
228
- }
221
+ if (u.protocol === "http:" && !isLoopbackHost(u.host)) ctx.addIssue({
222
+ code: "custom",
223
+ message: "Redirect URI must use HTTPS (HTTP allowed only for loopback hosts)"
224
+ });
229
225
  });
230
226
  //#endregion
231
227
  //#region src/userinfo.ts
@@ -1404,14 +1400,8 @@ function schemaToOAuth(input) {
1404
1400
  //#region src/oauthClient/endpoints.ts
1405
1401
  async function getClientEndpoint(ctx, opts) {
1406
1402
  const session = await getSessionFromCtx(ctx);
1403
+ await assertClientPrivileges(ctx, session, opts, "read");
1407
1404
  if (!session) throw new APIError("UNAUTHORIZED");
1408
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1409
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1410
- headers: ctx.headers,
1411
- action: "read",
1412
- session: session.session,
1413
- user: session.user
1414
- })) throw new APIError("UNAUTHORIZED");
1415
1405
  const client = await getClient(ctx, opts, ctx.query.client_id);
1416
1406
  if (!client) throw new APIError("NOT_FOUND", {
1417
1407
  error_description: "client not found",
@@ -1452,14 +1442,8 @@ async function getClientPublicEndpoint(ctx, opts, clientId) {
1452
1442
  }
1453
1443
  async function getClientsEndpoint(ctx, opts) {
1454
1444
  const session = await getSessionFromCtx(ctx);
1445
+ await assertClientPrivileges(ctx, session, opts, "list");
1455
1446
  if (!session) throw new APIError("UNAUTHORIZED");
1456
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1457
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1458
- headers: ctx.headers,
1459
- action: "list",
1460
- session: session.session,
1461
- user: session.user
1462
- })) throw new APIError("UNAUTHORIZED");
1463
1447
  const referenceId = await opts.clientReference?.(session);
1464
1448
  if (referenceId) return await ctx.context.adapter.findMany({
1465
1449
  model: "oauthClient",
@@ -1493,14 +1477,8 @@ async function getClientsEndpoint(ctx, opts) {
1493
1477
  }
1494
1478
  async function deleteClientEndpoint(ctx, opts) {
1495
1479
  const session = await getSessionFromCtx(ctx);
1480
+ await assertClientPrivileges(ctx, session, opts, "delete");
1496
1481
  if (!session) throw new APIError("UNAUTHORIZED");
1497
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1498
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1499
- headers: ctx.headers,
1500
- action: "delete",
1501
- session: session.session,
1502
- user: session.user
1503
- })) throw new APIError("UNAUTHORIZED");
1504
1482
  const clientId = ctx.body.client_id;
1505
1483
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1506
1484
  error_description: "trusted clients must be updated manually",
@@ -1526,14 +1504,8 @@ async function deleteClientEndpoint(ctx, opts) {
1526
1504
  }
1527
1505
  async function updateClientEndpoint(ctx, opts) {
1528
1506
  const session = await getSessionFromCtx(ctx);
1507
+ await assertClientPrivileges(ctx, session, opts, "update");
1529
1508
  if (!session) throw new APIError("UNAUTHORIZED");
1530
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1531
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1532
- headers: ctx.headers,
1533
- action: "update",
1534
- session: session.session,
1535
- user: session.user
1536
- })) throw new APIError("UNAUTHORIZED");
1537
1509
  const clientId = ctx.body.client_id;
1538
1510
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1539
1511
  error_description: "trusted clients must be updated manually",
@@ -1580,14 +1552,8 @@ async function updateClientEndpoint(ctx, opts) {
1580
1552
  }
1581
1553
  async function rotateClientSecretEndpoint(ctx, opts) {
1582
1554
  const session = await getSessionFromCtx(ctx);
1555
+ await assertClientPrivileges(ctx, session, opts, "rotate");
1583
1556
  if (!session) throw new APIError("UNAUTHORIZED");
1584
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1585
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1586
- headers: ctx.headers,
1587
- action: "rotate",
1588
- session: session.session,
1589
- user: session.user
1590
- })) throw new APIError("UNAUTHORIZED");
1591
1557
  const clientId = ctx.body.client_id;
1592
1558
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1593
1559
  error_description: "trusted clients must be updated manually",
@@ -1629,6 +1595,16 @@ async function rotateClientSecretEndpoint(ctx, opts) {
1629
1595
  clientSecret: (opts.prefix?.clientSecret ?? "") + clientSecret
1630
1596
  });
1631
1597
  }
1598
+ async function assertClientPrivileges(ctx, session, opts, action) {
1599
+ if (!session) throw new APIError("UNAUTHORIZED");
1600
+ if (!ctx.headers) throw new APIError("BAD_REQUEST");
1601
+ if (opts.clientPrivileges && !await opts.clientPrivileges({
1602
+ headers: ctx.headers,
1603
+ action,
1604
+ session: session.session,
1605
+ user: session.user
1606
+ })) throw new APIError("UNAUTHORIZED");
1607
+ }
1632
1608
  //#endregion
1633
1609
  //#region src/oauthClient/index.ts
1634
1610
  const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/create-client", {
@@ -1811,6 +1787,7 @@ const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/creat
1811
1787
  }
1812
1788
  }
1813
1789
  }, async (ctx) => {
1790
+ await assertClientPrivileges(ctx, await getSessionFromCtx(ctx), opts, "create");
1814
1791
  return createOAuthClientEndpoint(ctx, opts, { isRegister: false });
1815
1792
  });
1816
1793
  const createOAuthClient = (opts) => createAuthEndpoint("/oauth2/create-client", {
@@ -1980,6 +1957,7 @@ const createOAuthClient = (opts) => createAuthEndpoint("/oauth2/create-client",
1980
1957
  } }
1981
1958
  } }
1982
1959
  }, async (ctx) => {
1960
+ await assertClientPrivileges(ctx, await getSessionFromCtx(ctx), opts, "create");
1983
1961
  return createOAuthClientEndpoint(ctx, opts, { isRegister: false });
1984
1962
  });
1985
1963
  const getOAuthClient = (opts) => createAuthEndpoint("/oauth2/get-client", {
@@ -3724,8 +3702,7 @@ function redirectWithPromptNoneError(ctx, opts, query, error, description) {
3724
3702
  function validateIssuerUrl(issuer) {
3725
3703
  try {
3726
3704
  const url = new URL(issuer);
3727
- const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
3728
- if (url.protocol !== "https:" && !isLocalhost) url.protocol = "https:";
3705
+ if (url.protocol !== "https:" && !isLoopbackHost(url.host)) url.protocol = "https:";
3729
3706
  url.search = "";
3730
3707
  url.hash = "";
3731
3708
  return url.toString().replace(/\/$/, "");
@@ -3788,7 +3765,7 @@ async function authorizeEndpoint(ctx, opts, settings) {
3788
3765
  try {
3789
3766
  const registered = new URL(url);
3790
3767
  const requested = new URL(query.redirect_uri);
3791
- if ((registered.hostname === "127.0.0.1" || registered.hostname === "[::1]") && registered.hostname === requested.hostname && registered.pathname === requested.pathname && registered.protocol === requested.protocol && registered.search === requested.search) return true;
3768
+ if (isLoopbackIP(registered.hostname) && registered.hostname === requested.hostname && registered.pathname === requested.pathname && registered.protocol === requested.protocol && registered.search === requested.search) return true;
3792
3769
  } catch {}
3793
3770
  return false;
3794
3771
  }) || !query.redirect_uri) return handleRedirect(ctx, getErrorURL(ctx, "invalid_redirect", "invalid redirect uri"));
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.6.4";
3
+ const PACKAGE_VERSION = "1.6.6";
4
4
  //#endregion
5
5
  export { PACKAGE_VERSION as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/oauth-provider",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "description": "An oauth provider plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -64,15 +64,15 @@
64
64
  "@modelcontextprotocol/sdk": "^1.27.1",
65
65
  "listhen": "^1.9.0",
66
66
  "tsdown": "0.21.1",
67
- "@better-auth/core": "1.6.4",
68
- "better-auth": "1.6.4"
67
+ "better-auth": "1.6.6",
68
+ "@better-auth/core": "1.6.6"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "@better-auth/utils": "0.4.0",
72
72
  "@better-fetch/fetch": "1.1.21",
73
73
  "better-call": "1.3.5",
74
- "@better-auth/core": "^1.6.4",
75
- "better-auth": "^1.6.4"
74
+ "@better-auth/core": "^1.6.6",
75
+ "better-auth": "^1.6.6"
76
76
  },
77
77
  "scripts": {
78
78
  "build": "tsdown",