@better-auth/sso 1.4.8-beta.6 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @better-auth/sso@1.4.8-beta.6 build /home/runner/work/better-auth/better-auth/packages/sso
2
+ > @better-auth/sso@1.4.8 build /home/runner/work/better-auth/better-auth/packages/sso
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.17.2 powered by rolldown v1.0.0-beta.53
@@ -7,10 +7,10 @@
7
7
  ℹ entry: src/index.ts, src/client.ts
8
8
  ℹ tsconfig: tsconfig.json
9
9
  ℹ Build start
10
- ℹ dist/index.mjs 92.65 kB │ gzip: 18.13 kB
10
+ ℹ dist/index.mjs 92.73 kB │ gzip: 18.16 kB
11
11
  ℹ dist/client.mjs  0.15 kB │ gzip: 0.14 kB
12
12
  ℹ dist/index.d.mts  1.48 kB │ gzip: 0.51 kB
13
13
  ℹ dist/client.d.mts  0.49 kB │ gzip: 0.30 kB
14
- ℹ dist/index-D6Q3ojGP.d.mts 42.86 kB │ gzip: 8.79 kB
15
- ℹ 5 files, total: 137.63 kB
16
- ✔ Build complete in 11966ms
14
+ ℹ dist/index-ZWFEs7WQ.d.mts 42.70 kB │ gzip: 8.67 kB
15
+ ℹ 5 files, total: 137.55 kB
16
+ ✔ Build complete in 15873ms
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as SSOPlugin } from "./index-D6Q3ojGP.mjs";
1
+ import { t as SSOPlugin } from "./index-ZWFEs7WQ.mjs";
2
2
 
3
3
  //#region src/client.d.ts
4
4
  interface SSOClientOptions {
@@ -2,7 +2,7 @@ import { APIError } from "better-auth/api";
2
2
  import * as z$1 from "zod/v4";
3
3
  import z from "zod/v4";
4
4
  import { Awaitable, OAuth2Tokens, User } from "better-auth";
5
- import * as better_call7 from "better-call";
5
+ import * as better_call0 from "better-call";
6
6
 
7
7
  //#region src/saml/algorithms.d.ts
8
8
  declare const SignatureAlgorithm: {
@@ -257,13 +257,7 @@ interface SSOOptions {
257
257
  *
258
258
  * If you want to allow account linking for specific trusted providers, enable the `accountLinking` option in your auth config and specify those
259
259
  * providers in the `trustedProviders` list.
260
- *
261
260
  * @default false
262
- *
263
- * @deprecated This option is discouraged for new projects. Relying on provider-level `email_verified` is a weaker
264
- * trust signal compared to using `trustedProviders` in `accountLinking` or enabling `domainVerification` for SSO.
265
- * Existing configurations will continue to work, but new integrations should use explicit trust mechanisms.
266
- * This option may be removed in a future major version.
267
261
  */
268
262
  trustEmailVerified?: boolean | undefined;
269
263
  /**
@@ -371,7 +365,7 @@ interface SSOOptions {
371
365
  }
372
366
  //#endregion
373
367
  //#region src/routes/domain-verification.d.ts
374
- declare const requestDomainVerification: (options: SSOOptions) => better_call7.StrictEndpoint<"/sso/request-domain-verification", {
368
+ declare const requestDomainVerification: (options: SSOOptions) => better_call0.StrictEndpoint<"/sso/request-domain-verification", {
375
369
  method: "POST";
376
370
  body: z$1.ZodObject<{
377
371
  providerId: z$1.ZodString;
@@ -393,7 +387,7 @@ declare const requestDomainVerification: (options: SSOOptions) => better_call7.S
393
387
  };
394
388
  };
395
389
  };
396
- use: ((inputContext: better_call7.MiddlewareInputContext<better_call7.MiddlewareOptions>) => Promise<{
390
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
397
391
  session: {
398
392
  session: Record<string, any> & {
399
393
  id: string;
@@ -419,7 +413,7 @@ declare const requestDomainVerification: (options: SSOOptions) => better_call7.S
419
413
  }, {
420
414
  domainVerificationToken: string;
421
415
  }>;
422
- declare const verifyDomain: (options: SSOOptions) => better_call7.StrictEndpoint<"/sso/verify-domain", {
416
+ declare const verifyDomain: (options: SSOOptions) => better_call0.StrictEndpoint<"/sso/verify-domain", {
423
417
  method: "POST";
424
418
  body: z$1.ZodObject<{
425
419
  providerId: z$1.ZodString;
@@ -444,7 +438,7 @@ declare const verifyDomain: (options: SSOOptions) => better_call7.StrictEndpoint
444
438
  };
445
439
  };
446
440
  };
447
- use: ((inputContext: better_call7.MiddlewareInputContext<better_call7.MiddlewareOptions>) => Promise<{
441
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
448
442
  session: {
449
443
  session: Record<string, any> & {
450
444
  id: string;
@@ -488,7 +482,7 @@ interface SAMLConditions {
488
482
  * @throws {APIError} If timestamps are invalid, expired, or not yet valid
489
483
  */
490
484
  declare function validateSAMLTimestamp(conditions: SAMLConditions | undefined, options?: TimestampValidationOptions): void;
491
- declare const spMetadata: () => better_call7.StrictEndpoint<"/sso/saml2/sp/metadata", {
485
+ declare const spMetadata: () => better_call0.StrictEndpoint<"/sso/saml2/sp/metadata", {
492
486
  method: "GET";
493
487
  query: z.ZodObject<{
494
488
  providerId: z.ZodString;
@@ -510,7 +504,7 @@ declare const spMetadata: () => better_call7.StrictEndpoint<"/sso/saml2/sp/metad
510
504
  };
511
505
  };
512
506
  }, Response>;
513
- declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_call7.StrictEndpoint<"/sso/register", {
507
+ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_call0.StrictEndpoint<"/sso/register", {
514
508
  method: "POST";
515
509
  body: z.ZodObject<{
516
510
  providerId: z.ZodString;
@@ -589,7 +583,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
589
583
  organizationId: z.ZodOptional<z.ZodString>;
590
584
  overrideUserInfo: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
591
585
  }, z.core.$strip>;
592
- use: ((inputContext: better_call7.MiddlewareInputContext<better_call7.MiddlewareOptions>) => Promise<{
586
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
593
587
  session: {
594
588
  session: Record<string, any> & {
595
589
  id: string;
@@ -776,10 +770,18 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
776
770
  }, O["domainVerification"] extends {
777
771
  enabled: true;
778
772
  } ? {
773
+ redirectURI: string;
774
+ oidcConfig: OIDCConfig | null;
775
+ samlConfig: SAMLConfig | null;
776
+ } & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig"> & {
779
777
  domainVerified: boolean;
780
778
  domainVerificationToken: string;
781
- } & SSOProvider<O> : SSOProvider<O>>;
782
- declare const signInSSO: (options?: SSOOptions) => better_call7.StrictEndpoint<"/sign-in/sso", {
779
+ } : {
780
+ redirectURI: string;
781
+ oidcConfig: OIDCConfig | null;
782
+ samlConfig: SAMLConfig | null;
783
+ } & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig">>;
784
+ declare const signInSSO: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sign-in/sso", {
783
785
  method: "POST";
784
786
  body: z.ZodObject<{
785
787
  email: z.ZodOptional<z.ZodString>;
@@ -873,7 +875,7 @@ declare const signInSSO: (options?: SSOOptions) => better_call7.StrictEndpoint<"
873
875
  url: string;
874
876
  redirect: boolean;
875
877
  }>;
876
- declare const callbackSSO: (options?: SSOOptions) => better_call7.StrictEndpoint<"/sso/callback/:providerId", {
878
+ declare const callbackSSO: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/callback/:providerId", {
877
879
  method: "GET";
878
880
  query: z.ZodObject<{
879
881
  code: z.ZodOptional<z.ZodString>;
@@ -896,7 +898,7 @@ declare const callbackSSO: (options?: SSOOptions) => better_call7.StrictEndpoint
896
898
  scope: "server";
897
899
  };
898
900
  }, never>;
899
- declare const callbackSSOSAML: (options?: SSOOptions) => better_call7.StrictEndpoint<"/sso/saml2/callback/:providerId", {
901
+ declare const callbackSSOSAML: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/saml2/callback/:providerId", {
900
902
  method: "POST";
901
903
  body: z.ZodObject<{
902
904
  SAMLResponse: z.ZodString;
@@ -923,7 +925,7 @@ declare const callbackSSOSAML: (options?: SSOOptions) => better_call7.StrictEndp
923
925
  scope: "server";
924
926
  };
925
927
  }, never>;
926
- declare const acsEndpoint: (options?: SSOOptions) => better_call7.StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
928
+ declare const acsEndpoint: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
927
929
  method: "POST";
928
930
  params: z.ZodObject<{
929
931
  providerId: z.ZodOptional<z.ZodString>;
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as KeyEncryptionAlgorithm, C as SAMLConfig, D as DataEncryptionAlgorithm, E as AlgorithmValidationOptions, O as DeprecatedAlgorithmBehavior, S as OIDCConfig, T as SSOProvider, _ 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 SignatureAlgorithm, k as DigestAlgorithm, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as SSOOptions, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-D6Q3ojGP.mjs";
1
+ import { A as KeyEncryptionAlgorithm, C as SAMLConfig, D as DataEncryptionAlgorithm, E as AlgorithmValidationOptions, O as DeprecatedAlgorithmBehavior, S as OIDCConfig, T as SSOProvider, _ 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 SignatureAlgorithm, k as DigestAlgorithm, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as SSOOptions, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-ZWFEs7WQ.mjs";
2
2
  export { AlgorithmValidationOptions, DataEncryptionAlgorithm, DeprecatedAlgorithmBehavior, DigestAlgorithm, DiscoverOIDCConfigParams, DiscoveryError, DiscoveryErrorCode, HydratedOIDCConfig, KeyEncryptionAlgorithm, OIDCConfig, OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, RequiredDiscoveryField, SAMLConditions, SAMLConfig, SSOOptions, SSOPlugin, SSOProvider, SignatureAlgorithm, TimestampValidationOptions, computeDiscoveryUrl, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
package/dist/index.mjs CHANGED
@@ -4,6 +4,7 @@ import * as saml from "samlify";
4
4
  import { generateRandomString } from "better-auth/crypto";
5
5
  import * as z$1 from "zod/v4";
6
6
  import z from "zod/v4";
7
+ import { base64 } from "@better-auth/utils/base64";
7
8
  import { BetterFetchError, betterFetch } from "@better-fetch/fetch";
8
9
  import { HIDE_METADATA, createAuthorizationURL, generateState, parseState, validateAuthorizationCode, validateToken } from "better-auth";
9
10
  import { setSessionCookie } from "better-auth/cookies";
@@ -1297,14 +1298,15 @@ const registerSSOProvider = (options) => {
1297
1298
  }
1298
1299
  });
1299
1300
  }
1300
- return ctx.json({
1301
+ const result = {
1301
1302
  ...provider,
1302
1303
  oidcConfig: safeJsonParse(provider.oidcConfig),
1303
1304
  samlConfig: safeJsonParse(provider.samlConfig),
1304
1305
  redirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,
1305
1306
  ...options?.domainVerification?.enabled ? { domainVerified } : {},
1306
1307
  ...options?.domainVerification?.enabled ? { domainVerificationToken } : {}
1307
- });
1308
+ };
1309
+ return ctx.json(result);
1308
1310
  });
1309
1311
  };
1310
1312
  const signInSSOBodySchema = z.object({
@@ -1782,7 +1784,7 @@ const callbackSSOSAML = (options) => {
1782
1784
  } catch (error) {
1783
1785
  ctx.context.logger.error("SAML response validation failed", {
1784
1786
  error,
1785
- decodedResponse: Buffer.from(SAMLResponse, "base64").toString("utf-8")
1787
+ decodedResponse: new TextDecoder().decode(base64.decode(SAMLResponse))
1786
1788
  });
1787
1789
  throw new APIError("BAD_REQUEST", {
1788
1790
  message: "Invalid SAML response",
@@ -2016,7 +2018,7 @@ const acsEndpoint = (options) => {
2016
2018
  } catch (error) {
2017
2019
  ctx.context.logger.error("SAML response validation failed", {
2018
2020
  error,
2019
- decodedResponse: Buffer.from(SAMLResponse, "base64").toString("utf-8")
2021
+ decodedResponse: new TextDecoder().decode(base64.decode(SAMLResponse))
2020
2022
  });
2021
2023
  throw new APIError("BAD_REQUEST", {
2022
2024
  message: "Invalid SAML response",
@@ -2067,7 +2069,7 @@ const acsEndpoint = (options) => {
2067
2069
  throw ctx.redirect(`${redirectUrl}?error=unsolicited_response&error_description=IdP-initiated+SSO+not+allowed`);
2068
2070
  }
2069
2071
  }
2070
- const assertionIdAcs = extractAssertionId(Buffer.from(SAMLResponse, "base64").toString("utf-8"));
2072
+ const assertionIdAcs = extractAssertionId(new TextDecoder().decode(base64.decode(SAMLResponse)));
2071
2073
  if (assertionIdAcs) {
2072
2074
  const issuer = idp.entityMeta.getEntityID();
2073
2075
  const conditions = extract.conditions;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@better-auth/sso",
3
3
  "author": "Bereket Engida",
4
- "version": "1.4.8-beta.6",
4
+ "version": "1.4.8",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
7
7
  "types": "dist/index.d.mts",
@@ -52,6 +52,7 @@
52
52
  }
53
53
  },
54
54
  "dependencies": {
55
+ "@better-auth/utils": "0.3.0",
55
56
  "@better-fetch/fetch": "1.1.21",
56
57
  "fast-xml-parser": "^5.2.5",
57
58
  "jose": "^6.1.0",
@@ -66,10 +67,10 @@
66
67
  "express": "^5.1.0",
67
68
  "oauth2-mock-server": "^8.2.0",
68
69
  "tsdown": "^0.17.2",
69
- "better-auth": "1.4.8-beta.6"
70
+ "better-auth": "1.4.8"
70
71
  },
71
72
  "peerDependencies": {
72
- "better-auth": "1.4.8-beta.6"
73
+ "better-auth": "1.4.8"
73
74
  },
74
75
  "scripts": {
75
76
  "test": "vitest",
package/src/index.ts CHANGED
@@ -153,7 +153,7 @@ export function sso<O extends SSOOptions>(options?: O | undefined): any {
153
153
  return;
154
154
  }
155
155
 
156
- await assignOrganizationByDomain(ctx, {
156
+ await assignOrganizationByDomain(ctx as any, {
157
157
  user: newSession.user,
158
158
  provisioningOptions: options?.organizationProvisioning,
159
159
  domainVerification: options?.domainVerification,
@@ -1,7 +1,25 @@
1
- import type { GenericEndpointContext, OAuth2Tokens, User } from "better-auth";
1
+ import type { OAuth2Tokens, User } from "better-auth";
2
2
  import type { SSOOptions, SSOProvider } from "../types";
3
3
  import type { NormalizedSSOProfile } from "./types";
4
4
 
5
+ interface EndpointContext {
6
+ context: {
7
+ options: {
8
+ plugins?: Array<{ id: string }>;
9
+ };
10
+ adapter: {
11
+ findOne: <T>(options: {
12
+ model: string;
13
+ where: Array<{ field: string; value: unknown }>;
14
+ }) => Promise<T | null>;
15
+ create: (options: {
16
+ model: string;
17
+ data: Record<string, unknown>;
18
+ }) => Promise<unknown>;
19
+ };
20
+ };
21
+ }
22
+
5
23
  export interface OrganizationProvisioningOptions {
6
24
  disabled?: boolean;
7
25
  defaultRole?: "member" | "admin";
@@ -26,7 +44,7 @@ export interface AssignOrganizationFromProviderOptions {
26
44
  * Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.
27
45
  */
28
46
  export async function assignOrganizationFromProvider(
29
- ctx: GenericEndpointContext,
47
+ ctx: EndpointContext,
30
48
  options: AssignOrganizationFromProviderOptions,
31
49
  ): Promise<void> {
32
50
  const { user, profile, provider, token, provisioningOptions } = options;
@@ -96,7 +114,7 @@ export interface AssignOrganizationByDomainOptions {
96
114
  * (e.g., Google OAuth with @acme.com email gets added to Acme's org).
97
115
  */
98
116
  export async function assignOrganizationByDomain(
99
- ctx: GenericEndpointContext,
117
+ ctx: EndpointContext,
100
118
  options: AssignOrganizationByDomainOptions,
101
119
  ): Promise<void> {
102
120
  const { user, provisioningOptions, domainVerification } = options;
package/src/routes/sso.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { base64 } from "@better-auth/utils/base64";
1
2
  import { BetterFetchError, betterFetch } from "@better-fetch/fetch";
2
3
  import type { User, Verification } from "better-auth";
3
4
  import {
@@ -836,14 +837,20 @@ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
836
837
  });
837
838
  }
838
839
 
840
+ type SSOProviderResponse = {
841
+ redirectURI: string;
842
+ oidcConfig: OIDCConfig | null;
843
+ samlConfig: SAMLConfig | null;
844
+ } & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig">;
845
+
839
846
  type SSOProviderReturn = O["domainVerification"] extends { enabled: true }
840
- ? {
847
+ ? SSOProviderResponse & {
841
848
  domainVerified: boolean;
842
849
  domainVerificationToken: string;
843
- } & SSOProvider<O>
844
- : SSOProvider<O>;
850
+ }
851
+ : SSOProviderResponse;
845
852
 
846
- return ctx.json({
853
+ const result = {
847
854
  ...provider,
848
855
  oidcConfig: safeJsonParse<OIDCConfig>(
849
856
  provider.oidcConfig as unknown as string,
@@ -856,7 +863,9 @@ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
856
863
  ...(options?.domainVerification?.enabled
857
864
  ? { domainVerificationToken }
858
865
  : {}),
859
- } as unknown as SSOProviderReturn);
866
+ };
867
+
868
+ return ctx.json(result as SSOProviderReturn);
860
869
  },
861
870
  );
862
871
  };
@@ -1807,8 +1816,8 @@ export const callbackSSOSAML = (options?: SSOOptions) => {
1807
1816
  } catch (error) {
1808
1817
  ctx.context.logger.error("SAML response validation failed", {
1809
1818
  error,
1810
- decodedResponse: Buffer.from(SAMLResponse, "base64").toString(
1811
- "utf-8",
1819
+ decodedResponse: new TextDecoder().decode(
1820
+ base64.decode(SAMLResponse),
1812
1821
  ),
1813
1822
  });
1814
1823
  throw new APIError("BAD_REQUEST", {
@@ -2237,8 +2246,8 @@ export const acsEndpoint = (options?: SSOOptions) => {
2237
2246
  } catch (error) {
2238
2247
  ctx.context.logger.error("SAML response validation failed", {
2239
2248
  error,
2240
- decodedResponse: Buffer.from(SAMLResponse, "base64").toString(
2241
- "utf-8",
2249
+ decodedResponse: new TextDecoder().decode(
2250
+ base64.decode(SAMLResponse),
2242
2251
  ),
2243
2252
  });
2244
2253
  throw new APIError("BAD_REQUEST", {
@@ -2334,8 +2343,8 @@ export const acsEndpoint = (options?: SSOOptions) => {
2334
2343
  }
2335
2344
 
2336
2345
  // Assertion Replay Protection
2337
- const samlContentAcs = Buffer.from(SAMLResponse, "base64").toString(
2338
- "utf-8",
2346
+ const samlContentAcs = new TextDecoder().decode(
2347
+ base64.decode(SAMLResponse),
2339
2348
  );
2340
2349
  const assertionIdAcs = extractAssertionId(samlContentAcs);
2341
2350
 
package/src/types.ts CHANGED
@@ -231,13 +231,7 @@ export interface SSOOptions {
231
231
  *
232
232
  * If you want to allow account linking for specific trusted providers, enable the `accountLinking` option in your auth config and specify those
233
233
  * providers in the `trustedProviders` list.
234
- *
235
234
  * @default false
236
- *
237
- * @deprecated This option is discouraged for new projects. Relying on provider-level `email_verified` is a weaker
238
- * trust signal compared to using `trustedProviders` in `accountLinking` or enabling `domainVerification` for SSO.
239
- * Existing configurations will continue to work, but new integrations should use explicit trust mechanisms.
240
- * This option may be removed in a future major version.
241
235
  */
242
236
  trustEmailVerified?: boolean | undefined;
243
237
  /**