@better-auth/sso 1.7.0-rc.3 → 1.7.0-rc.5

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-Sxfa9KQp.mjs";
1
+ import { t as SSOPlugin } from "./index-SHmV18t8.mjs";
2
2
  import { DBFieldAttribute } from "better-auth/db";
3
3
  //#region src/client.d.ts
4
4
  interface SSOClientOptions {
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as PACKAGE_VERSION } from "./version-xoFrTW0m.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-CwxXc1Y6.mjs";
2
2
  //#region src/client.ts
3
3
  const ssoClient = (options) => {
4
4
  return {
@@ -263,7 +263,7 @@ interface BaseSSOUserResolutionInput {
263
263
  providerId: string;
264
264
  accountKey: {
265
265
  issuer: string;
266
- providerAccountId: string;
266
+ accountId: string;
267
267
  };
268
268
  providerUser: SSOProviderUserProfile;
269
269
  providerReference: SSOProviderReference;
@@ -704,7 +704,7 @@ declare const requestDomainVerification: (options: SSOOptions) => import("better
704
704
  };
705
705
  };
706
706
  };
707
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
707
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
708
708
  session: {
709
709
  session: Record<string, any> & {
710
710
  id: string;
@@ -726,7 +726,7 @@ declare const requestDomainVerification: (options: SSOOptions) => import("better
726
726
  image?: string | null | undefined;
727
727
  };
728
728
  };
729
- }>)[];
729
+ }>>[];
730
730
  }, {
731
731
  domainVerificationToken: string;
732
732
  }>;
@@ -755,7 +755,7 @@ declare const verifyDomain: (options: SSOOptions) => import("better-call").Stric
755
755
  };
756
756
  };
757
757
  };
758
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
758
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
759
759
  session: {
760
760
  session: Record<string, any> & {
761
761
  id: string;
@@ -777,7 +777,7 @@ declare const verifyDomain: (options: SSOOptions) => import("better-call").Stric
777
777
  image?: string | null | undefined;
778
778
  };
779
779
  };
780
- }>)[];
780
+ }>>[];
781
781
  }, void>;
782
782
  //#endregion
783
783
  //#region src/utils.d.ts
@@ -795,7 +795,7 @@ type SanitizedCert = ParsedCert | {
795
795
  };
796
796
  declare const listSSOProviders: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/providers", {
797
797
  method: "GET";
798
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
798
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
799
799
  session: {
800
800
  session: Record<string, any> & {
801
801
  id: string;
@@ -817,7 +817,7 @@ declare const listSSOProviders: (options?: SSOOptions) => import("better-call").
817
817
  image?: string | null | undefined;
818
818
  };
819
819
  };
820
- }>)[];
820
+ }>>[];
821
821
  metadata: {
822
822
  openapi: {
823
823
  operationId: string;
@@ -866,7 +866,7 @@ declare const listSSOProviders: (options?: SSOOptions) => import("better-call").
866
866
  }>;
867
867
  declare const getSSOProvider: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/get-provider", {
868
868
  method: "GET";
869
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
869
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
870
870
  session: {
871
871
  session: Record<string, any> & {
872
872
  id: string;
@@ -888,7 +888,7 @@ declare const getSSOProvider: (options?: SSOOptions) => import("better-call").St
888
888
  image?: string | null | undefined;
889
889
  };
890
890
  };
891
- }>)[];
891
+ }>>[];
892
892
  query: z.ZodObject<{
893
893
  providerId: z.ZodString;
894
894
  }, z.core.$strip>;
@@ -944,7 +944,7 @@ declare const getSSOProvider: (options?: SSOOptions) => import("better-call").St
944
944
  }>;
945
945
  declare const updateSSOProvider: (options: SSOOptions) => import("better-call").StrictEndpoint<"/sso/update-provider", {
946
946
  method: "POST";
947
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
947
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
948
948
  session: {
949
949
  session: Record<string, any> & {
950
950
  id: string;
@@ -966,7 +966,7 @@ declare const updateSSOProvider: (options: SSOOptions) => import("better-call").
966
966
  image?: string | null | undefined;
967
967
  };
968
968
  };
969
- }>)[];
969
+ }>>[];
970
970
  body: z.ZodObject<{
971
971
  issuer: z.ZodOptional<z.ZodString>;
972
972
  domain: z.ZodOptional<z.ZodString>;
@@ -1100,7 +1100,7 @@ declare const updateSSOProvider: (options: SSOOptions) => import("better-call").
1100
1100
  }>;
1101
1101
  declare const deleteSSOProvider: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/delete-provider", {
1102
1102
  method: "POST";
1103
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1103
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1104
1104
  session: {
1105
1105
  session: Record<string, any> & {
1106
1106
  id: string;
@@ -1122,7 +1122,7 @@ declare const deleteSSOProvider: (options?: SSOOptions) => import("better-call")
1122
1122
  image?: string | null | undefined;
1123
1123
  };
1124
1124
  };
1125
- }>)[];
1125
+ }>>[];
1126
1126
  body: z.ZodObject<{
1127
1127
  providerId: z.ZodString;
1128
1128
  }, z.core.$strip>;
@@ -1172,7 +1172,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => import(
1172
1172
  body: z.ZodObject<{
1173
1173
  [x: string]: z.ZodOptional<z.ZodAny>;
1174
1174
  }, z.core.$strip>;
1175
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1175
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1176
1176
  session: {
1177
1177
  session: Record<string, any> & {
1178
1178
  id: string;
@@ -1194,7 +1194,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => import(
1194
1194
  image?: string | null | undefined;
1195
1195
  };
1196
1196
  };
1197
- }>)[];
1197
+ }>>[];
1198
1198
  metadata: {
1199
1199
  $Infer: {
1200
1200
  body: Record<string, any> & SSOProviderAdditionalFieldsInput<O>;
@@ -1598,7 +1598,7 @@ declare const initiateSLO: (options?: SSOOptions) => import("better-call").Stric
1598
1598
  body: z.ZodObject<{
1599
1599
  callbackURL: z.ZodOptional<z.ZodString>;
1600
1600
  }, z.core.$strip>;
1601
- use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1601
+ use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
1602
1602
  session: {
1603
1603
  session: Record<string, any> & {
1604
1604
  id: string;
@@ -1620,7 +1620,7 @@ declare const initiateSLO: (options?: SSOOptions) => import("better-call").Stric
1620
1620
  image?: string | null | undefined;
1621
1621
  };
1622
1622
  };
1623
- }>)[];
1623
+ }>>[];
1624
1624
  metadata: {
1625
1625
  readonly scope: "server";
1626
1626
  };
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-Sxfa9KQp.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-SHmV18t8.mjs";
2
2
  export { type AlgorithmValidationOptions, DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, type DeprecatedAlgorithmBehavior, DigestAlgorithm, type DiscoverOIDCConfigParams, DiscoveryError, type DiscoveryErrorCode, type HydratedOIDCConfig, KeyEncryptionAlgorithm, type OIDCConfig, type OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, type RequiredDiscoveryField, type SAMLConditions, type SAMLConfig, type SAMLIdentityProviderMetadata, type SAMLServiceProviderPolicy, type SSOOIDCUserResolutionInput, type SSOOptions, SSOPlugin, type SSOProvider, type SSOProviderMutationGuardContext, type SSOProviderMutationGuardInput, type SSOProviderReference, type SSOProviderUserProfile, type SSOSAMLUserResolutionInput, type SSOUserResolution, type SSOUserResolutionContext, type SSOUserResolutionInput, SignatureAlgorithm, type 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-xoFrTW0m.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-CwxXc1Y6.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";
@@ -151,98 +151,146 @@ function maskClientId(clientId) {
151
151
  }
152
152
  //#endregion
153
153
  //#region src/linking/org-assignment.ts
154
- /**
155
- * Assigns a user to an organization based on the SSO provider's organizationId.
156
- * Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.
157
- */
158
- async function assignOrganizationFromProvider(ctx, options) {
159
- const { user, profile, provider, token, provisioningOptions } = options;
160
- if (!provider.organizationId) return;
161
- if (provisioningOptions?.disabled) return;
162
- if (!ctx.context.hasPlugin("organization")) return;
154
+ const VERIFIED_PROVIDER_PAGE_SIZE = 100;
155
+ function getEmailDomain(email) {
156
+ const parts = email.trim().toLowerCase().split("@");
157
+ if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
158
+ const domain = parts[1];
159
+ if (domain.includes("/") || domain.includes("\\") || domain.includes(":")) return null;
160
+ const normalizedDomains = parseProviderDomains(domain);
161
+ if (!normalizedDomains || normalizedDomains.length !== 1) return null;
162
+ return normalizedDomains[0] ?? null;
163
+ }
164
+ async function findVerifiedDomainProviders(ctx, domain) {
165
+ const matchingProviders = [];
166
+ let offset = 0;
167
+ while (true) {
168
+ const page = await ctx.context.adapter.findMany({
169
+ model: "ssoProvider",
170
+ where: [{
171
+ field: "domainVerified",
172
+ value: true
173
+ }],
174
+ limit: VERIFIED_PROVIDER_PAGE_SIZE,
175
+ offset,
176
+ sortBy: {
177
+ field: "providerId",
178
+ direction: "asc"
179
+ }
180
+ });
181
+ matchingProviders.push(...page.filter((provider) => domainMatches(domain, provider.domain)));
182
+ if (page.length < VERIFIED_PROVIDER_PAGE_SIZE) return matchingProviders;
183
+ offset += page.length;
184
+ }
185
+ }
186
+ async function assignOrganization(ctx, options) {
187
+ if (options.provisioningOptions?.disabled) return "provisioning-disabled";
188
+ if (!ctx.context.hasPlugin("organization")) return "ineligible-source";
189
+ let user;
190
+ let provider;
191
+ let organizationId;
192
+ let userInfo;
193
+ let token;
194
+ if (options.reason === "provider-bound") {
195
+ if (!options.provider.organizationId) return "no-target";
196
+ user = options.user;
197
+ provider = options.provider;
198
+ organizationId = options.provider.organizationId;
199
+ userInfo = options.profile.rawAttributes || {};
200
+ token = options.token;
201
+ } else {
202
+ if (!options.domainVerification?.enabled) return "provisioning-disabled";
203
+ const canonicalUser = await ctx.context.internalAdapter.findUserById(options.user.id);
204
+ if (!canonicalUser) {
205
+ ctx.context.logger.error("Unable to assign SSO organization membership because the canonical user was not found", { userId: options.user.id });
206
+ return "ineligible-source";
207
+ }
208
+ if (!canonicalUser.emailVerified) return "unverified-identity";
209
+ const domain = getEmailDomain(canonicalUser.email);
210
+ if (!domain) return "unverified-identity";
211
+ const matchingProviders = (await findVerifiedDomainProviders(ctx, domain)).filter((matchingProvider) => Boolean(matchingProvider.organizationId));
212
+ if (new Set(matchingProviders.map((matchingProvider) => matchingProvider.organizationId)).size > 1) {
213
+ ctx.context.logger.warn("Skipped SSO organization provisioning because a verified domain maps to multiple organizations", {
214
+ domain,
215
+ userId: canonicalUser.id
216
+ });
217
+ return "ambiguous-target";
218
+ }
219
+ if (matchingProviders.length === 0) return "no-target";
220
+ matchingProviders.sort((left, right) => left.providerId.localeCompare(right.providerId));
221
+ const selectedProvider = matchingProviders[0];
222
+ if (!selectedProvider) return "no-target";
223
+ user = canonicalUser;
224
+ provider = selectedProvider;
225
+ organizationId = selectedProvider.organizationId;
226
+ userInfo = {};
227
+ token = void 0;
228
+ }
163
229
  if (await ctx.context.adapter.findOne({
164
230
  model: "member",
165
231
  where: [{
166
232
  field: "organizationId",
167
- value: provider.organizationId
233
+ value: organizationId
168
234
  }, {
169
235
  field: "userId",
170
236
  value: user.id
171
237
  }]
172
- })) return;
173
- const role = provisioningOptions?.getRole ? await provisioningOptions.getRole({
238
+ })) return "already-member";
239
+ if (await ctx.context.adapter.findOne({
240
+ model: "invitation",
241
+ where: [
242
+ {
243
+ field: "organizationId",
244
+ value: organizationId
245
+ },
246
+ {
247
+ field: "email",
248
+ value: user.email.toLowerCase()
249
+ },
250
+ {
251
+ field: "status",
252
+ value: "pending"
253
+ },
254
+ {
255
+ field: "expiresAt",
256
+ value: /* @__PURE__ */ new Date(),
257
+ operator: "gt"
258
+ }
259
+ ]
260
+ })) return "invitation-pending";
261
+ const role = options.provisioningOptions?.getRole ? await options.provisioningOptions.getRole({
174
262
  user,
175
- userInfo: profile.rawAttributes || {},
263
+ userInfo,
176
264
  token,
177
265
  provider
178
- }) : provisioningOptions?.defaultRole || "member";
266
+ }) : options.provisioningOptions?.defaultRole || "member";
179
267
  await ctx.context.adapter.create({
180
268
  model: "member",
181
269
  data: {
182
- organizationId: provider.organizationId,
270
+ organizationId,
183
271
  userId: user.id,
184
272
  role,
185
273
  createdAt: /* @__PURE__ */ new Date()
186
274
  }
187
275
  });
276
+ return "assigned";
188
277
  }
189
278
  /**
190
- * Assigns a user to an organization based on their email domain.
191
- * Looks up SSO providers that match the user's email domain and assigns
192
- * the user to the associated organization.
193
- *
194
- * This enables domain-based org assignment for non-SSO sign-in methods
195
- * (e.g., Google OAuth with @acme.com email gets added to Acme's org).
279
+ * Assigns a user to the organization explicitly bound to an SSO provider.
196
280
  */
197
- async function assignOrganizationByDomain(ctx, options) {
198
- const { user, provisioningOptions, domainVerification } = options;
199
- if (provisioningOptions?.disabled) return;
200
- if (!ctx.context.hasPlugin("organization")) return;
201
- const domain = user.email.split("@")[1];
202
- if (!domain) return;
203
- const whereClause = [{
204
- field: "domain",
205
- value: domain
206
- }];
207
- if (domainVerification?.enabled) whereClause.push({
208
- field: "domainVerified",
209
- value: true
210
- });
211
- let ssoProvider = await ctx.context.adapter.findOne({
212
- model: "ssoProvider",
213
- where: whereClause
281
+ async function assignOrganizationFromProvider(ctx, options) {
282
+ await assignOrganization(ctx, {
283
+ reason: "provider-bound",
284
+ ...options
214
285
  });
215
- if (!ssoProvider) ssoProvider = (await ctx.context.adapter.findMany({
216
- model: "ssoProvider",
217
- where: domainVerification?.enabled ? [{
218
- field: "domainVerified",
219
- value: true
220
- }] : []
221
- })).find((p) => domainMatches(domain, p.domain)) ?? null;
222
- if (!ssoProvider || !ssoProvider.organizationId) return;
223
- if (await ctx.context.adapter.findOne({
224
- model: "member",
225
- where: [{
226
- field: "organizationId",
227
- value: ssoProvider.organizationId
228
- }, {
229
- field: "userId",
230
- value: user.id
231
- }]
232
- })) return;
233
- const role = provisioningOptions?.getRole ? await provisioningOptions.getRole({
234
- user,
235
- userInfo: {},
236
- provider: ssoProvider
237
- }) : provisioningOptions?.defaultRole || "member";
238
- await ctx.context.adapter.create({
239
- model: "member",
240
- data: {
241
- organizationId: ssoProvider.organizationId,
242
- userId: user.id,
243
- role,
244
- createdAt: /* @__PURE__ */ new Date()
245
- }
286
+ }
287
+ /**
288
+ * Assigns a verified user to the organization for their verified email domain.
289
+ */
290
+ async function assignOrganizationByDomain(ctx, options) {
291
+ await assignOrganization(ctx, {
292
+ reason: "verified-domain",
293
+ ...options
246
294
  });
247
295
  }
248
296
  //#endregion
@@ -2452,6 +2500,23 @@ const deleteSSOProvider = (options) => {
2452
2500
  const DNS_LABEL_MAX_LENGTH = 63;
2453
2501
  const DEFAULT_TOKEN_PREFIX = "better-auth-token";
2454
2502
  const domainVerificationBodySchema = z.object({ providerId: z.string() });
2503
+ async function completeDomainVerification(adapter, provider) {
2504
+ if (!await adapter.incrementOne({
2505
+ model: "ssoProvider",
2506
+ where: [{
2507
+ field: "id",
2508
+ value: provider.id
2509
+ }, {
2510
+ field: "domain",
2511
+ value: provider.domain
2512
+ }],
2513
+ increment: {},
2514
+ set: { domainVerified: true }
2515
+ })) throw new APIError("CONFLICT", {
2516
+ code: "SSO_PROVIDER_CHANGED",
2517
+ message: "SSO provider changed while domain verification was in progress. Reload the provider and try again"
2518
+ });
2519
+ }
2455
2520
  function getVerificationIdentifier(options, providerId) {
2456
2521
  return `_${options.domainVerification?.tokenPrefix || DEFAULT_TOKEN_PREFIX}-${providerId}`;
2457
2522
  }
@@ -2501,7 +2566,7 @@ const verifyDomain = (options) => {
2501
2566
  description: "Verify the provider domain ownership via DNS records",
2502
2567
  responses: {
2503
2568
  "404": { description: "Provider not found" },
2504
- "409": { description: "Domain has already been verified or no pending verification exists" },
2569
+ "409": { description: "Domain has already been verified, or the provider changed while verification was in progress" },
2505
2570
  "502": { description: "Unable to verify domain ownership due to upstream validator error" },
2506
2571
  "204": { description: "Domain ownership was verified" }
2507
2572
  }
@@ -2556,14 +2621,7 @@ const verifyDomain = (options) => {
2556
2621
  code: "DOMAIN_VERIFICATION_FAILED"
2557
2622
  });
2558
2623
  }
2559
- await ctx.context.adapter.update({
2560
- model: "ssoProvider",
2561
- where: [{
2562
- field: "providerId",
2563
- value: provider.providerId
2564
- }],
2565
- update: { domainVerified: true }
2566
- });
2624
+ await completeDomainVerification(ctx.context.adapter, provider);
2567
2625
  ctx.setStatus(204);
2568
2626
  });
2569
2627
  };
@@ -2944,7 +3002,7 @@ async function processSAMLResponse(ctx, params, options) {
2944
3002
  };
2945
3003
  const accountKey = {
2946
3004
  issuer,
2947
- providerAccountId: userInfo.id
3005
+ accountId: userInfo.id
2948
3006
  };
2949
3007
  const isTrustedProvider = "domainVerified" in provider && !!provider.domainVerified && validateEmailDomain(userInfo.email, provider.domain);
2950
3008
  const callbackUrl = redirectCandidates.some(Boolean) ? samlRedirectUrl : ctx.context.baseURL;
@@ -2983,7 +3041,7 @@ async function processSAMLResponse(ctx, params, options) {
2983
3041
  account: {
2984
3042
  providerId,
2985
3043
  issuer: accountKey.issuer,
2986
- providerAccountId: accountKey.providerAccountId,
3044
+ accountId: accountKey.accountId,
2987
3045
  accessToken: "",
2988
3046
  refreshToken: ""
2989
3047
  },
@@ -3030,7 +3088,7 @@ async function processSAMLResponse(ctx, params, options) {
3030
3088
  profile: {
3031
3089
  providerType: "saml",
3032
3090
  providerId,
3033
- providerAccountId: userInfo.id,
3091
+ accountId: userInfo.id,
3034
3092
  email: userInfo.email,
3035
3093
  emailVerified: userInfo.emailVerified,
3036
3094
  rawAttributes: attributes
@@ -3891,7 +3949,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3891
3949
  };
3892
3950
  const accountKey = {
3893
3951
  issuer: verifiedIdToken && readStringClaim(verifiedIdToken.payload, "iss") || provider.issuer,
3894
- providerAccountId: userInfoId
3952
+ accountId: userInfoId
3895
3953
  };
3896
3954
  const isTrustedProvider = "domainVerified" in provider && provider.domainVerified === true && validateEmailDomain(userInfoEmail, provider.domain);
3897
3955
  let linked;
@@ -3935,7 +3993,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3935
3993
  accessToken: tokenResponse.accessToken,
3936
3994
  refreshToken: tokenResponse.refreshToken,
3937
3995
  issuer: accountKey.issuer,
3938
- providerAccountId: userInfoId,
3996
+ accountId: userInfoId,
3939
3997
  providerId: provider.providerId,
3940
3998
  accessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,
3941
3999
  refreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,
@@ -3993,7 +4051,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
3993
4051
  profile: {
3994
4052
  providerType: "oidc",
3995
4053
  providerId: provider.providerId,
3996
- providerAccountId: userInfoId,
4054
+ accountId: userInfoId,
3997
4055
  email: userInfoEmail,
3998
4056
  emailVerified: Boolean(userInfo.emailVerified),
3999
4057
  rawAttributes: userInfo
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.0-rc.3";
3
+ const PACKAGE_VERSION = "1.7.0-rc.5";
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.0-rc.3",
3
+ "version": "1.7.0-rc.5",
4
4
  "description": "SSO plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -66,22 +66,22 @@
66
66
  "devDependencies": {
67
67
  "@types/body-parser": "^1.19.6",
68
68
  "@types/express": "^5.0.6",
69
- "better-call": "1.3.7",
70
- "body-parser": "^2.2.2",
69
+ "better-call": "1.4.0",
70
+ "body-parser": "^2.3.0",
71
71
  "express": "^5.2.1",
72
72
  "kysely": "^0.28.17 || ^0.29.0",
73
73
  "oauth2-mock-server": "^9.0.0",
74
74
  "tsdown": "0.22.7",
75
- "@better-auth/core": "1.7.0-rc.3",
76
- "better-auth": "1.7.0-rc.3",
77
- "@better-auth/kysely-adapter": "1.7.0-rc.3"
75
+ "@better-auth/core": "1.7.0-rc.5",
76
+ "@better-auth/kysely-adapter": "1.7.0-rc.5",
77
+ "better-auth": "1.7.0-rc.5"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@better-auth/utils": "0.4.2",
81
81
  "@better-fetch/fetch": "1.3.1",
82
- "better-call": "1.3.7",
83
- "@better-auth/core": "^1.7.0-rc.3",
84
- "better-auth": "^1.7.0-rc.3"
82
+ "better-call": "1.4.0",
83
+ "@better-auth/core": "^1.7.0-rc.5",
84
+ "better-auth": "^1.7.0-rc.5"
85
85
  },
86
86
  "scripts": {
87
87
  "build": "tsdown",