@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.
package/dist/client-resource.mjs
CHANGED
|
@@ -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-
|
|
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
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-
|
|
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:"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
|
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"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/oauth-provider",
|
|
3
|
-
"version": "1.6.
|
|
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
|
-
"
|
|
68
|
-
"better-auth": "1.6.
|
|
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.
|
|
75
|
-
"better-auth": "^1.6.
|
|
74
|
+
"@better-auth/core": "^1.6.6",
|
|
75
|
+
"better-auth": "^1.6.6"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"build": "tsdown",
|