@better-auth/sso 1.7.1 → 1.7.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/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as SSOPlugin } from "./index-CZytzKv6.mjs";
1
+ import { t as SSOPlugin } from "./index-sM6JWXeV.mjs";
2
2
  import { DBFieldAttribute } from "better-auth/db";
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-BbG1DbY-.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-BA_IW5w3.mjs";
2
2
  //#region src/client.ts
3
3
  const ssoClient = (options) => {
4
4
  return {
@@ -1511,7 +1511,7 @@ declare const callbackSSO: (options?: SSOOptions) => _$better_call0.StrictEndpoi
1511
1511
  };
1512
1512
  scope: "server";
1513
1513
  };
1514
- }, void>;
1514
+ }, never>;
1515
1515
  /**
1516
1516
  * Shared OIDC callback endpoint (no `:providerId` in path).
1517
1517
  * Used when `options.redirectURI` is set — the `providerId` is read from
@@ -1539,7 +1539,7 @@ declare const callbackSSOShared: (options?: SSOOptions) => _$better_call0.Strict
1539
1539
  error_description: z.ZodOptional<z.ZodString>;
1540
1540
  }, z.core.$strip>;
1541
1541
  allowedMediaTypes: readonly ["application/x-www-form-urlencoded", "application/json"];
1542
- }, void>;
1542
+ }, never>;
1543
1543
  declare const acsEndpoint: (options?: SSOOptions) => _$better_call0.StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
1544
1544
  method: ("GET" | "POST")[];
1545
1545
  body: z.ZodOptional<z.ZodObject<{
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as SAMLIdentityProviderMetadata, B as SSOUserResolutionContext, C as deriveSAMLIdentityProviderEntityID, D as DEFAULT_MAX_SAML_RESPONSE_SIZE, E as DEFAULT_MAX_SAML_METADATA_SIZE, F as SSOProviderMutationGuardInput, G as DigestAlgorithm, H as AlgorithmValidationOptions, I as SSOProviderReference, K as KeyEncryptionAlgorithm, L as SSOProviderUserProfile, M as SSOOptions, N as SSOProvider, O as OIDCConfig, P as SSOProviderMutationGuardContext, R as SSOSAMLUserResolutionInput, S as SAMLServiceProviderPolicy, T as DEFAULT_CLOCK_SKEW_MS, U as DataEncryptionAlgorithm, V as SSOUserResolutionInput, W as DeprecatedAlgorithmBehavior, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SSOOIDCUserResolutionInput, k as SAMLConfig, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, q as SignatureAlgorithm, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as deriveSAMLServiceProviderPolicy, x as validateSAMLTimestamp, y as SAMLConditions, z as SSOUserResolution } from "./index-CZytzKv6.mjs";
1
+ import { A as SAMLIdentityProviderMetadata, B as SSOUserResolutionContext, C as deriveSAMLIdentityProviderEntityID, D as DEFAULT_MAX_SAML_RESPONSE_SIZE, E as DEFAULT_MAX_SAML_METADATA_SIZE, F as SSOProviderMutationGuardInput, G as DigestAlgorithm, H as AlgorithmValidationOptions, I as SSOProviderReference, K as KeyEncryptionAlgorithm, L as SSOProviderUserProfile, M as SSOOptions, N as SSOProvider, O as OIDCConfig, P as SSOProviderMutationGuardContext, R as SSOSAMLUserResolutionInput, S as SAMLServiceProviderPolicy, T as DEFAULT_CLOCK_SKEW_MS, U as DataEncryptionAlgorithm, V as SSOUserResolutionInput, W as DeprecatedAlgorithmBehavior, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SSOOIDCUserResolutionInput, k as SAMLConfig, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, q as SignatureAlgorithm, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as deriveSAMLServiceProviderPolicy, x as validateSAMLTimestamp, y as SAMLConditions, z as SSOUserResolution } from "./index-sM6JWXeV.mjs";
2
2
  export { AlgorithmValidationOptions, DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, DeprecatedAlgorithmBehavior, DigestAlgorithm, DiscoverOIDCConfigParams, DiscoveryError, DiscoveryErrorCode, HydratedOIDCConfig, KeyEncryptionAlgorithm, OIDCConfig, OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, RequiredDiscoveryField, SAMLConditions, SAMLConfig, SAMLIdentityProviderMetadata, SAMLServiceProviderPolicy, SSOOIDCUserResolutionInput, SSOOptions, SSOPlugin, SSOProvider, SSOProviderMutationGuardContext, SSOProviderMutationGuardInput, SSOProviderReference, SSOProviderUserProfile, SSOSAMLUserResolutionInput, SSOUserResolution, SSOUserResolutionContext, SSOUserResolutionInput, SignatureAlgorithm, TimestampValidationOptions, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as PACKAGE_VERSION } from "./version-BbG1DbY-.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-BA_IW5w3.mjs";
2
2
  import { APIError, addOAuthServerContext, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
3
3
  import { XMLParser, XMLValidator } from "fast-xml-parser";
4
4
  import { X509Certificate } from "node:crypto";
@@ -18,6 +18,7 @@ import samlifyDefault from "samlify";
18
18
  import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
19
19
  import { parseInputData, toZodSchema } from "better-auth/db";
20
20
  import { isAPIError } from "@better-auth/core/utils/is-api-error";
21
+ import { appendQueryParams } from "@better-auth/core/utils/url";
21
22
  import { HIDE_METADATA, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, authorizationCodeRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, generateGenericState, generateState, getOAuth2Tokens, parseGenericState, parseState } from "better-auth";
22
23
  import { deleteSessionCookie, setAccountCookie, setSessionCookie } from "better-auth/cookies";
23
24
  import { additionalAuthorizationParamsSchema, handleOAuthUserInfo } from "better-auth/oauth2";
@@ -3040,7 +3041,6 @@ async function processSAMLResponse(ctx, params, options) {
3040
3041
  },
3041
3042
  account: {
3042
3043
  providerId,
3043
- issuer: accountKey.issuer,
3044
3044
  accountId: accountKey.accountId,
3045
3045
  accessToken: "",
3046
3046
  refreshToken: ""
@@ -3796,33 +3796,31 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3796
3796
  const { code, error, error_description } = ctx.query;
3797
3797
  if (!stateData) stateData = await parseState(ctx);
3798
3798
  if (!stateData) {
3799
- const errorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
3800
- throw ctx.redirect(`${errorURL}?error=invalid_state`);
3799
+ const redirectURL = appendQueryParams(ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`, new URLSearchParams({ error: "invalid_state" }));
3800
+ throw ctx.redirect(redirectURL);
3801
3801
  }
3802
3802
  const providerReference = parsedProviderReference ?? parseSSOProviderReference(stateData.serverContext?.["ssoProviderReference"]);
3803
3803
  const { callbackURL, errorURL, newUserURL, requestSignUp } = stateData;
3804
3804
  const redirectOIDCError = (error, description) => {
3805
3805
  const baseURL = errorURL || callbackURL;
3806
- const params = new URLSearchParams({
3807
- error,
3808
- error_description: description
3809
- });
3810
- const separator = baseURL.includes("?") ? "&" : "?";
3811
- throw ctx.redirect(`${baseURL}${separator}${params.toString()}`);
3806
+ const params = new URLSearchParams({ error });
3807
+ if (description) params.set("error_description", description);
3808
+ const redirectURL = appendQueryParams(baseURL, params);
3809
+ throw ctx.redirect(redirectURL);
3812
3810
  };
3813
- if (!code || error) redirectOIDCError(error || "invalid_request", error_description || (error ? error : "authorization_code_not_found"));
3811
+ if (!code || error) return redirectOIDCError(error || "invalid_request", error_description || (error ? error : "authorization_code_not_found"));
3814
3812
  const provider = await resolveOIDCProvider(ctx, options, providerId);
3815
- if (!provider) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=provider not found`);
3813
+ if (!provider) return redirectOIDCError("invalid_provider", "provider not found");
3816
3814
  const acceptedProviderReference = providerReference ?? redirectOIDCError("invalid_state", "missing_sso_provider_reference");
3817
- if (!await isCurrentSSOProviderReference(provider, acceptedProviderReference)) redirectOIDCError("invalid_state", "sso_provider_changed_during_authentication");
3815
+ if (!await isCurrentSSOProviderReference(provider, acceptedProviderReference)) return redirectOIDCError("invalid_state", "sso_provider_changed_during_authentication");
3818
3816
  if (options?.domainVerification?.enabled && !("domainVerified" in provider && provider.domainVerified)) throw new APIError("UNAUTHORIZED", { message: "Provider domain has not been verified" });
3819
3817
  let config = provider.oidcConfig;
3820
- if (!config) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=provider not found`);
3818
+ if (!config) return redirectOIDCError("invalid_provider", "provider not found");
3821
3819
  try {
3822
3820
  config = await ensureRuntimeDiscovery(config, provider.issuer, (url) => ctx.context.isTrustedOrigin(url));
3823
3821
  } catch (error) {
3824
- if (error instanceof DiscoveryError) throw ctx.redirect(`${errorURL || callbackURL}?error=discovery_failed&error_description=${encodeURIComponent(error.message)}`);
3825
- throw ctx.redirect(`${errorURL || callbackURL}?error=discovery_failed&error_description=unexpected_discovery_error`);
3822
+ if (error instanceof DiscoveryError) return redirectOIDCError("discovery_failed", error.message);
3823
+ return redirectOIDCError("discovery_failed", "unexpected_discovery_error");
3826
3824
  }
3827
3825
  if (!config.scopes) config = {
3828
3826
  ...config,
@@ -3833,7 +3831,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3833
3831
  "offline_access"
3834
3832
  ]
3835
3833
  };
3836
- if (!config.tokenEndpoint) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=token_endpoint_not_found`);
3834
+ if (!config.tokenEndpoint) return redirectOIDCError("invalid_provider", "token_endpoint_not_found");
3837
3835
  const tokenEndpoint = config.tokenEndpoint;
3838
3836
  let tokenEndpointAuth = config.tokenEndpointAuthentication === "client_secret_post" ? { method: "client_secret_post" } : { method: "client_secret_basic" };
3839
3837
  if (config.tokenEndpointAuthentication === "private_key_jwt") {
@@ -3845,7 +3843,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3845
3843
  keyId: config.privateKeyId,
3846
3844
  issuer: config.issuer
3847
3845
  });
3848
- if (!resolved || !resolved.privateKeyJwk && !resolved.privateKeyPem) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=no_private_key_available`);
3846
+ if (!resolved || !resolved.privateKeyJwk && !resolved.privateKeyPem) return redirectOIDCError("invalid_provider", "no_private_key_available");
3849
3847
  const rawAlg = config.privateKeyAlgorithm ?? resolved.algorithm;
3850
3848
  const algorithm = rawAlg && PRIVATE_KEY_JWT_SIGNING_ALGORITHMS.includes(rawAlg) ? rawAlg : void 0;
3851
3849
  tokenEndpointAuth = {
@@ -3874,16 +3872,16 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3874
3872
  body,
3875
3873
  headers
3876
3874
  }, (url) => ctx.context.isTrustedOrigin(url));
3877
- if (error) redirectOIDCError("invalid_provider", getOIDCErrorDescription(error, "token_response_error"));
3875
+ if (error) return redirectOIDCError("invalid_provider", getOIDCErrorDescription(error, "token_response_error"));
3878
3876
  if (!data) throw new Error("Token endpoint returned an empty response");
3879
3877
  return getOAuth2Tokens(data);
3880
3878
  })().catch((e) => {
3881
3879
  if (isAPIError(e)) throw e;
3882
3880
  ctx.context.logger.error("Error validating authorization code", e);
3883
- if (e instanceof DiscoveryError) redirectOIDCError("invalid_provider", e.message);
3884
- redirectOIDCError("invalid_provider", getOIDCErrorDescription(e, "token_response_error"));
3881
+ if (e instanceof DiscoveryError) return redirectOIDCError("invalid_provider", e.message);
3882
+ return redirectOIDCError("invalid_provider", getOIDCErrorDescription(e, "token_response_error"));
3885
3883
  });
3886
- if (!tokenResponse) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=token_response_not_found`);
3884
+ if (!tokenResponse) return redirectOIDCError("invalid_provider", "token_response_not_found");
3887
3885
  let userInfo = null;
3888
3886
  const mapping = config.mapping || {};
3889
3887
  const readStringClaim = (claims, claim) => {
@@ -3894,28 +3892,28 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3894
3892
  let verifiedIdToken = null;
3895
3893
  if (tokenResponse.idToken) {
3896
3894
  const jwksEndpoint = config.jwksEndpoint;
3897
- if (!jwksEndpoint) redirectOIDCError("invalid_provider", "jwks_endpoint_not_found");
3895
+ if (!jwksEndpoint) return redirectOIDCError("invalid_provider", "jwks_endpoint_not_found");
3898
3896
  const verified = await validateOIDCIdToken(tokenResponse.idToken, jwksEndpoint, {
3899
3897
  audience: config.clientId,
3900
3898
  issuer: provider.issuer
3901
3899
  }, (url) => ctx.context.isTrustedOrigin(url)).catch((error) => {
3902
- if (error instanceof DiscoveryError) redirectOIDCError("invalid_provider", error.message);
3900
+ if (error instanceof DiscoveryError) return redirectOIDCError("invalid_provider", error.message);
3903
3901
  ctx.context.logger.error(error);
3904
3902
  return null;
3905
3903
  });
3906
- if (!verified) redirectOIDCError("invalid_provider", "token_not_verified");
3907
- if (!readStringClaim(verified.payload, "sub")) redirectOIDCError("invalid_provider", "id_token_subject_missing");
3904
+ if (!verified) return redirectOIDCError("invalid_provider", "token_not_verified");
3905
+ if (!readStringClaim(verified.payload, "sub")) return redirectOIDCError("invalid_provider", "id_token_subject_missing");
3908
3906
  verifiedIdToken = verified;
3909
3907
  }
3910
- if (options?.resolveUser && !verifiedIdToken) redirectOIDCError("invalid_provider", "id_token_required_for_user_resolution");
3908
+ if (options?.resolveUser && !verifiedIdToken) return redirectOIDCError("invalid_provider", "id_token_required_for_user_resolution");
3911
3909
  if (config.userInfoEndpoint) {
3912
3910
  const userInfoResponse = await fetchOIDCEndpoint("userInfoEndpoint", config.userInfoEndpoint, { headers: { Authorization: `Bearer ${tokenResponse.accessToken}` } }, (url) => ctx.context.isTrustedOrigin(url)).catch((e) => {
3913
- if (e instanceof DiscoveryError) redirectOIDCError("invalid_provider", e.message);
3911
+ if (e instanceof DiscoveryError) return redirectOIDCError("invalid_provider", e.message);
3914
3912
  throw e;
3915
3913
  });
3916
- if (userInfoResponse.error) redirectOIDCError("invalid_provider", userInfoResponse.error.message || userInfoResponse.error.statusText || "userinfo_response_error");
3914
+ if (userInfoResponse.error) return redirectOIDCError("invalid_provider", userInfoResponse.error.message || userInfoResponse.error.statusText || "userinfo_response_error");
3917
3915
  const rawUserInfo = userInfoResponse.data ?? redirectOIDCError("invalid_provider", "userinfo_response_not_found");
3918
- if (verifiedIdToken && rawUserInfo.sub !== verifiedIdToken.payload.sub) redirectOIDCError("invalid_provider", "id_token_userinfo_subject_mismatch");
3916
+ if (verifiedIdToken && rawUserInfo.sub !== verifiedIdToken.payload.sub) return redirectOIDCError("invalid_provider", "id_token_userinfo_subject_mismatch");
3919
3917
  rawProfile = rawUserInfo;
3920
3918
  userInfo = {
3921
3919
  ...Object.fromEntries(Object.entries(mapping.extraFields || {}).map(([key, value]) => [key, rawUserInfo[value]])),
@@ -3936,8 +3934,8 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3936
3934
  name: readStringClaim(idToken, mapping.name || "name"),
3937
3935
  image: readStringClaim(idToken, mapping.image || "picture")
3938
3936
  };
3939
- } else throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=user_info_endpoint_not_found`);
3940
- if (!userInfo.email || !userInfo.id) throw ctx.redirect(`${errorURL || callbackURL}?error=invalid_provider&error_description=missing_user_info`);
3937
+ } else return redirectOIDCError("invalid_provider", "user_info_endpoint_not_found");
3938
+ if (!userInfo.email || !userInfo.id) return redirectOIDCError("invalid_provider", "missing_user_info");
3941
3939
  const userInfoEmail = userInfo.email;
3942
3940
  const userInfoId = userInfo.id;
3943
3941
  const providerUser = {
@@ -3992,7 +3990,6 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3992
3990
  idToken: tokenResponse.idToken,
3993
3991
  accessToken: tokenResponse.accessToken,
3994
3992
  refreshToken: tokenResponse.refreshToken,
3995
- issuer: accountKey.issuer,
3996
3993
  accountId: userInfoId,
3997
3994
  providerId: provider.providerId,
3998
3995
  accessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,
@@ -4025,20 +4022,10 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
4025
4022
  } catch (e) {
4026
4023
  const failedAuthentication = getFailedSSOAuthenticationResult(e);
4027
4024
  if (failedAuthentication) linked = failedAuthentication;
4028
- else if (isAPIError(e) && e.body?.code) {
4029
- const baseURL = errorURL || callbackURL;
4030
- const params = new URLSearchParams({ error: e.body.code });
4031
- if (e.body.message) params.set("error_description", e.body.message);
4032
- const sep = baseURL.includes("?") ? "&" : "?";
4033
- throw ctx.redirect(`${baseURL}${sep}${params.toString()}`);
4034
- } else throw e;
4035
- }
4036
- if (linked.error) {
4037
- const baseURL = errorURL || callbackURL;
4038
- const params = new URLSearchParams({ error: linked.error });
4039
- const sep = baseURL.includes("?") ? "&" : "?";
4040
- throw ctx.redirect(`${baseURL}${sep}${params.toString()}`);
4025
+ else if (isAPIError(e) && e.body?.code) return redirectOIDCError(e.body.code, e.body.message);
4026
+ else throw e;
4041
4027
  }
4028
+ if (linked.error) return redirectOIDCError(linked.error);
4042
4029
  const { session, user } = linked.data;
4043
4030
  if (options?.provisionUser && (linked.isRegister || options.provisionUserOnEveryLogin)) await options.provisionUser({
4044
4031
  user,
@@ -4189,13 +4176,16 @@ const callbackSSOShared = (options) => {
4189
4176
  }, async (ctx) => {
4190
4177
  const stateData = await parseState(ctx);
4191
4178
  if (!stateData) {
4192
- const errorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
4193
- throw ctx.redirect(`${errorURL}?error=invalid_state`);
4179
+ const redirectURL = appendQueryParams(ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`, new URLSearchParams({ error: "invalid_state" }));
4180
+ throw ctx.redirect(redirectURL);
4194
4181
  }
4195
4182
  const providerReference = parseSSOProviderReference(stateData.serverContext?.[SSO_PROVIDER_STATE_KEY]);
4196
4183
  if (!providerReference) {
4197
- const errorURL = stateData.errorURL || stateData.callbackURL;
4198
- throw ctx.redirect(`${errorURL}?error=invalid_state&error_description=missing_sso_provider_reference`);
4184
+ const redirectURL = appendQueryParams(stateData.errorURL || stateData.callbackURL, new URLSearchParams({
4185
+ error: "invalid_state",
4186
+ error_description: "missing_sso_provider_reference"
4187
+ }));
4188
+ throw ctx.redirect(redirectURL);
4199
4189
  }
4200
4190
  return handleOIDCCallback(ctx, options, providerReference.providerId, stateData, providerReference);
4201
4191
  });
@@ -4230,8 +4220,8 @@ const acsEndpoint = (options) => {
4230
4220
  let resolvedErrorRedirectUrl;
4231
4221
  if (ctx.method === "GET" && !ctx.body?.SAMLResponse) {
4232
4222
  if (!(await getSessionFromCtx(ctx))?.session) {
4233
- const errorURL = ctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`;
4234
- throw ctx.redirect(`${errorURL}?error=invalid_request`);
4223
+ const redirectURL = appendQueryParams(ctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`, new URLSearchParams({ error: "invalid_request" }));
4224
+ throw ctx.redirect(redirectURL);
4235
4225
  }
4236
4226
  const relayState = ctx.query?.RelayState;
4237
4227
  throw ctx.redirect(getSafeRedirectUrl([relayState], currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings)));
@@ -4298,7 +4288,13 @@ const sloEndpoint = (options) => {
4298
4288
  const relayState = ctx.body?.RelayState || ctx.query?.RelayState;
4299
4289
  const appOrigin = new URL(ctx.context.baseURL).origin;
4300
4290
  const safeErrorURL = getSafeRedirectUrl([relayState], `${appOrigin}/sso/saml2/sp/slo/${providerId}`, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
4301
- if (!samlRequest && !samlResponse) throw ctx.redirect(`${safeErrorURL}?error=invalid_request&error_description=missing_logout_data`);
4291
+ if (!samlRequest && !samlResponse) {
4292
+ const redirectURL = appendQueryParams(safeErrorURL, new URLSearchParams({
4293
+ error: "invalid_request",
4294
+ error_description: "missing_logout_data"
4295
+ }));
4296
+ throw ctx.redirect(redirectURL);
4297
+ }
4302
4298
  const provider = await findSAMLProvider(providerId, options, ctx.context.adapter);
4303
4299
  if (!provider?.samlConfig) throw APIError.from("NOT_FOUND", SAML_ERROR_CODES.SAML_PROVIDER_NOT_FOUND);
4304
4300
  const config = provider.samlConfig;
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.1";
3
+ const PACKAGE_VERSION = "1.7.3";
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/sso",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "SSO plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -56,12 +56,12 @@
56
56
  }
57
57
  },
58
58
  "dependencies": {
59
- "@xmldom/xmldom": "^0.9.10",
59
+ "@xmldom/xmldom": "^0.9.12",
60
60
  "fast-xml-parser": "^5.8.0",
61
61
  "jose": "^6.2.3",
62
62
  "samlify": "^2.13.1",
63
63
  "tldts": "^7.4.3",
64
- "zod": "^4.3.6"
64
+ "zod": "^4.5.4"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/body-parser": "^1.19.6",
@@ -72,16 +72,16 @@
72
72
  "kysely": "^0.28.17 || ^0.29.0",
73
73
  "oauth2-mock-server": "^9.0.0",
74
74
  "tsdown": "0.21.10",
75
- "@better-auth/core": "1.7.1",
76
- "@better-auth/kysely-adapter": "1.7.1",
77
- "better-auth": "1.7.1"
75
+ "@better-auth/core": "1.7.3",
76
+ "@better-auth/kysely-adapter": "1.7.3",
77
+ "better-auth": "1.7.3"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@better-auth/utils": "0.4.2",
81
81
  "@better-fetch/fetch": "1.3.1",
82
82
  "better-call": "1.4.0",
83
- "@better-auth/core": "^1.7.1",
84
- "better-auth": "^1.7.1"
83
+ "@better-auth/core": "^1.7.3",
84
+ "better-auth": "^1.7.3"
85
85
  },
86
86
  "scripts": {
87
87
  "build": "tsdown",