@better-auth/oauth-provider 1.6.0-beta.0 → 1.6.0

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,4 +1,4 @@
1
- import { a as ResourceServerMetadata } from "./oauth-4vgZlF-I.mjs";
1
+ import { a as ResourceServerMetadata } from "./oauth-Cc0nzj5Q.mjs";
2
2
  import { JWTPayload, JWTVerifyOptions } from "jose";
3
3
  import { Auth } from "better-auth/types";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { a as getJwtPlugin, o as getOAuthProviderPlugin, v as handleMcpErrors } from "./utils-sQ4gYeh3.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-DevbO3Yy.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-DNcPo3T3.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.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as oauthProvider } from "./oauth-CEoJtL3Y.mjs";
1
+ import { n as oauthProvider } from "./oauth-CYgzO8Am.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-DevbO3Yy.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-DNcPo3T3.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
- 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-4vgZlF-I.mjs";
2
- import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-CEoJtL3Y.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-Cc0nzj5Q.mjs";
2
+ import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-CYgzO8Am.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { _ as verifyOAuthQueryParams, a as getJwtPlugin, c as isPKCERequired, d as parsePrompt, f as resolveSessionAuthTime, g as validateClientCredentials, h as storeToken, i as getClient, l as normalizeTimestampValue, m as storeClientSecret, n as decryptStoredClientSecret, p as resolveSubjectIdentifier, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as parseClientMetadata, y as mcpHandler } from "./utils-sQ4gYeh3.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-DevbO3Yy.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-DNcPo3T3.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";
@@ -474,7 +474,7 @@ async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri)
474
474
  error: "invalid_user"
475
475
  });
476
476
  if (verificationValue.query?.redirect_uri && verificationValue.query?.redirect_uri !== redirect_uri) throw new APIError("BAD_REQUEST", {
477
- error_description: "missing verification redirect_uri",
477
+ error_description: "redirect_uri mismatch",
478
478
  error: "invalid_request"
479
479
  });
480
480
  return verificationValue;
@@ -1172,6 +1172,10 @@ async function checkOAuthClient(client, opts, settings) {
1172
1172
  error: "invalid_client_metadata",
1173
1173
  error_description: `pkce is required for registered clients.`
1174
1174
  });
1175
+ if (settings?.isRegister && client.skip_consent) throw new APIError("BAD_REQUEST", {
1176
+ error: "invalid_client_metadata",
1177
+ error_description: "skip_consent cannot be set during dynamic client registration"
1178
+ });
1175
1179
  }
1176
1180
  async function createOAuthClientEndpoint(ctx, opts, settings) {
1177
1181
  const body = ctx.body;
@@ -2805,11 +2809,12 @@ const oauthProvider = (options) => {
2805
2809
  oauth2Authorize: createAuthEndpoint("/oauth2/authorize", {
2806
2810
  method: "GET",
2807
2811
  query: z.object({
2808
- response_type: z.enum(["code"]),
2812
+ response_type: z.enum(["code"]).optional(),
2809
2813
  client_id: z.string(),
2810
2814
  redirect_uri: SafeUrlSchema.optional(),
2811
2815
  scope: z.string().optional(),
2812
2816
  state: z.string().optional(),
2817
+ request_uri: z.string().optional(),
2813
2818
  code_challenge: z.string().optional(),
2814
2819
  code_challenge_method: z.enum(["S256"]).optional(),
2815
2820
  nonce: z.string().optional(),
@@ -2829,7 +2834,7 @@ const oauthProvider = (options) => {
2829
2834
  {
2830
2835
  name: "response_type",
2831
2836
  in: "query",
2832
- required: true,
2837
+ required: false,
2833
2838
  schema: { type: "string" },
2834
2839
  description: "OAuth2 response type (e.g., 'code')"
2835
2840
  },
@@ -2864,6 +2869,13 @@ const oauthProvider = (options) => {
2864
2869
  schema: { type: "string" },
2865
2870
  description: "OAuth2 state parameter"
2866
2871
  },
2872
+ {
2873
+ name: "request_uri",
2874
+ in: "query",
2875
+ required: false,
2876
+ schema: { type: "string" },
2877
+ description: "Pushed Authorization Request URI referencing stored parameters"
2878
+ },
2867
2879
  {
2868
2880
  name: "code_challenge",
2869
2881
  in: "query",
@@ -3436,7 +3448,8 @@ const oauthProvider = (options) => {
3436
3448
  "native",
3437
3449
  "user-agent-based"
3438
3450
  ]).optional(),
3439
- subject_type: z.enum(["public", "pairwise"]).optional()
3451
+ subject_type: z.enum(["public", "pairwise"]).optional(),
3452
+ skip_consent: z.boolean().optional()
3440
3453
  }),
3441
3454
  metadata: { openapi: {
3442
3455
  description: "Register an OAuth2 application",
@@ -3698,8 +3711,21 @@ async function authorizeEndpoint(ctx, opts, settings) {
3698
3711
  error_description: "request not found",
3699
3712
  error: "invalid_request"
3700
3713
  });
3701
- const query = ctx.query;
3702
- await oAuthState.set({ query: query.toString() });
3714
+ let query = ctx.query;
3715
+ if (query.request_uri) {
3716
+ if (!opts.requestUriResolver) return handleRedirect(ctx, getErrorURL(ctx, "invalid_request_uri", "request_uri not supported"));
3717
+ const resolvedParams = await opts.requestUriResolver({
3718
+ requestUri: query.request_uri,
3719
+ clientId: query.client_id ?? "",
3720
+ ctx
3721
+ });
3722
+ if (!resolvedParams) return handleRedirect(ctx, getErrorURL(ctx, "invalid_request_uri", "request_uri is invalid or expired"));
3723
+ const urlClientId = query.client_id;
3724
+ query = resolvedParams;
3725
+ if (urlClientId) query.client_id = urlClientId;
3726
+ }
3727
+ ctx.query = query;
3728
+ await oAuthState.set({ query: serializeAuthorizationQuery(query).toString() });
3703
3729
  if (!query.client_id) return handleRedirect(ctx, getErrorURL(ctx, "invalid_client", "client_id is required"));
3704
3730
  if (!query.response_type) return handleRedirect(ctx, getErrorURL(ctx, "invalid_request", "response_type is required"));
3705
3731
  const promptSet = ctx.query?.prompt ? parsePrompt(ctx.query?.prompt) : void 0;
@@ -3709,7 +3735,15 @@ async function authorizeEndpoint(ctx, opts, settings) {
3709
3735
  const client = await getClient(ctx, opts, query.client_id);
3710
3736
  if (!client) return handleRedirect(ctx, getErrorURL(ctx, "invalid_client", "client_id is required"));
3711
3737
  if (client.disabled) return handleRedirect(ctx, getErrorURL(ctx, "client_disabled", "client is disabled"));
3712
- if (!client.redirectUris?.find((url) => url === query.redirect_uri) || !query.redirect_uri) return handleRedirect(ctx, getErrorURL(ctx, "invalid_redirect", "invalid redirect uri"));
3738
+ if (!client.redirectUris?.find((url) => {
3739
+ if (url === query.redirect_uri) return true;
3740
+ try {
3741
+ const registered = new URL(url);
3742
+ const requested = new URL(query.redirect_uri);
3743
+ 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;
3744
+ } catch {}
3745
+ return false;
3746
+ }) || !query.redirect_uri) return handleRedirect(ctx, getErrorURL(ctx, "invalid_redirect", "invalid redirect uri"));
3713
3747
  let requestedScopes = query.scope?.split(" ").filter((s) => s);
3714
3748
  if (requestedScopes) {
3715
3749
  const validScopes = new Set(client.scopes ?? opts.scopes);
@@ -3814,6 +3848,11 @@ async function authorizeEndpoint(ctx, opts, settings) {
3814
3848
  referenceId
3815
3849
  });
3816
3850
  }
3851
+ function serializeAuthorizationQuery(query) {
3852
+ const params = new URLSearchParams();
3853
+ for (const [key, value] of Object.entries(query)) if (value != null) params.set(key, String(value));
3854
+ return params;
3855
+ }
3817
3856
  async function redirectWithAuthorizationCode(ctx, opts, verificationValue) {
3818
3857
  const code = generateRandomString(32, "a-z", "A-Z", "0-9");
3819
3858
  const iat = Math.floor(Date.now() / 1e3);
@@ -3824,7 +3863,7 @@ async function redirectWithAuthorizationCode(ctx, opts, verificationValue) {
3824
3863
  expiresAt: /* @__PURE__ */ new Date(exp * 1e3),
3825
3864
  value: JSON.stringify({
3826
3865
  type: "authorization_code",
3827
- query: ctx.query,
3866
+ query: verificationValue.query,
3828
3867
  userId: verificationValue.userId,
3829
3868
  sessionId: verificationValue?.sessionId,
3830
3869
  referenceId: verificationValue.referenceId,
@@ -3854,7 +3893,7 @@ async function redirectWithPromptCode(ctx, opts, type, page) {
3854
3893
  }
3855
3894
  async function signParams(ctx, opts) {
3856
3895
  const exp = Math.floor(Date.now() / 1e3) + (opts.codeExpiresIn ?? 600);
3857
- const params = new URLSearchParams(ctx.query);
3896
+ const params = serializeAuthorizationQuery(ctx.query);
3858
3897
  params.set("exp", String(exp));
3859
3898
  const signature = await makeSignature(params.toString(), ctx.context.secret);
3860
3899
  params.append("sig", signature);