@better-auth/oauth-provider 1.7.0-beta.1 → 1.7.0-beta.2

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,6 +1,6 @@
1
1
  import { t as handleMcpErrors } from "./mcp-CYnz-MXn.mjs";
2
2
  import { o as getJwtPlugin, s as getOAuthProviderPlugin } from "./utils-Cx_XnD9i.mjs";
3
- import { t as PACKAGE_VERSION } from "./version-DIwdpXrQ.mjs";
3
+ import { t as PACKAGE_VERSION } from "./version-CZxZ64qJ.mjs";
4
4
  import { verifyAccessToken } from "better-auth/oauth2";
5
5
  import { APIError } from "better-call";
6
6
  import { logger } from "@better-auth/core/env";
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as oauthProvider } from "./oauth-B_qonG53.mjs";
1
+ import { n as oauthProvider } from "./oauth-DJcZ8MMZ.mjs";
2
2
  import * as _better_fetch_fetch0 from "@better-fetch/fetch";
3
3
 
4
4
  //#region src/client.d.ts
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as PACKAGE_VERSION } from "./version-DIwdpXrQ.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-CZxZ64qJ.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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { _ as Scope, a as OIDCMetadata, b as Awaitable, c as AuthorizePrompt, d as OAuthConsent, f as OAuthOpaqueAccessToken, g as SchemaClient, h as Prompt, i as OAuthClient, l as ClientDiscovery, m as OAuthRefreshToken, n as AuthServerMetadata, o as ResourceServerMetadata, p as OAuthOptions, r as GrantType, s as TokenEndpointAuthMethod, t as AuthMethod, u as OAuthAuthorizationQuery, v as StoreTokenType, y as VerificationValue } from "./oauth-CU79t-eG.mjs";
2
- import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-B_qonG53.mjs";
2
+ import { a as OAuthErrorCode, c as OAuthRedirectOnError, i as OAuthEndpointRedirectContext, n as oauthProvider, o as OAuthFieldErrorCode, r as OAuthEndpointErrorResult, s as OAuthFieldErrorCodeMap, t as getOAuthProviderState } from "./oauth-DJcZ8MMZ.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
@@ -105,4 +105,4 @@ declare function checkOAuthClient(client: OAuthClient, opts: OAuthOptions<Scope[
105
105
  */
106
106
  declare function oauthToSchema(input: OAuthClient): SchemaClient<Scope[]>;
107
107
  //#endregion
108
- export { AuthServerMetadata, AuthorizePrompt, ClientDiscovery, OAuthAuthorizationQuery, OAuthClient, OAuthConsent, OAuthOpaqueAccessToken, OAuthOptions, OAuthRefreshToken, OIDCMetadata, Prompt, ResourceServerMetadata, SchemaClient, Scope, StoreTokenType, VerificationValue, authServerMetadata, checkOAuthClient, getOAuthProviderState, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oauthToSchema, oidcServerMetadata };
108
+ export { AuthServerMetadata, AuthorizePrompt, ClientDiscovery, OAuthAuthorizationQuery, OAuthClient, OAuthConsent, type OAuthEndpointErrorResult, type OAuthEndpointRedirectContext, type OAuthErrorCode, type OAuthFieldErrorCode, type OAuthFieldErrorCodeMap, OAuthOpaqueAccessToken, OAuthOptions, type OAuthRedirectOnError, OAuthRefreshToken, OIDCMetadata, Prompt, ResourceServerMetadata, SchemaClient, Scope, StoreTokenType, VerificationValue, authServerMetadata, checkOAuthClient, getOAuthProviderState, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oauthToSchema, oidcServerMetadata };
package/dist/index.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  import { n as isPrivateHostname } from "./client-assertion-CderPEmR.mjs";
2
2
  import { n as mcpHandler } from "./mcp-CYnz-MXn.mjs";
3
3
  import { _ as storeClientSecret, a as getClient, b as validateClientCredentials, c as getStoredToken, d as normalizeTimestampValue, f as parseClientMetadata, g as searchParamsToQuery, h as resolveSubjectIdentifier, i as extractClientCredentials, l as isPKCERequired, m as resolveSessionAuthTime, n as deleteFromPrompt, o as getJwtPlugin, p as parsePrompt, r as destructureCredentials, t as decryptStoredClientSecret, u as mergeDiscoveryMetadata, v as storeToken, x as verifyOAuthQueryParams, y as toClientDiscoveryArray } from "./utils-Cx_XnD9i.mjs";
4
- import { t as PACKAGE_VERSION } from "./version-DIwdpXrQ.mjs";
4
+ import { t as PACKAGE_VERSION } from "./version-CZxZ64qJ.mjs";
5
5
  import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
6
6
  import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
7
7
  import { APIError as APIError$1 } from "better-call";
8
8
  import { ASSERTION_SIGNING_ALGORITHMS } from "@better-auth/core/oauth2";
9
9
  import { isBrowserFetchRequest } from "@better-auth/core/utils/fetch-metadata";
10
+ import { isLoopbackHost, isLoopbackIP } from "@better-auth/core/utils/host";
10
11
  import { generateRandomString, makeSignature } from "better-auth/crypto";
11
12
  import { defineRequestState } from "@better-auth/core/context";
12
13
  import { logger } from "@better-auth/core/env";
@@ -162,9 +163,6 @@ const DANGEROUS_SCHEMES = [
162
163
  "data:",
163
164
  "vbscript:"
164
165
  ];
165
- function isLocalhost(hostname) {
166
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.endsWith(".localhost");
167
- }
168
166
  /**
169
167
  * Runtime schema for OAuthAuthorizationQuery.
170
168
  * Uses passthrough to tolerate fields added by future extensions (PAR, FPA, etc.)
@@ -203,7 +201,7 @@ const verificationValueSchema = z.object({
203
201
  /**
204
202
  * Reusable URL validation for OAuth redirect URIs.
205
203
  * - Blocks dangerous schemes (javascript:, data:, vbscript:)
206
- * - For http/https: requires HTTPS (HTTP allowed only for localhost)
204
+ * - For http/https: requires HTTPS (HTTP allowed only for loopback hosts: 127.0.0.0/8, [::1], *.localhost per RFC 6761)
207
205
  * - Allows custom schemes for mobile apps (e.g., myapp://callback)
208
206
  */
209
207
  const SafeUrlSchema = z.url().superRefine((val, ctx) => {
@@ -223,12 +221,10 @@ const SafeUrlSchema = z.url().superRefine((val, ctx) => {
223
221
  });
224
222
  return;
225
223
  }
226
- if (u.protocol === "http:" || u.protocol === "https:") {
227
- if (u.protocol === "http:" && !isLocalhost(u.hostname)) ctx.addIssue({
228
- code: "custom",
229
- message: "Redirect URI must use HTTPS (HTTP allowed only for localhost)"
230
- });
231
- }
224
+ if (u.protocol === "http:" && !isLoopbackHost(u.host)) ctx.addIssue({
225
+ code: "custom",
226
+ message: "Redirect URI must use HTTPS (HTTP allowed only for loopback hosts)"
227
+ });
232
228
  });
233
229
  //#endregion
234
230
  //#region src/userinfo.ts
@@ -259,11 +255,7 @@ function userNormalClaims(user, scopes) {
259
255
  * Handles the /oauth2/userinfo endpoint
260
256
  */
261
257
  async function userInfoEndpoint(ctx, opts) {
262
- if (!ctx.request) throw new APIError("UNAUTHORIZED", {
263
- error_description: "request not found",
264
- error: "invalid_request"
265
- });
266
- const authorization = ctx.request.headers.get("authorization");
258
+ const authorization = ctx.headers?.get("authorization");
267
259
  const token = typeof authorization === "string" && authorization?.startsWith("Bearer ") ? authorization?.replace("Bearer ", "") : authorization;
268
260
  if (!token?.length) throw new APIError("UNAUTHORIZED", {
269
261
  error_description: "authorization header not found",
@@ -309,8 +301,8 @@ async function userInfoEndpoint(ctx, opts) {
309
301
  * the grant types
310
302
  */
311
303
  async function tokenEndpoint(ctx, opts) {
312
- const grantType = ctx.body?.grant_type;
313
- if (opts.grantTypes && grantType && !opts.grantTypes.includes(grantType)) throw new APIError("BAD_REQUEST", {
304
+ const grantType = ctx.body.grant_type;
305
+ if (opts.grantTypes && !opts.grantTypes.includes(grantType)) throw new APIError("BAD_REQUEST", {
314
306
  error_description: `unsupported grant_type ${grantType}`,
315
307
  error: "unsupported_grant_type"
316
308
  });
@@ -318,14 +310,6 @@ async function tokenEndpoint(ctx, opts) {
318
310
  case "authorization_code": return handleAuthorizationCodeGrant(ctx, opts);
319
311
  case "client_credentials": return handleClientCredentialsGrant(ctx, opts);
320
312
  case "refresh_token": return handleRefreshTokenGrant(ctx, opts);
321
- case void 0: throw new APIError("BAD_REQUEST", {
322
- error_description: "missing required grant_type",
323
- error: "unsupported_grant_type"
324
- });
325
- default: throw new APIError("BAD_REQUEST", {
326
- error_description: `unsupported grant_type ${grantType}`,
327
- error: "unsupported_grant_type"
328
- });
329
313
  }
330
314
  }
331
315
  async function createJwtAccessToken(ctx, opts, user, client, audience, scopes, referenceId, overrides) {
@@ -1016,6 +1000,7 @@ async function resolveIntrospectionSub(opts, payload, client) {
1016
1000
  }
1017
1001
  async function introspectEndpoint(ctx, opts) {
1018
1002
  let { token, token_type_hint } = ctx.body;
1003
+ if (token_type_hint !== "access_token" && token_type_hint !== "refresh_token") token_type_hint = void 0;
1019
1004
  const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/introspect`));
1020
1005
  if (!client_id || !client_secret && !preVerifiedClient) throw new APIError$1("UNAUTHORIZED", {
1021
1006
  error_description: "missing required credentials",
@@ -1173,6 +1158,109 @@ async function rpInitiatedLogoutEndpoint(ctx, opts) {
1173
1158
  }
1174
1159
  }
1175
1160
  //#endregion
1161
+ //#region src/oauth-endpoint.ts
1162
+ /**
1163
+ * Wraps `createAuthEndpoint` so zod schemas stay the single source of truth
1164
+ * for body/query shape while validation failures serialize as the RFC 6749
1165
+ * §5.2 error envelope `{ error, error_description }`.
1166
+ *
1167
+ * A failing issue is routed by its first path segment via `errorCodesByField`:
1168
+ * - missing required (`invalid_type` + "received undefined") → `.missing`
1169
+ * - unsupported value (`invalid_value`) → `.invalid`
1170
+ * - anything else (wrong type, duplicated params, bad format) → `defaultError`
1171
+ *
1172
+ * For enum fields that need to distinguish missing from unsupported, compose
1173
+ * as `z.string().pipe(z.enum([...]))` so duplicated params fail the outer
1174
+ * `z.string()` as `invalid_type` instead of masquerading as an unsupported
1175
+ * enum value.
1176
+ */
1177
+ function createOAuthEndpoint(path, options, handler) {
1178
+ const { redirectOnError, onValidationError: userHook, errorCodesByField, defaultError = "invalid_request", ...rest } = options;
1179
+ if (!redirectOnError) return createAuthEndpoint(path, {
1180
+ ...rest,
1181
+ onValidationError: async (args) => {
1182
+ if (userHook) await userHook(args);
1183
+ throw new APIError$1("BAD_REQUEST", { ...mapIssuesToOAuthError(args.issues, errorCodesByField, defaultError) });
1184
+ }
1185
+ }, handler);
1186
+ const redirect = redirectOnError;
1187
+ const { body: bodySchema, query: querySchema, ...forwarded } = rest;
1188
+ async function validateSlot(ctx, slot, schema) {
1189
+ if (!schema) return { ok: true };
1190
+ const result = await schema.safeParseAsync(ctx[slot] ?? {});
1191
+ if (result.success) {
1192
+ ctx[slot] = result.data;
1193
+ return { ok: true };
1194
+ }
1195
+ if (userHook) await userHook({
1196
+ message: result.error.message,
1197
+ issues: result.error.issues
1198
+ });
1199
+ return {
1200
+ ok: false,
1201
+ response: redirect({
1202
+ ...mapIssuesToOAuthError(result.error.issues, errorCodesByField, defaultError),
1203
+ ctx
1204
+ })
1205
+ };
1206
+ }
1207
+ return createAuthEndpoint(path, forwarded, async (ctx) => {
1208
+ const body = await validateSlot(ctx, "body", bodySchema);
1209
+ if (!body.ok) return body.response;
1210
+ const query = await validateSlot(ctx, "query", querySchema);
1211
+ if (!query.ok) return query.response;
1212
+ return handler(ctx);
1213
+ });
1214
+ }
1215
+ function mapIssuesToOAuthError(issues, errorCodesByField, defaultError = "invalid_request") {
1216
+ const issue = issues[0];
1217
+ if (!issue) return {
1218
+ error: defaultError,
1219
+ error_description: "Invalid request."
1220
+ };
1221
+ const first = issue.path?.[0];
1222
+ const fieldKey = typeof first === "string" ? first : void 0;
1223
+ const mapping = fieldKey ? errorCodesByField?.[fieldKey] : void 0;
1224
+ const field = issue.path?.length ? z.core.toDotPath(issue.path) : "";
1225
+ return {
1226
+ error: resolveErrorCode(issue, mapping, defaultError),
1227
+ error_description: describeIssue(issue, field)
1228
+ };
1229
+ }
1230
+ function resolveErrorCode(issue, mapping, defaultError) {
1231
+ if (typeof mapping === "string") return mapping;
1232
+ if (isMissingValueIssue(issue)) return mapping?.missing ?? defaultError;
1233
+ if (issue.code === "invalid_value") return mapping?.invalid ?? defaultError;
1234
+ return defaultError;
1235
+ }
1236
+ /**
1237
+ * Returns `true` for issues that represent an absent required value. Zod v4
1238
+ * strips `input` from published issues, so the signal is the `invalid_type`
1239
+ * code combined with a message suffix of "received undefined". The suffix is
1240
+ * pinned by a regression test so a zod rephrase fails the test instead of
1241
+ * silently reclassifying missing fields.
1242
+ *
1243
+ * Assumes the default zod error map. Consumers that install a localized map
1244
+ * via `z.setErrorMap()` will break this check, collapsing missing-field
1245
+ * failures to `defaultError`.
1246
+ */
1247
+ function isMissingValueIssue(issue) {
1248
+ return issue.code === "invalid_type" && issue.message.endsWith("received undefined");
1249
+ }
1250
+ function describeIssue(issue, field) {
1251
+ if (!field) return issue.message;
1252
+ if (issue.code === "invalid_type") {
1253
+ if (issue.message.endsWith("received undefined")) return `${field} is required`;
1254
+ if (issue.message.endsWith("received array")) return `${field} must not appear more than once`;
1255
+ return `${field} must be a ${issue.expected ?? "valid value"}`;
1256
+ }
1257
+ if (issue.code === "invalid_value") {
1258
+ const values = issue.values;
1259
+ if (Array.isArray(values) && values.length > 0) return `${field} must be one of: ${values.join(", ")}`;
1260
+ }
1261
+ return `${field}: ${issue.message}`;
1262
+ }
1263
+ //#endregion
1176
1264
  //#region src/middleware/index.ts
1177
1265
  const publicSessionMiddleware = (opts) => createAuthMiddleware(async (ctx) => {
1178
1266
  if (!opts.allowPublicClientPrelogin) throw new APIError("BAD_REQUEST");
@@ -1458,14 +1546,8 @@ function schemaToOAuth(input) {
1458
1546
  //#region src/oauthClient/endpoints.ts
1459
1547
  async function getClientEndpoint(ctx, opts) {
1460
1548
  const session = await getSessionFromCtx(ctx);
1549
+ await assertClientPrivileges(ctx, session, opts, "read");
1461
1550
  if (!session) throw new APIError("UNAUTHORIZED");
1462
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1463
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1464
- headers: ctx.headers,
1465
- action: "read",
1466
- session: session.session,
1467
- user: session.user
1468
- })) throw new APIError("UNAUTHORIZED");
1469
1551
  const client = await getClient(ctx, opts, ctx.query.client_id);
1470
1552
  if (!client) throw new APIError("NOT_FOUND", {
1471
1553
  error_description: "client not found",
@@ -1506,14 +1588,8 @@ async function getClientPublicEndpoint(ctx, opts, clientId) {
1506
1588
  }
1507
1589
  async function getClientsEndpoint(ctx, opts) {
1508
1590
  const session = await getSessionFromCtx(ctx);
1591
+ await assertClientPrivileges(ctx, session, opts, "list");
1509
1592
  if (!session) throw new APIError("UNAUTHORIZED");
1510
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1511
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1512
- headers: ctx.headers,
1513
- action: "list",
1514
- session: session.session,
1515
- user: session.user
1516
- })) throw new APIError("UNAUTHORIZED");
1517
1593
  const referenceId = await opts.clientReference?.(session);
1518
1594
  if (referenceId) return await ctx.context.adapter.findMany({
1519
1595
  model: "oauthClient",
@@ -1547,14 +1623,8 @@ async function getClientsEndpoint(ctx, opts) {
1547
1623
  }
1548
1624
  async function deleteClientEndpoint(ctx, opts) {
1549
1625
  const session = await getSessionFromCtx(ctx);
1626
+ await assertClientPrivileges(ctx, session, opts, "delete");
1550
1627
  if (!session) throw new APIError("UNAUTHORIZED");
1551
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1552
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1553
- headers: ctx.headers,
1554
- action: "delete",
1555
- session: session.session,
1556
- user: session.user
1557
- })) throw new APIError("UNAUTHORIZED");
1558
1628
  const clientId = ctx.body.client_id;
1559
1629
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1560
1630
  error_description: "trusted clients must be updated manually",
@@ -1580,14 +1650,8 @@ async function deleteClientEndpoint(ctx, opts) {
1580
1650
  }
1581
1651
  async function updateClientEndpoint(ctx, opts) {
1582
1652
  const session = await getSessionFromCtx(ctx);
1653
+ await assertClientPrivileges(ctx, session, opts, "update");
1583
1654
  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: "update",
1588
- session: session.session,
1589
- user: session.user
1590
- })) throw new APIError("UNAUTHORIZED");
1591
1655
  const clientId = ctx.body.client_id;
1592
1656
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1593
1657
  error_description: "trusted clients must be updated manually",
@@ -1641,14 +1705,8 @@ async function updateClientEndpoint(ctx, opts) {
1641
1705
  }
1642
1706
  async function rotateClientSecretEndpoint(ctx, opts) {
1643
1707
  const session = await getSessionFromCtx(ctx);
1708
+ await assertClientPrivileges(ctx, session, opts, "rotate");
1644
1709
  if (!session) throw new APIError("UNAUTHORIZED");
1645
- if (!ctx.headers) throw new APIError("BAD_REQUEST");
1646
- if (opts.clientPrivileges && !await opts.clientPrivileges({
1647
- headers: ctx.headers,
1648
- action: "rotate",
1649
- session: session.session,
1650
- user: session.user
1651
- })) throw new APIError("UNAUTHORIZED");
1652
1710
  const clientId = ctx.body.client_id;
1653
1711
  if (opts.cachedTrustedClients?.has(clientId)) throw new APIError("INTERNAL_SERVER_ERROR", {
1654
1712
  error_description: "trusted clients must be updated manually",
@@ -1690,6 +1748,16 @@ async function rotateClientSecretEndpoint(ctx, opts) {
1690
1748
  clientSecret: (opts.prefix?.clientSecret ?? "") + clientSecret
1691
1749
  });
1692
1750
  }
1751
+ async function assertClientPrivileges(ctx, session, opts, action) {
1752
+ if (!session) throw new APIError("UNAUTHORIZED");
1753
+ if (!ctx.headers) throw new APIError("BAD_REQUEST");
1754
+ if (opts.clientPrivileges && !await opts.clientPrivileges({
1755
+ headers: ctx.headers,
1756
+ action,
1757
+ session: session.session,
1758
+ user: session.user
1759
+ })) throw new APIError("UNAUTHORIZED");
1760
+ }
1693
1761
  //#endregion
1694
1762
  //#region src/oauthClient/index.ts
1695
1763
  const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/create-client", {
@@ -1875,6 +1943,7 @@ const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/creat
1875
1943
  }
1876
1944
  }
1877
1945
  }, async (ctx) => {
1946
+ await assertClientPrivileges(ctx, await getSessionFromCtx(ctx), opts, "create");
1878
1947
  return createOAuthClientEndpoint(ctx, opts, { isRegister: false });
1879
1948
  });
1880
1949
  const createOAuthClient = (opts) => createAuthEndpoint("/oauth2/create-client", {
@@ -2047,6 +2116,7 @@ const createOAuthClient = (opts) => createAuthEndpoint("/oauth2/create-client",
2047
2116
  } }
2048
2117
  } }
2049
2118
  }, async (ctx) => {
2119
+ await assertClientPrivileges(ctx, await getSessionFromCtx(ctx), opts, "create");
2050
2120
  return createOAuthClientEndpoint(ctx, opts, { isRegister: false });
2051
2121
  });
2052
2122
  const getOAuthClient = (opts) => createAuthEndpoint("/oauth2/get-client", {
@@ -2459,6 +2529,7 @@ async function revokeAccessToken(ctx, opts, clientId, token) {
2459
2529
  }
2460
2530
  async function revokeEndpoint(ctx, opts) {
2461
2531
  let { token, token_type_hint } = ctx.body;
2532
+ if (token_type_hint !== "access_token" && token_type_hint !== "refresh_token") token_type_hint = void 0;
2462
2533
  const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/revoke`));
2463
2534
  if (!client_id) throw new APIError$1("UNAUTHORIZED", {
2464
2535
  error_description: "missing required credentials",
@@ -2927,19 +2998,19 @@ const oauthProvider = (options) => {
2927
2998
  if (opts.scopes && !opts.scopes.includes("openid")) throw new APIError("NOT_FOUND");
2928
2999
  return oidcServerMetadata(ctx, opts);
2929
3000
  }),
2930
- oauth2Authorize: createAuthEndpoint("/oauth2/authorize", {
3001
+ oauth2Authorize: createOAuthEndpoint("/oauth2/authorize", {
2931
3002
  method: "GET",
2932
3003
  query: z.object({
2933
- response_type: z.enum(["code"]).optional(),
3004
+ response_type: z.string().pipe(z.enum(["code"])).optional(),
2934
3005
  client_id: z.string(),
2935
3006
  redirect_uri: SafeUrlSchema.optional(),
2936
3007
  scope: z.string().optional(),
2937
3008
  state: z.string().optional(),
2938
3009
  request_uri: z.string().optional(),
2939
3010
  code_challenge: z.string().optional(),
2940
- code_challenge_method: z.enum(["S256"]).optional(),
3011
+ code_challenge_method: z.string().pipe(z.enum(["S256"])).optional(),
2941
3012
  nonce: z.string().optional(),
2942
- prompt: z.enum([
3013
+ prompt: z.string().pipe(z.enum([
2943
3014
  "none",
2944
3015
  "consent",
2945
3016
  "login",
@@ -2947,8 +3018,10 @@ const oauthProvider = (options) => {
2947
3018
  "select_account",
2948
3019
  "login consent",
2949
3020
  "select_account consent"
2950
- ]).optional()
3021
+ ])).optional()
2951
3022
  }),
3023
+ redirectOnError: authorizeRedirectOnError(opts),
3024
+ errorCodesByField: { response_type: { invalid: "unsupported_response_type" } },
2952
3025
  metadata: { openapi: {
2953
3026
  description: "Authorize an OAuth2 request",
2954
3027
  parameters: [
@@ -3107,14 +3180,14 @@ const oauthProvider = (options) => {
3107
3180
  }, async (ctx) => {
3108
3181
  return continueEndpoint(ctx, opts);
3109
3182
  }),
3110
- oauth2Token: createAuthEndpoint("/oauth2/token", {
3183
+ oauth2Token: createOAuthEndpoint("/oauth2/token", {
3111
3184
  method: "POST",
3112
3185
  body: z.object({
3113
- grant_type: z.enum([
3186
+ grant_type: z.string().pipe(z.enum([
3114
3187
  "authorization_code",
3115
3188
  "client_credentials",
3116
3189
  "refresh_token"
3117
- ]),
3190
+ ])),
3118
3191
  client_id: z.string().optional(),
3119
3192
  client_secret: z.string().optional(),
3120
3193
  client_assertion: z.string().optional(),
@@ -3126,6 +3199,10 @@ const oauthProvider = (options) => {
3126
3199
  resource: z.string().optional(),
3127
3200
  scope: z.string().optional()
3128
3201
  }),
3202
+ errorCodesByField: { grant_type: {
3203
+ missing: "invalid_request",
3204
+ invalid: "unsupported_grant_type"
3205
+ } },
3129
3206
  metadata: {
3130
3207
  allowedMediaTypes: ["application/x-www-form-urlencoded"],
3131
3208
  openapi: {
@@ -3238,7 +3315,7 @@ const oauthProvider = (options) => {
3238
3315
  }, async (ctx) => {
3239
3316
  return tokenEndpoint(ctx, opts);
3240
3317
  }),
3241
- oauth2Introspect: createAuthEndpoint("/oauth2/introspect", {
3318
+ oauth2Introspect: createOAuthEndpoint("/oauth2/introspect", {
3242
3319
  method: "POST",
3243
3320
  body: z.object({
3244
3321
  client_id: z.string().optional(),
@@ -3246,7 +3323,7 @@ const oauthProvider = (options) => {
3246
3323
  client_assertion: z.string().optional(),
3247
3324
  client_assertion_type: z.string().optional(),
3248
3325
  token: z.string(),
3249
- token_type_hint: z.enum(["access_token", "refresh_token"]).optional()
3326
+ token_type_hint: z.string().optional()
3250
3327
  }),
3251
3328
  metadata: {
3252
3329
  allowedMediaTypes: ["application/x-www-form-urlencoded"],
@@ -3271,8 +3348,7 @@ const oauthProvider = (options) => {
3271
3348
  },
3272
3349
  token_type_hint: {
3273
3350
  type: "string",
3274
- enum: ["access_token", "refresh_token"],
3275
- description: "Hint about the type of the token submitted for introspection"
3351
+ description: "Hint about the token type. Recognized values: `access_token`, `refresh_token`."
3276
3352
  },
3277
3353
  resource: {
3278
3354
  type: "string",
@@ -3358,7 +3434,7 @@ const oauthProvider = (options) => {
3358
3434
  }, async (ctx) => {
3359
3435
  return introspectEndpoint(ctx, opts);
3360
3436
  }),
3361
- oauth2Revoke: createAuthEndpoint("/oauth2/revoke", {
3437
+ oauth2Revoke: createOAuthEndpoint("/oauth2/revoke", {
3362
3438
  method: "POST",
3363
3439
  body: z.object({
3364
3440
  client_id: z.string().optional(),
@@ -3366,7 +3442,7 @@ const oauthProvider = (options) => {
3366
3442
  client_assertion: z.string().optional(),
3367
3443
  client_assertion_type: z.string().optional(),
3368
3444
  token: z.string(),
3369
- token_type_hint: z.enum(["access_token", "refresh_token"]).optional()
3445
+ token_type_hint: z.string().optional()
3370
3446
  }),
3371
3447
  metadata: {
3372
3448
  allowedMediaTypes: ["application/x-www-form-urlencoded"],
@@ -3391,8 +3467,7 @@ const oauthProvider = (options) => {
3391
3467
  },
3392
3468
  token_type_hint: {
3393
3469
  type: "string",
3394
- enum: ["access_token", "refresh_token"],
3395
- description: "Hint about the type of the token submitted for revocation"
3470
+ description: "Hint about the token type. Recognized values: `access_token`, `refresh_token`."
3396
3471
  }
3397
3472
  },
3398
3473
  required: ["token"]
@@ -3513,7 +3588,7 @@ const oauthProvider = (options) => {
3513
3588
  }, async (ctx) => {
3514
3589
  return userInfoEndpoint(ctx, opts);
3515
3590
  }),
3516
- oauth2EndSession: createAuthEndpoint("/oauth2/end-session", {
3591
+ oauth2EndSession: createOAuthEndpoint("/oauth2/end-session", {
3517
3592
  method: "GET",
3518
3593
  query: z.object({
3519
3594
  id_token_hint: z.string(),
@@ -3544,7 +3619,7 @@ const oauthProvider = (options) => {
3544
3619
  }, async (ctx) => {
3545
3620
  return rpInitiatedLogoutEndpoint(ctx, opts);
3546
3621
  }),
3547
- registerOAuthClient: createAuthEndpoint("/oauth2/register", {
3622
+ registerOAuthClient: createOAuthEndpoint("/oauth2/register", {
3548
3623
  method: "POST",
3549
3624
  body: z.object({
3550
3625
  redirect_uris: z.array(SafeUrlSchema).min(1).min(1),
@@ -3581,6 +3656,12 @@ const oauthProvider = (options) => {
3581
3656
  subject_type: z.enum(["public", "pairwise"]).optional(),
3582
3657
  skip_consent: z.never({ error: "skip_consent cannot be set during dynamic client registration" }).optional()
3583
3658
  }),
3659
+ errorCodesByField: {
3660
+ redirect_uris: "invalid_redirect_uri",
3661
+ post_logout_redirect_uris: "invalid_redirect_uri",
3662
+ software_statement: "invalid_software_statement"
3663
+ },
3664
+ defaultError: "invalid_client_metadata",
3584
3665
  metadata: { openapi: {
3585
3666
  description: "Register an OAuth2 application",
3586
3667
  responses: { "200": {
@@ -3773,17 +3854,37 @@ const oauthProvider = (options) => {
3773
3854
  //#endregion
3774
3855
  //#region src/authorize.ts
3775
3856
  /**
3776
- * Formats an error url
3857
+ * Formats an error url. Per OIDC Core 1.0 §5 / RFC 6749 §4.2.2.1, errors on
3858
+ * implicit and hybrid flows are delivered in the URL fragment, not the query.
3859
+ * Callers on the code flow (default) omit `mode` and get query delivery.
3777
3860
  */
3778
- function formatErrorURL(url, error, description, state, iss) {
3861
+ function formatErrorURL(url, error, description, state, iss, mode = "query") {
3779
3862
  const searchParams = new URLSearchParams({
3780
3863
  error,
3781
3864
  error_description: description
3782
3865
  });
3783
3866
  state && searchParams.append("state", state);
3784
3867
  iss && searchParams.append("iss", iss);
3868
+ if (mode === "fragment") return `${url}#${searchParams.toString()}`;
3785
3869
  return `${url}${url.includes("?") ? "&" : "?"}${searchParams.toString()}`;
3786
3870
  }
3871
+ /**
3872
+ * Selects the response mode for an error redirect to the RP. OIDC Core 1.0 §5
3873
+ * defines defaults based on response_type: `code` → query, types containing
3874
+ * `token` / `id_token` → fragment. An explicit `response_mode` overrides.
3875
+ *
3876
+ * When `response_type` is duplicated (array) or absent, we can't trust the
3877
+ * caller's intent, so we default to query — the safer channel for
3878
+ * unrecognized shapes.
3879
+ */
3880
+ function deriveResponseMode(raw) {
3881
+ const responseMode = typeof raw.response_mode === "string" ? raw.response_mode : void 0;
3882
+ if (responseMode === "fragment") return "fragment";
3883
+ if (responseMode === "query") return "query";
3884
+ const responseType = typeof raw.response_type === "string" ? raw.response_type : void 0;
3885
+ if (responseType && /\b(token|id_token)\b/.test(responseType)) return "fragment";
3886
+ return "query";
3887
+ }
3787
3888
  const handleRedirect = (ctx, uri) => {
3788
3889
  const fromFetch = isBrowserFetchRequest(ctx.request?.headers);
3789
3890
  const acceptJson = ctx.headers?.get("accept")?.includes("application/json");
@@ -3807,8 +3908,7 @@ function redirectWithPromptNoneError(ctx, opts, query, error, description) {
3807
3908
  function validateIssuerUrl(issuer) {
3808
3909
  try {
3809
3910
  const url = new URL(issuer);
3810
- const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
3811
- if (url.protocol !== "https:" && !isLocalhost) url.protocol = "https:";
3911
+ if (url.protocol !== "https:" && !isLoopbackHost(url.host)) url.protocol = "https:";
3812
3912
  url.search = "";
3813
3913
  url.hash = "";
3814
3914
  return url.toString().replace(/\/$/, "");
@@ -3836,6 +3936,64 @@ function getIssuer(ctx, opts) {
3836
3936
  function getErrorURL(ctx, error, description) {
3837
3937
  return formatErrorURL(ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`, error, description);
3838
3938
  }
3939
+ /**
3940
+ * Finds the matching entry in a client's registered redirect_uris for a
3941
+ * requested redirect_uri. Honors RFC 8252 §7.3 loopback port variance for
3942
+ * the full 127.0.0.0/8 range and [::1], matching on scheme+host+path+query
3943
+ * and ignoring port. DNS names like "localhost" are excluded per §8.3.
3944
+ */
3945
+ function findRegisteredRedirectUri(registered, requested) {
3946
+ if (!registered || !requested) return void 0;
3947
+ let req;
3948
+ try {
3949
+ req = new URL(requested);
3950
+ } catch {}
3951
+ return registered.find((url) => {
3952
+ if (url === requested) return true;
3953
+ if (!req) return false;
3954
+ try {
3955
+ const reg = new URL(url);
3956
+ return isLoopbackIP(reg.hostname) && reg.hostname === req.hostname && reg.pathname === req.pathname && reg.protocol === req.protocol && reg.search === req.search;
3957
+ } catch {
3958
+ return false;
3959
+ }
3960
+ });
3961
+ }
3962
+ /**
3963
+ * Loads the client, verifies it's enabled, and returns the requested
3964
+ * redirect_uri when it matches a registered entry. Returns null whenever the
3965
+ * RP cannot be safely reached, so callers can fall back to the server error
3966
+ * page (avoiding open-redirect risk on validation failures).
3967
+ */
3968
+ async function resolveTrustedRedirectUri(ctx, opts, clientId, redirectUri) {
3969
+ if (!clientId || !redirectUri) return null;
3970
+ let client;
3971
+ try {
3972
+ client = await getClient(ctx, opts, clientId);
3973
+ } catch {
3974
+ return null;
3975
+ }
3976
+ if (!client || client.disabled) return null;
3977
+ return findRegisteredRedirectUri(client.redirectUris, redirectUri) ? redirectUri : null;
3978
+ }
3979
+ /**
3980
+ * `redirectOnError` callback for `/oauth2/authorize`. Per RFC 6749 §4.1.2.1,
3981
+ * authorize errors MUST be delivered to the client's `redirect_uri` with
3982
+ * `error`, `error_description`, `state`, and (RFC 9207) `iss`. The clause
3983
+ * carves out one case: a missing/invalid `redirect_uri` or `client_id` MUST
3984
+ * NOT redirect to the requested URI. We implement the carve-out via
3985
+ * `resolveTrustedRedirectUri`, falling back to the server error page.
3986
+ *
3987
+ * Channel (query vs fragment) follows OIDC Core §5 via `deriveResponseMode`.
3988
+ */
3989
+ function authorizeRedirectOnError(opts) {
3990
+ return async ({ error, error_description, ctx }) => {
3991
+ const raw = ctx.query ?? {};
3992
+ const trusted = await resolveTrustedRedirectUri(ctx, opts, typeof raw.client_id === "string" ? raw.client_id : void 0, typeof raw.redirect_uri === "string" ? raw.redirect_uri : void 0);
3993
+ if (trusted) return handleRedirect(ctx, formatErrorURL(trusted, error, error_description, typeof raw.state === "string" ? raw.state : void 0, getIssuer(ctx, opts), deriveResponseMode(raw)));
3994
+ return handleRedirect(ctx, getErrorURL(ctx, error, error_description));
3995
+ };
3996
+ }
3839
3997
  async function authorizeEndpoint(ctx, opts, settings) {
3840
3998
  if (opts.grantTypes && !opts.grantTypes.includes("authorization_code")) throw new APIError$1("NOT_FOUND");
3841
3999
  if (!ctx.request) throw new APIError$1("UNAUTHORIZED", {
@@ -3866,15 +4024,7 @@ async function authorizeEndpoint(ctx, opts, settings) {
3866
4024
  const client = await getClient(ctx, opts, query.client_id);
3867
4025
  if (!client) return handleRedirect(ctx, getErrorURL(ctx, "invalid_client", "client_id is required"));
3868
4026
  if (client.disabled) return handleRedirect(ctx, getErrorURL(ctx, "client_disabled", "client is disabled"));
3869
- if (!client.redirectUris?.find((url) => {
3870
- if (url === query.redirect_uri) return true;
3871
- try {
3872
- const registered = new URL(url);
3873
- const requested = new URL(query.redirect_uri);
3874
- 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;
3875
- } catch {}
3876
- return false;
3877
- }) || !query.redirect_uri) return handleRedirect(ctx, getErrorURL(ctx, "invalid_redirect", "invalid redirect uri"));
4027
+ if (!findRegisteredRedirectUri(client.redirectUris, query.redirect_uri) || !query.redirect_uri) return handleRedirect(ctx, getErrorURL(ctx, "invalid_redirect", "invalid redirect uri"));
3878
4028
  let requestedScopes = query.scope?.split(" ").filter((s) => s);
3879
4029
  if (requestedScopes) {
3880
4030
  const validScopes = new Set(client.scopes ?? opts.scopes);
@@ -5,6 +5,36 @@ import * as better_auth_plugins0 from "better-auth/plugins";
5
5
  import * as jose from "jose";
6
6
  import * as better_auth0 from "better-auth";
7
7
 
8
+ //#region src/oauth-endpoint.d.ts
9
+ /**
10
+ * Canonical OAuth 2.0 / OpenID Connect error codes. The union is the single
11
+ * vocabulary for every error-emitting surface in this plugin: token, authorize,
12
+ * revoke, introspect, register, userinfo, logout, consent, and the redirect
13
+ * error channel. Entries are grouped by source RFC so the declaration doubles
14
+ * as a specification map.
15
+ *
16
+ * The trailing `(string & {})` keeps the type open for product-specific codes
17
+ * (e.g. `"invalid_verification"`, `"invalid_user"`) while preserving editor
18
+ * autocomplete for the listed standard codes. Prefer a standard code whenever
19
+ * one applies; fall back to a custom string only for states no RFC covers.
20
+ */
21
+ type OAuthErrorCode = "invalid_request" | "invalid_client" | "invalid_grant" | "unauthorized_client" | "unsupported_grant_type" | "unsupported_response_type" | "invalid_scope" | "access_denied" | "server_error" | "temporarily_unavailable" | "invalid_token" | "unsupported_token_type" | "invalid_redirect_uri" | "invalid_client_metadata" | "invalid_software_statement" | "unapproved_software_statement" | "invalid_target" | "invalid_request_object" | "login_required" | "consent_required" | "interaction_required" | "account_selection_required" | "invalid_request_uri" | "request_not_supported" | "request_uri_not_supported" | "registration_not_supported" | (string & {});
22
+ type OAuthFieldErrorCodeMap = {
23
+ missing?: OAuthErrorCode;
24
+ invalid?: OAuthErrorCode;
25
+ };
26
+ type OAuthFieldErrorCode = OAuthErrorCode | OAuthFieldErrorCodeMap;
27
+ interface OAuthEndpointErrorResult {
28
+ error: OAuthErrorCode;
29
+ error_description: string;
30
+ }
31
+ interface OAuthEndpointRedirectContext<Ctx = unknown> {
32
+ error: OAuthErrorCode;
33
+ error_description: string;
34
+ ctx: Ctx;
35
+ }
36
+ type OAuthRedirectOnError<Ctx = any> = (result: OAuthEndpointRedirectContext<Ctx>) => unknown;
37
+ //#endregion
8
38
  //#region src/oauth.d.ts
9
39
  declare module "@better-auth/core" {
10
40
  interface BetterAuthPluginRegistry<AuthOptions, Options> {
@@ -160,20 +190,20 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
160
190
  oauth2Authorize: better_call0.StrictEndpoint<"/oauth2/authorize", {
161
191
  method: "GET";
162
192
  query: z.ZodObject<{
163
- response_type: z.ZodOptional<z.ZodEnum<{
193
+ response_type: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
164
194
  code: "code";
165
- }>>;
195
+ }>>>;
166
196
  client_id: z.ZodString;
167
197
  redirect_uri: z.ZodOptional<z.ZodURL>;
168
198
  scope: z.ZodOptional<z.ZodString>;
169
199
  state: z.ZodOptional<z.ZodString>;
170
200
  request_uri: z.ZodOptional<z.ZodString>;
171
201
  code_challenge: z.ZodOptional<z.ZodString>;
172
- code_challenge_method: z.ZodOptional<z.ZodEnum<{
202
+ code_challenge_method: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
173
203
  S256: "S256";
174
- }>>;
204
+ }>>>;
175
205
  nonce: z.ZodOptional<z.ZodString>;
176
- prompt: z.ZodOptional<z.ZodEnum<{
206
+ prompt: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
177
207
  none: "none";
178
208
  consent: "consent";
179
209
  login: "login";
@@ -181,8 +211,14 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
181
211
  select_account: "select_account";
182
212
  "login consent": "login consent";
183
213
  "select_account consent": "select_account consent";
184
- }>>;
214
+ }>>>;
185
215
  }, z.core.$strip>;
216
+ redirectOnError: OAuthRedirectOnError<better_auth0.GenericEndpointContext>;
217
+ errorCodesByField: {
218
+ response_type: {
219
+ invalid: "unsupported_response_type";
220
+ };
221
+ };
186
222
  metadata: {
187
223
  openapi: {
188
224
  description: string;
@@ -378,11 +414,11 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
378
414
  oauth2Token: better_call0.StrictEndpoint<"/oauth2/token", {
379
415
  method: "POST";
380
416
  body: z.ZodObject<{
381
- grant_type: z.ZodEnum<{
417
+ grant_type: z.ZodPipe<z.ZodString, z.ZodEnum<{
382
418
  authorization_code: "authorization_code";
383
419
  client_credentials: "client_credentials";
384
420
  refresh_token: "refresh_token";
385
- }>;
421
+ }>>;
386
422
  client_id: z.ZodOptional<z.ZodString>;
387
423
  client_secret: z.ZodOptional<z.ZodString>;
388
424
  client_assertion: z.ZodOptional<z.ZodString>;
@@ -394,6 +430,12 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
394
430
  resource: z.ZodOptional<z.ZodString>;
395
431
  scope: z.ZodOptional<z.ZodString>;
396
432
  }, z.core.$strip>;
433
+ errorCodesByField: {
434
+ grant_type: {
435
+ missing: "invalid_request";
436
+ invalid: "unsupported_grant_type";
437
+ };
438
+ };
397
439
  metadata: {
398
440
  allowedMediaTypes: string[];
399
441
  openapi: {
@@ -530,10 +572,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
530
572
  client_assertion: z.ZodOptional<z.ZodString>;
531
573
  client_assertion_type: z.ZodOptional<z.ZodString>;
532
574
  token: z.ZodString;
533
- token_type_hint: z.ZodOptional<z.ZodEnum<{
534
- refresh_token: "refresh_token";
535
- access_token: "access_token";
536
- }>>;
575
+ token_type_hint: z.ZodOptional<z.ZodString>;
537
576
  }, z.core.$strip>;
538
577
  metadata: {
539
578
  allowedMediaTypes: string[];
@@ -560,7 +599,6 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
560
599
  };
561
600
  token_type_hint: {
562
601
  type: string;
563
- enum: string[];
564
602
  description: string;
565
603
  };
566
604
  resource: {
@@ -669,10 +707,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
669
707
  client_assertion: z.ZodOptional<z.ZodString>;
670
708
  client_assertion_type: z.ZodOptional<z.ZodString>;
671
709
  token: z.ZodString;
672
- token_type_hint: z.ZodOptional<z.ZodEnum<{
673
- refresh_token: "refresh_token";
674
- access_token: "access_token";
675
- }>>;
710
+ token_type_hint: z.ZodOptional<z.ZodString>;
676
711
  }, z.core.$strip>;
677
712
  metadata: {
678
713
  allowedMediaTypes: string[];
@@ -699,7 +734,6 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
699
734
  };
700
735
  token_type_hint: {
701
736
  type: string;
702
- enum: string[];
703
737
  description: string;
704
738
  };
705
739
  };
@@ -951,6 +985,12 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
951
985
  }>>;
952
986
  skip_consent: z.ZodOptional<z.ZodNever>;
953
987
  }, z.core.$strip>;
988
+ errorCodesByField: {
989
+ redirect_uris: "invalid_redirect_uri";
990
+ post_logout_redirect_uris: "invalid_redirect_uri";
991
+ software_statement: "invalid_software_statement";
992
+ };
993
+ defaultError: "invalid_client_metadata";
954
994
  metadata: {
955
995
  openapi: {
956
996
  description: string;
@@ -2196,4 +2236,4 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2196
2236
  })[];
2197
2237
  };
2198
2238
  //#endregion
2199
- export { oauthProvider as n, getOAuthProviderState as t };
2239
+ export { OAuthErrorCode as a, OAuthRedirectOnError as c, OAuthEndpointRedirectContext as i, oauthProvider as n, OAuthFieldErrorCode as o, OAuthEndpointErrorResult as r, OAuthFieldErrorCodeMap as s, getOAuthProviderState as t };
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.0-beta.1";
3
+ const PACKAGE_VERSION = "1.7.0-beta.2";
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.7.0-beta.1",
3
+ "version": "1.7.0-beta.2",
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.7.0-beta.1",
68
- "better-auth": "1.7.0-beta.1"
67
+ "@better-auth/core": "1.7.0-beta.2",
68
+ "better-auth": "1.7.0-beta.2"
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.7.0-beta.1",
75
- "better-auth": "^1.7.0-beta.1"
74
+ "@better-auth/core": "^1.7.0-beta.2",
75
+ "better-auth": "^1.7.0-beta.2"
76
76
  },
77
77
  "scripts": {
78
78
  "build": "tsdown",