@startino/better-auth-oidc 0.1.2 → 0.1.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.
@@ -1,8 +1,8 @@
1
1
  import * as z$1 from "zod/v4";
2
2
  import z from "zod/v4";
3
- import * as better_auth9 from "better-auth";
3
+ import * as better_auth0 from "better-auth";
4
4
  import { Awaitable, OAuth2Tokens, User } from "better-auth";
5
- import * as better_call6 from "better-call";
5
+ import * as better_call0 from "better-call";
6
6
 
7
7
  //#region src/types.d.ts
8
8
  interface OIDCMapping {
@@ -170,7 +170,7 @@ interface SSOOptions {
170
170
  }
171
171
  //#endregion
172
172
  //#region src/routes/domain-verification.d.ts
173
- declare const requestDomainVerification: (options: SSOOptions) => better_call6.StrictEndpoint<"/sso/request-domain-verification", {
173
+ declare const requestDomainVerification: (options: SSOOptions) => better_call0.StrictEndpoint<"/sso/request-domain-verification", {
174
174
  method: "POST";
175
175
  body: z$1.ZodObject<{
176
176
  providerId: z$1.ZodString;
@@ -192,7 +192,7 @@ declare const requestDomainVerification: (options: SSOOptions) => better_call6.S
192
192
  };
193
193
  };
194
194
  };
195
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
195
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
196
196
  session: {
197
197
  session: Record<string, any> & {
198
198
  id: string;
@@ -218,7 +218,7 @@ declare const requestDomainVerification: (options: SSOOptions) => better_call6.S
218
218
  }, {
219
219
  domainVerificationToken: string;
220
220
  }>;
221
- declare const verifyDomain: (options: SSOOptions) => better_call6.StrictEndpoint<"/sso/verify-domain", {
221
+ declare const verifyDomain: (options: SSOOptions) => better_call0.StrictEndpoint<"/sso/verify-domain", {
222
222
  method: "POST";
223
223
  body: z$1.ZodObject<{
224
224
  providerId: z$1.ZodString;
@@ -243,7 +243,7 @@ declare const verifyDomain: (options: SSOOptions) => better_call6.StrictEndpoint
243
243
  };
244
244
  };
245
245
  };
246
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
246
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
247
247
  session: {
248
248
  session: Record<string, any> & {
249
249
  id: string;
@@ -270,9 +270,9 @@ declare const verifyDomain: (options: SSOOptions) => better_call6.StrictEndpoint
270
270
  //# sourceMappingURL=domain-verification.d.ts.map
271
271
  //#endregion
272
272
  //#region src/routes/providers.d.ts
273
- declare const listSSOProviders: () => better_call6.StrictEndpoint<"/sso/providers", {
273
+ declare const listSSOProviders: () => better_call0.StrictEndpoint<"/sso/providers", {
274
274
  method: "GET";
275
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
275
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
276
276
  session: {
277
277
  session: Record<string, any> & {
278
278
  id: string;
@@ -328,9 +328,9 @@ declare const listSSOProviders: () => better_call6.StrictEndpoint<"/sso/provider
328
328
  } | undefined;
329
329
  }[];
330
330
  }>;
331
- declare const getSSOProvider: () => better_call6.StrictEndpoint<"/sso/get-provider", {
331
+ declare const getSSOProvider: () => better_call0.StrictEndpoint<"/sso/get-provider", {
332
332
  method: "GET";
333
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
333
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
334
334
  session: {
335
335
  session: Record<string, any> & {
336
336
  id: string;
@@ -393,9 +393,9 @@ declare const getSSOProvider: () => better_call6.StrictEndpoint<"/sso/get-provid
393
393
  tokenEndpointAuthentication: "client_secret_post" | "client_secret_basic" | undefined;
394
394
  } | undefined;
395
395
  }>;
396
- declare const updateSSOProvider: (options: SSOOptions) => better_call6.StrictEndpoint<"/sso/update-provider", {
396
+ declare const updateSSOProvider: (options: SSOOptions) => better_call0.StrictEndpoint<"/sso/update-provider", {
397
397
  method: "POST";
398
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
398
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
399
399
  session: {
400
400
  session: Record<string, any> & {
401
401
  id: string;
@@ -484,9 +484,9 @@ declare const updateSSOProvider: (options: SSOOptions) => better_call6.StrictEnd
484
484
  tokenEndpointAuthentication: "client_secret_post" | "client_secret_basic" | undefined;
485
485
  } | undefined;
486
486
  }>;
487
- declare const deleteSSOProvider: () => better_call6.StrictEndpoint<"/sso/delete-provider", {
487
+ declare const deleteSSOProvider: () => better_call0.StrictEndpoint<"/sso/delete-provider", {
488
488
  method: "POST";
489
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
489
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
490
490
  session: {
491
491
  session: Record<string, any> & {
492
492
  id: string;
@@ -536,7 +536,7 @@ declare const deleteSSOProvider: () => better_call6.StrictEndpoint<"/sso/delete-
536
536
  //# sourceMappingURL=providers.d.ts.map
537
537
  //#endregion
538
538
  //#region src/routes/sso.d.ts
539
- declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_call6.StrictEndpoint<"/sso/register", {
539
+ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_call0.StrictEndpoint<"/sso/register", {
540
540
  method: "POST";
541
541
  body: z.ZodObject<{
542
542
  providerId: z.ZodString;
@@ -569,7 +569,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
569
569
  organizationId: z.ZodOptional<z.ZodString>;
570
570
  overrideUserInfo: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
571
571
  }, z.core.$strip>;
572
- use: ((inputContext: better_auth9.MiddlewareInputContext<better_auth9.MiddlewareOptions>) => Promise<{
572
+ use: ((inputContext: better_auth0.MiddlewareInputContext<better_auth0.MiddlewareOptions>) => Promise<{
573
573
  session: {
574
574
  session: Record<string, any> & {
575
575
  id: string;
@@ -616,7 +616,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
616
616
  redirectURI: string;
617
617
  oidcConfig: OIDCConfig | null;
618
618
  } & Omit<SSOProvider<O>, "oidcConfig">>;
619
- declare const signInSSO: (options?: SSOOptions) => better_call6.StrictEndpoint<"/sign-in/sso", {
619
+ declare const signInSSO: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sign-in/sso", {
620
620
  method: "POST";
621
621
  body: z.ZodObject<{
622
622
  email: z.ZodOptional<z.ZodString>;
@@ -646,7 +646,7 @@ declare const signInSSO: (options?: SSOOptions) => better_call6.StrictEndpoint<"
646
646
  url: string;
647
647
  redirect: boolean;
648
648
  }>;
649
- declare const callbackSSO: (options?: SSOOptions) => better_call6.StrictEndpoint<"/sso/callback/:providerId", {
649
+ declare const callbackSSO: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/callback/:providerId", {
650
650
  method: "GET";
651
651
  query: z.ZodObject<{
652
652
  code: z.ZodOptional<z.ZodString>;
package/dist/index.js CHANGED
@@ -1036,7 +1036,7 @@ const registerSSOProvider = (options) => {
1036
1036
  userInfoEndpoint: body.oidcConfig.userInfoEndpoint,
1037
1037
  tokenEndpointAuthentication: body.oidcConfig.tokenEndpointAuthentication
1038
1038
  },
1039
- isTrustedOrigin: (url) => ctx.context.isTrustedOrigin(url)
1039
+ isTrustedOrigin: () => true
1040
1040
  });
1041
1041
  } catch (error) {
1042
1042
  if (error instanceof DiscoveryError) throw mapDiscoveryErrorToAPIError(error);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["z"],"sources":["../src/utils.ts","../src/linking/org-assignment.ts","../src/routes/domain-verification.ts","../src/routes/schemas.ts","../src/routes/providers.ts","../src/oidc/types.ts","../src/oidc/discovery.ts","../src/oidc/errors.ts","../src/routes/sso.ts","../src/index.ts"],"sourcesContent":["/**\n * Safely parses a value that might be a JSON string or already a parsed object.\n * This handles cases where ORMs like Drizzle might return already parsed objects\n * instead of JSON strings from TEXT/JSON columns.\n *\n * @param value - The value to parse (string, object, null, or undefined)\n * @returns The parsed object or null\n * @throws Error if string parsing fails\n */\nexport function safeJsonParse<T>(\n\tvalue: string | T | null | undefined,\n): T | null {\n\tif (!value) return null;\n\n\tif (typeof value === \"object\") {\n\t\treturn value as T;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse JSON: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Checks if a domain matches any domain in a comma-separated list.\n */\nexport const domainMatches = (searchDomain: string, domainList: string) => {\n\tconst search = searchDomain.toLowerCase();\n\tconst domains = domainList\n\t\t.split(\",\")\n\t\t.map((d) => d.trim().toLowerCase())\n\t\t.filter(Boolean);\n\treturn domains.some((d) => search === d || search.endsWith(`.${d}`));\n};\n\n/**\n * Validates email domain against allowed domain(s).\n * Supports comma-separated domains for multi-domain SSO.\n */\nexport const validateEmailDomain = (email: string, domain: string) => {\n\tconst emailDomain = email.split(\"@\")[1]?.toLowerCase();\n\tif (!emailDomain || !domain) {\n\t\treturn false;\n\t}\n\treturn domainMatches(emailDomain, domain);\n};\n\nexport function maskClientId(clientId: string): string {\n\tif (clientId.length <= 4) {\n\t\treturn \"****\";\n\t}\n\treturn `****${clientId.slice(-4)}`;\n}\n","import type { GenericEndpointContext, OAuth2Tokens, User } from \"better-auth\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\nimport { domainMatches } from \"../utils\";\nimport type { NormalizedSSOProfile } from \"./types\";\n\nexport interface OrganizationProvisioningOptions {\n\tdisabled?: boolean;\n\tdefaultRole?: string;\n\tgetRole?: (data: {\n\t\tuser: User & Record<string, any>;\n\t\tuserInfo: Record<string, any>;\n\t\ttoken?: OAuth2Tokens;\n\t\tprovider: SSOProvider<SSOOptions>;\n\t}) => Promise<string>;\n}\n\nexport interface AssignOrganizationFromProviderOptions {\n\tuser: User;\n\tprofile: NormalizedSSOProfile;\n\tprovider: SSOProvider<SSOOptions>;\n\ttoken?: OAuth2Tokens;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n}\n\n/**\n * Assigns a user to an organization based on the SSO provider's organizationId.\n * Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.\n */\nexport async function assignOrganizationFromProvider(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationFromProviderOptions,\n): Promise<void> {\n\tconst { user, profile, provider, token, provisioningOptions } = options;\n\n\tif (!provider.organizationId) {\n\t\treturn;\n\t}\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: profile.rawAttributes || {},\n\t\t\t\ttoken,\n\t\t\t\tprovider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: provider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n\nexport interface AssignOrganizationByDomainOptions {\n\tuser: User;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n\tdomainVerification?: {\n\t\tenabled?: boolean;\n\t};\n}\n\n/**\n * Assigns a user to an organization based on their email domain.\n * Looks up SSO providers that match the user's email domain and assigns\n * the user to the associated organization.\n *\n * This enables domain-based org assignment for non-SSO sign-in methods\n * (e.g., Google OAuth with @acme.com email gets added to Acme's org).\n */\nexport async function assignOrganizationByDomain(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationByDomainOptions,\n): Promise<void> {\n\tconst { user, provisioningOptions, domainVerification } = options;\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst domain = user.email.split(\"@\")[1];\n\tif (!domain) {\n\t\treturn;\n\t}\n\n\t// Support comma-separated domains for multi-domain SSO\n\t// First try exact match (fast path)\n\tconst whereClause: { field: string; value: string | boolean }[] = [\n\t\t{ field: \"domain\", value: domain },\n\t];\n\n\tif (domainVerification?.enabled) {\n\t\twhereClause.push({ field: \"domainVerified\", value: true });\n\t}\n\n\tlet ssoProvider = await ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: whereClause,\n\t});\n\n\t// If not found, search all providers for comma-separated domain match\n\tif (!ssoProvider) {\n\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\tSSOProvider<SSOOptions>\n\t\t>({\n\t\t\tmodel: \"ssoProvider\",\n\t\t\twhere: domainVerification?.enabled\n\t\t\t\t? [{ field: \"domainVerified\", value: true }]\n\t\t\t\t: [],\n\t\t});\n\t\tssoProvider =\n\t\t\tallProviders.find((p) => domainMatches(domain, p.domain)) ?? null;\n\t}\n\n\tif (!ssoProvider || !ssoProvider.organizationId) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: ssoProvider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: {},\n\t\t\t\tprovider: ssoProvider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: ssoProvider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n","import type { Verification } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport * as z from \"zod/v4\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\n\nconst DNS_LABEL_MAX_LENGTH = 63;\nconst DEFAULT_TOKEN_PREFIX = \"better-auth-token\";\n\nconst domainVerificationBodySchema = z.object({\n\tproviderId: z.string(),\n});\n\nexport function getVerificationIdentifier(\n\toptions: SSOOptions,\n\tproviderId: string,\n): string {\n\tconst tokenPrefix =\n\t\toptions.domainVerification?.tokenPrefix || DEFAULT_TOKEN_PREFIX;\n\treturn `_${tokenPrefix}-${providerId}`;\n}\n\n/**\n * DNS-over-HTTPS TXT record lookup using Cloudflare's resolver.\n * Replaces node:dns/promises for edge/serverless compatibility.\n */\nasync function resolveTxtDoH(hostname: string): Promise<string[]> {\n\tconst url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=TXT`;\n\tconst response = await fetch(url, {\n\t\theaders: { Accept: \"application/dns-json\" },\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`DoH request failed with status ${response.status}`);\n\t}\n\n\tconst data = (await response.json()) as {\n\t\tAnswer?: Array<{ type: number; data: string }>;\n\t};\n\n\tif (!data.Answer) {\n\t\treturn [];\n\t}\n\n\treturn data.Answer.filter((record) => record.type === 16).map((record) =>\n\t\t// TXT records come quoted from DoH JSON API — strip surrounding quotes\n\t\trecord.data.replace(/^\"|\"$/g, \"\"),\n\t);\n}\n\nexport const requestDomainVerification = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/request-domain-verification\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Request a domain verification\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Request a domain verification for the given SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription: \"Domain has already been verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"201\": {\n\t\t\t\t\t\t\tdescription: \"Domain submitted for verification\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst identifier = getVerificationIdentifier(\n\t\t\t\toptions,\n\t\t\t\tprovider.providerId,\n\t\t\t);\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: identifier,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (activeVerification) {\n\t\t\t\tctx.setStatus(201);\n\t\t\t\treturn ctx.json({ domainVerificationToken: activeVerification.value });\n\t\t\t}\n\n\t\t\tconst domainVerificationToken = generateRandomString(24);\n\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\tmodel: \"verification\",\n\t\t\t\tdata: {\n\t\t\t\t\tidentifier,\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tvalue: domainVerificationToken,\n\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(201);\n\t\t\treturn ctx.json({\n\t\t\t\tdomainVerificationToken,\n\t\t\t});\n\t\t},\n\t);\n};\n\nexport const verifyDomain = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/verify-domain\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify the provider domain ownership\",\n\t\t\t\t\tdescription: \"Verify the provider domain ownership via DNS records\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Domain has already been verified or no pending verification exists\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"502\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Unable to verify domain ownership due to upstream validator error\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"204\": {\n\t\t\t\t\t\t\tdescription: \"Domain ownership was verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst identifier = getVerificationIdentifier(\n\t\t\t\toptions,\n\t\t\t\tprovider.providerId,\n\t\t\t);\n\n\t\t\tif (identifier.length > DNS_LABEL_MAX_LENGTH) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `Verification identifier exceeds the DNS label limit of ${DNS_LABEL_MAX_LENGTH} characters`,\n\t\t\t\t\tcode: \"IDENTIFIER_TOO_LONG\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: identifier,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (!activeVerification) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No pending domain verification exists\",\n\t\t\t\t\tcode: \"NO_PENDING_VERIFICATION\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet records: string[] = [];\n\n\t\t\ttry {\n\t\t\t\tconst hostname = new URL(provider.domain).hostname;\n\t\t\t\trecords = await resolveTxtDoH(`${identifier}.${hostname}`);\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\"DNS resolution failure while validating domain ownership\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst record = records.find((record) =>\n\t\t\t\trecord.includes(\n\t\t\t\t\t`${activeVerification.identifier}=${activeVerification.value}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (!record) {\n\t\t\t\tthrow new APIError(\"BAD_GATEWAY\", {\n\t\t\t\t\tmessage: \"Unable to verify domain ownership. Try again later\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFICATION_FAILED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update<SSOProvider<SSOOptions>>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: provider.providerId }],\n\t\t\t\tupdate: {\n\t\t\t\t\tdomainVerified: true,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(204);\n\t\t\treturn;\n\t\t},\n\t);\n};\n","import z from \"zod/v4\";\n\nconst oidcMappingSchema = z\n\t.object({\n\t\tid: z.string().optional(),\n\t\temail: z.string().optional(),\n\t\temailVerified: z.string().optional(),\n\t\tname: z.string().optional(),\n\t\timage: z.string().optional(),\n\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t})\n\t.optional();\n\nexport const oidcConfigSchema = z.object({\n\tclientId: z.string().optional(),\n\tclientSecret: z.string().optional(),\n\tauthorizationEndpoint: z.string().url().optional(),\n\ttokenEndpoint: z.string().url().optional(),\n\tuserInfoEndpoint: z.string().url().optional(),\n\ttokenEndpointAuthentication: z\n\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t.optional(),\n\tjwksEndpoint: z.string().url().optional(),\n\tdiscoveryEndpoint: z.string().url().optional(),\n\tscopes: z.array(z.string()).optional(),\n\tpkce: z.boolean().optional(),\n\toverrideUserInfo: z.boolean().optional(),\n\tmapping: oidcMappingSchema,\n});\n\nexport const updateSSOProviderBodySchema = z.object({\n\tissuer: z.string().url().optional(),\n\tdomain: z.string().optional(),\n\toidcConfig: oidcConfigSchema.optional(),\n});\n","import type { AuthContext } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport z from \"zod/v4\";\nimport type { Member, OIDCConfig, SSOOptions } from \"../types\";\nimport { maskClientId, safeJsonParse } from \"../utils\";\nimport { updateSSOProviderBodySchema } from \"./schemas\";\n\ninterface SSOProviderRecord {\n\tid: string;\n\tproviderId: string;\n\tissuer: string;\n\tdomain: string;\n\torganizationId?: string | null;\n\tdomainVerified?: boolean;\n\tuserId: string;\n\toidcConfig?: string | null;\n}\n\nconst ADMIN_ROLES = [\"owner\", \"admin\"];\n\nasync function isOrgAdmin(\n\tctx: {\n\t\tcontext: {\n\t\t\tadapter: {\n\t\t\t\tfindOne: <T>(query: {\n\t\t\t\t\tmodel: string;\n\t\t\t\t\twhere: { field: string; value: string }[];\n\t\t\t\t}) => Promise<T | null>;\n\t\t\t};\n\t\t};\n\t},\n\tuserId: string,\n\torganizationId: string,\n): Promise<boolean> {\n\tconst member = await ctx.context.adapter.findOne<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationId },\n\t\t],\n\t});\n\tif (!member) return false;\n\tconst roles = member.role.split(\",\");\n\treturn roles.some((r) => ADMIN_ROLES.includes(r.trim()));\n}\n\nasync function batchCheckOrgAdmin(\n\tctx: {\n\t\tcontext: AuthContext;\n\t},\n\tuserId: string,\n\torganizationIds: string[],\n): Promise<Set<string>> {\n\tif (organizationIds.length === 0) {\n\t\treturn new Set();\n\t}\n\n\tconst members = await ctx.context.adapter.findMany<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationIds, operator: \"in\" },\n\t\t],\n\t});\n\n\tconst adminOrgIds = new Set<string>();\n\tfor (const member of members) {\n\t\tconst roles = member.role.split(\",\");\n\t\tif (roles.some((r: string) => ADMIN_ROLES.includes(r.trim()))) {\n\t\t\tadminOrgIds.add(member.organizationId);\n\t\t}\n\t}\n\n\treturn adminOrgIds;\n}\n\nfunction sanitizeProvider(\n\tprovider: {\n\t\tproviderId: string;\n\t\tissuer: string;\n\t\tdomain: string;\n\t\torganizationId?: string | null;\n\t\tdomainVerified?: boolean;\n\t\toidcConfig?: string | OIDCConfig | null;\n\t},\n) {\n\tlet oidcConfig: OIDCConfig | null = null;\n\n\ttry {\n\t\toidcConfig = safeJsonParse<OIDCConfig>(provider.oidcConfig as string);\n\t} catch {\n\t\toidcConfig = null;\n\t}\n\n\treturn {\n\t\tproviderId: provider.providerId,\n\t\ttype: \"oidc\" as const,\n\t\tissuer: provider.issuer,\n\t\tdomain: provider.domain,\n\t\torganizationId: provider.organizationId || null,\n\t\tdomainVerified: provider.domainVerified ?? false,\n\t\toidcConfig: oidcConfig\n\t\t\t? {\n\t\t\t\t\tdiscoveryEndpoint: oidcConfig.discoveryEndpoint,\n\t\t\t\t\tclientIdLastFour: maskClientId(oidcConfig.clientId),\n\t\t\t\t\tpkce: oidcConfig.pkce,\n\t\t\t\t\tauthorizationEndpoint: oidcConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: oidcConfig.tokenEndpoint,\n\t\t\t\t\tuserInfoEndpoint: oidcConfig.userInfoEndpoint,\n\t\t\t\t\tjwksEndpoint: oidcConfig.jwksEndpoint,\n\t\t\t\t\tscopes: oidcConfig.scopes,\n\t\t\t\t\ttokenEndpointAuthentication: oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n\nexport const listSSOProviders = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listSSOProviders\",\n\t\t\t\t\tsummary: \"List SSO providers\",\n\t\t\t\t\tdescription: \"Returns a list of SSO providers the user has access to\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"List of SSO providers\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst userId = ctx.context.session.user.id;\n\n\t\t\tconst allProviders =\n\t\t\t\tawait ctx.context.adapter.findMany<SSOProviderRecord>({\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t});\n\n\t\t\tconst userOwnedProviders = allProviders.filter(\n\t\t\t\t(p) => p.userId === userId && !p.organizationId,\n\t\t\t);\n\n\t\t\tconst orgProviders = allProviders.filter(\n\t\t\t\t(p) => p.organizationId !== null && p.organizationId !== undefined,\n\t\t\t);\n\n\t\t\tconst orgPluginEnabled = !!(ctx.context as any).hasPlugin?.(\"organization\");\n\n\t\t\tlet accessibleProviders: typeof userOwnedProviders = [\n\t\t\t\t...userOwnedProviders,\n\t\t\t];\n\n\t\t\tif (orgPluginEnabled && orgProviders.length > 0) {\n\t\t\t\tconst orgIds = [\n\t\t\t\t\t...new Set(\n\t\t\t\t\t\torgProviders\n\t\t\t\t\t\t\t.map((p) => p.organizationId)\n\t\t\t\t\t\t\t.filter((id): id is string => id !== null && id !== undefined),\n\t\t\t\t\t),\n\t\t\t\t];\n\n\t\t\t\tconst adminOrgIds = await batchCheckOrgAdmin(ctx, userId, orgIds);\n\n\t\t\t\tconst orgAccessibleProviders = orgProviders.filter(\n\t\t\t\t\t(provider) =>\n\t\t\t\t\t\tprovider.organizationId && adminOrgIds.has(provider.organizationId),\n\t\t\t\t);\n\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...orgAccessibleProviders,\n\t\t\t\t];\n\t\t\t} else if (!orgPluginEnabled) {\n\t\t\t\tconst userOwnedOrgProviders = orgProviders.filter(\n\t\t\t\t\t(p) => p.userId === userId,\n\t\t\t\t);\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...userOwnedOrgProviders,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tconst providers = accessibleProviders.map((p) =>\n\t\t\t\tsanitizeProvider(p),\n\t\t\t);\n\n\t\t\treturn ctx.json({ providers });\n\t\t},\n\t);\n};\n\nconst getSSOProviderQuerySchema = z.object({\n\tproviderId: z.string(),\n});\n\nasync function checkProviderAccess(\n\tctx: {\n\t\tcontext: AuthContext & {\n\t\t\tsession: { user: { id: string } };\n\t\t};\n\t},\n\tproviderId: string,\n) {\n\tconst userId = ctx.context.session.user.id;\n\n\tconst provider = await ctx.context.adapter.findOne<SSOProviderRecord>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t});\n\n\tif (!provider) {\n\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\tmessage: \"Provider not found\",\n\t\t});\n\t}\n\n\tlet hasAccess = false;\n\tif (provider.organizationId) {\n\t\tif ((ctx.context as any).hasPlugin?.(\"organization\")) {\n\t\t\thasAccess = await isOrgAdmin(ctx, userId, provider.organizationId);\n\t\t} else {\n\t\t\thasAccess = provider.userId === userId;\n\t\t}\n\t} else {\n\t\thasAccess = provider.userId === userId;\n\t}\n\n\tif (!hasAccess) {\n\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\tmessage: \"You don't have access to this provider\",\n\t\t});\n\t}\n\n\treturn provider;\n}\n\nexport const getSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/get-provider\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tquery: getSSOProviderQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"getSSOProvider\",\n\t\t\t\t\tsummary: \"Get SSO provider details\",\n\t\t\t\t\tdescription: \"Returns sanitized details for a specific SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider details\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.query;\n\n\t\t\tconst provider = await checkProviderAccess(ctx, providerId);\n\n\t\t\treturn ctx.json(sanitizeProvider(provider));\n\t\t},\n\t);\n};\n\nfunction parseAndValidateConfig<T>(\n\tconfigString: string | null | undefined,\n\tconfigType: \"OIDC\",\n): T {\n\tlet config: T | null = null;\n\ttry {\n\t\tconfig = safeJsonParse<T>(configString as string);\n\t} catch {\n\t\tconfig = null;\n\t}\n\tif (!config) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: `Cannot update ${configType} config for a provider that doesn't have ${configType} configured`,\n\t\t});\n\t}\n\treturn config;\n}\n\nfunction mergeOIDCConfig(\n\tcurrent: OIDCConfig,\n\tupdates: Partial<OIDCConfig>,\n\tissuer: string,\n): OIDCConfig {\n\treturn {\n\t\t...current,\n\t\t...updates,\n\t\tissuer,\n\t\tpkce: updates.pkce ?? current.pkce ?? true,\n\t\tclientId: updates.clientId ?? current.clientId,\n\t\tclientSecret: updates.clientSecret ?? current.clientSecret,\n\t\tdiscoveryEndpoint: updates.discoveryEndpoint ?? current.discoveryEndpoint,\n\t\tmapping: updates.mapping ?? current.mapping,\n\t\tscopes: updates.scopes ?? current.scopes,\n\t\tauthorizationEndpoint:\n\t\t\tupdates.authorizationEndpoint ?? current.authorizationEndpoint,\n\t\ttokenEndpoint: updates.tokenEndpoint ?? current.tokenEndpoint,\n\t\tuserInfoEndpoint: updates.userInfoEndpoint ?? current.userInfoEndpoint,\n\t\tjwksEndpoint: updates.jwksEndpoint ?? current.jwksEndpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\tupdates.tokenEndpointAuthentication ??\n\t\t\tcurrent.tokenEndpointAuthentication,\n\t};\n}\n\nexport const updateSSOProvider = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/update-provider\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: updateSSOProviderBodySchema.extend({\n\t\t\t\tproviderId: z.string(),\n\t\t\t}),\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"updateSSOProvider\",\n\t\t\t\t\tsummary: \"Update SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Partially update an SSO provider. Only provided fields are updated. If domain changes, domainVerified is reset to false.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider updated successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId, ...body } = ctx.body;\n\n\t\t\tconst { issuer, domain, oidcConfig } = body;\n\t\t\tif (!issuer && !domain && !oidcConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"No fields provided for update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst existingProvider = await checkProviderAccess(ctx, providerId);\n\n\t\t\tconst updateData: Partial<SSOProviderRecord> = {};\n\n\t\t\tif (body.issuer !== undefined) {\n\t\t\t\tupdateData.issuer = body.issuer;\n\t\t\t}\n\n\t\t\tif (body.domain !== undefined) {\n\t\t\t\tupdateData.domain = body.domain;\n\t\t\t\tif (body.domain !== existingProvider.domain) {\n\t\t\t\t\tupdateData.domainVerified = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (body.oidcConfig) {\n\t\t\t\tconst currentOidcConfig = parseAndValidateConfig<OIDCConfig>(\n\t\t\t\t\texistingProvider.oidcConfig,\n\t\t\t\t\t\"OIDC\",\n\t\t\t\t);\n\n\t\t\t\tconst updatedOidcConfig = mergeOIDCConfig(\n\t\t\t\t\tcurrentOidcConfig,\n\t\t\t\t\tbody.oidcConfig,\n\t\t\t\t\tupdateData.issuer ||\n\t\t\t\t\t\tcurrentOidcConfig.issuer ||\n\t\t\t\t\t\texistingProvider.issuer,\n\t\t\t\t);\n\n\t\t\t\tupdateData.oidcConfig = JSON.stringify(updatedOidcConfig);\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\tupdate: updateData,\n\t\t\t});\n\n\t\t\tconst fullProvider = await ctx.context.adapter.findOne<SSOProviderRecord>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!fullProvider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found after update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(sanitizeProvider(fullProvider));\n\t\t},\n\t);\n};\n\nexport const deleteSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/delete-provider\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: z.object({\n\t\t\t\tproviderId: z.string(),\n\t\t\t}),\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"deleteSSOProvider\",\n\t\t\t\t\tsummary: \"Delete SSO provider\",\n\t\t\t\t\tdescription: \"Deletes an SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider deleted successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.body;\n\n\t\t\tawait checkProviderAccess(ctx, providerId);\n\n\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t});\n\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","/**\n * OIDC Discovery Types\n *\n * Types for the OIDC discovery document and hydrated configuration.\n * Based on OpenID Connect Discovery 1.0 specification.\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\n/**\n * Raw OIDC Discovery Document as returned by the IdP's\n * .well-known/openid-configuration endpoint.\n *\n * Required fields for Better Auth's OIDC support:\n * - issuer\n * - authorization_endpoint\n * - token_endpoint\n * - jwks_uri (required for ID token validation)\n *\n */\nexport interface OIDCDiscoveryDocument {\n\t/** REQUIRED. URL using the https scheme that the OP asserts as its Issuer Identifier. */\n\tissuer: string;\n\n\t/** REQUIRED. URL of the OP's OAuth 2.0 Authorization Endpoint. */\n\tauthorization_endpoint: string;\n\n\t/**\n\t * REQUIRED (spec says \"unless only implicit flow is used\").\n\t * URL of the OP's OAuth 2.0 Token Endpoint.\n\t * We only support authorization code flow.\n\t */\n\ttoken_endpoint: string;\n\n\t/** REQUIRED. URL of the OP's JSON Web Key Set document for ID token validation. */\n\tjwks_uri: string;\n\n\t/** RECOMMENDED. URL of the OP's UserInfo Endpoint. */\n\tuserinfo_endpoint?: string;\n\n\t/**\n\t * OPTIONAL. JSON array containing a list of Client Authentication methods\n\t * supported by this Token Endpoint.\n\t * Default: [\"client_secret_basic\"]\n\t */\n\ttoken_endpoint_auth_methods_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 scope values that this server supports. */\n\tscopes_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 response_type values that this OP supports. */\n\tresponse_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the Subject Identifier types that this OP supports. */\n\tsubject_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the JWS signing algorithms supported by the OP. */\n\tid_token_signing_alg_values_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the claim names that the OP may supply values for. */\n\tclaims_supported?: string[];\n\n\t/** OPTIONAL. URL of a page containing human-readable information about the OP. */\n\tservice_documentation?: string;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the claims parameter. */\n\tclaims_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter. */\n\trequest_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter. */\n\trequest_uri_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP requires any request_uri values to be pre-registered. */\n\trequire_request_uri_registration?: boolean;\n\n\t/** OPTIONAL. URL of the OP's end session endpoint. */\n\tend_session_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's revocation endpoint. */\n\trevocation_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's introspection endpoint. */\n\tintrospection_endpoint?: string;\n\n\t/** OPTIONAL. JSON array of PKCE code challenge methods supported (e.g., \"S256\", \"plain\"). */\n\tcode_challenge_methods_supported?: string[];\n\n\t/** Allow additional fields from the discovery document */\n\t[key: string]: unknown;\n}\n\n/**\n * Error codes for OIDC discovery operations.\n */\nexport type DiscoveryErrorCode =\n\t/** Request to discovery endpoint timed out */\n\t| \"discovery_timeout\"\n\t/** Discovery endpoint returned 404 or similar */\n\t| \"discovery_not_found\"\n\t/** Discovery endpoint returned invalid JSON */\n\t| \"discovery_invalid_json\"\n\t/** Discovery URL is invalid or malformed */\n\t| \"discovery_invalid_url\"\n\t/** Discovery URL is not trusted by the trusted origins configuration */\n\t| \"discovery_untrusted_origin\"\n\t/** Discovery document issuer doesn't match configured issuer */\n\t| \"issuer_mismatch\"\n\t/** Discovery document is missing required fields */\n\t| \"discovery_incomplete\"\n\t/** IdP only advertises token auth methods that Better Auth doesn't currently support */\n\t| \"unsupported_token_auth_method\"\n\t/** Catch-all for unexpected errors */\n\t| \"discovery_unexpected_error\";\n\n/**\n * Custom error class for OIDC discovery failures.\n * Can be caught and mapped to APIError at the edge.\n */\nexport class DiscoveryError extends Error {\n\tpublic readonly code: DiscoveryErrorCode;\n\tpublic readonly details?: Record<string, unknown>;\n\n\tconstructor(\n\t\tcode: DiscoveryErrorCode,\n\t\tmessage: string,\n\t\tdetails?: Record<string, unknown>,\n\t\toptions?: { cause?: unknown },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"DiscoveryError\";\n\t\tthis.code = code;\n\t\tthis.details = details;\n\n\t\t// Maintains proper stack trace for where the error was thrown\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, DiscoveryError);\n\t\t}\n\t}\n}\n\n/**\n * Hydrated OIDC configuration after discovery.\n * This is the normalized shape that gets persisted to the database\n * or merged into provider config at runtime.\n *\n * Field names are camelCase to match Better Auth conventions.\n */\nexport interface HydratedOIDCConfig {\n\t/** The issuer URL (validated to match configured issuer) */\n\tissuer: string;\n\n\t/** The discovery endpoint URL */\n\tdiscoveryEndpoint: string;\n\n\t/** URL of the authorization endpoint */\n\tauthorizationEndpoint: string;\n\n\t/** URL of the token endpoint */\n\ttokenEndpoint: string;\n\n\t/** URL of the JWKS endpoint */\n\tjwksEndpoint: string;\n\n\t/** URL of the userinfo endpoint (optional) */\n\tuserInfoEndpoint?: string;\n\n\t/** Token endpoint authentication method */\n\ttokenEndpointAuthentication?: \"client_secret_basic\" | \"client_secret_post\";\n\n\t/** Scopes supported by the IdP */\n\tscopesSupported?: string[];\n}\n\n/**\n * Parameters for the discoverOIDCConfig function.\n */\nexport interface DiscoverOIDCConfigParams {\n\t/** The issuer URL to discover configuration from */\n\tissuer: string;\n\n\t/**\n\t * Optional existing configuration.\n\t * Values provided here will override discovered values.\n\t */\n\texistingConfig?: Partial<HydratedOIDCConfig>;\n\n\t/**\n\t * Optional custom discovery endpoint URL.\n\t * If not provided, defaults to <issuer>/.well-known/openid-configuration\n\t */\n\tdiscoveryEndpoint?: string;\n\n\t/**\n\t * Optional timeout in milliseconds for the discovery request.\n\t * @default 10000 (10 seconds)\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Trusted origin predicate. See \"trustedOrigins\" option\n\t * @param url the url to test\n\t * @returns {boolean} return true for urls that belong to a trusted origin and false otherwise\n\t */\n\tisTrustedOrigin: (url: string) => boolean;\n}\n\n/**\n * Required fields that must be present in a valid discovery document.\n */\nexport const REQUIRED_DISCOVERY_FIELDS = [\n\t\"issuer\",\n\t\"authorization_endpoint\",\n\t\"token_endpoint\",\n\t\"jwks_uri\",\n] as const;\n\nexport type RequiredDiscoveryField = (typeof REQUIRED_DISCOVERY_FIELDS)[number];\n","/**\n * OIDC Discovery Pipeline\n *\n * Implements OIDC discovery document fetching, validation, and hydration.\n * This module is used both at provider registration time (to persist validated config)\n * and at runtime (to hydrate legacy providers that are missing metadata).\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\nimport { betterFetch } from \"@better-fetch/fetch\";\nimport type {\n\tDiscoverOIDCConfigParams,\n\tHydratedOIDCConfig,\n\tOIDCDiscoveryDocument,\n} from \"./types\";\nimport { DiscoveryError, REQUIRED_DISCOVERY_FIELDS } from \"./types\";\n\n/** Default timeout for discovery requests (10 seconds) */\nconst DEFAULT_DISCOVERY_TIMEOUT = 10000;\n\n/**\n * Main entry point: Discover and hydrate OIDC configuration from an issuer.\n *\n * This function:\n * 1. Computes the discovery URL from the issuer\n * 2. Validates the discovery URL\n * 3. Fetches the discovery document\n * 4. Validates the discovery document (issuer match + required fields)\n * 5. Normalizes URLs\n * 6. Selects token endpoint auth method\n * 7. Merges with existing config (existing values take precedence)\n *\n * @param params - Discovery parameters\n * @param isTrustedOrigin - Origin verification tester function\n * @returns Hydrated OIDC configuration ready for persistence\n * @throws DiscoveryError on any failure\n */\nexport async function discoverOIDCConfig(\n\tparams: DiscoverOIDCConfigParams,\n): Promise<HydratedOIDCConfig> {\n\tconst {\n\t\tissuer,\n\t\texistingConfig,\n\t\ttimeout = DEFAULT_DISCOVERY_TIMEOUT,\n\t} = params;\n\n\tconst discoveryUrl =\n\t\tparams.discoveryEndpoint ||\n\t\texistingConfig?.discoveryEndpoint ||\n\t\tcomputeDiscoveryUrl(issuer);\n\n\tvalidateDiscoveryUrl(discoveryUrl, params.isTrustedOrigin);\n\n\tconst discoveryDoc = await fetchDiscoveryDocument(discoveryUrl, timeout);\n\n\tvalidateDiscoveryDocument(discoveryDoc, issuer);\n\n\tconst normalizedDoc = normalizeDiscoveryUrls(\n\t\tdiscoveryDoc,\n\t\tissuer,\n\t\tparams.isTrustedOrigin,\n\t);\n\n\tconst tokenEndpointAuth = selectTokenEndpointAuthMethod(\n\t\tnormalizedDoc,\n\t\texistingConfig?.tokenEndpointAuthentication,\n\t);\n\n\tconst hydratedConfig: HydratedOIDCConfig = {\n\t\tissuer: existingConfig?.issuer ?? normalizedDoc.issuer,\n\t\tdiscoveryEndpoint: existingConfig?.discoveryEndpoint ?? discoveryUrl,\n\t\tauthorizationEndpoint:\n\t\t\texistingConfig?.authorizationEndpoint ??\n\t\t\tnormalizedDoc.authorization_endpoint,\n\t\ttokenEndpoint:\n\t\t\texistingConfig?.tokenEndpoint ?? normalizedDoc.token_endpoint,\n\t\tjwksEndpoint: existingConfig?.jwksEndpoint ?? normalizedDoc.jwks_uri,\n\t\tuserInfoEndpoint:\n\t\t\texistingConfig?.userInfoEndpoint ?? normalizedDoc.userinfo_endpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\texistingConfig?.tokenEndpointAuthentication ?? tokenEndpointAuth,\n\t\tscopesSupported:\n\t\t\texistingConfig?.scopesSupported ?? normalizedDoc.scopes_supported,\n\t};\n\n\treturn hydratedConfig;\n}\n\n/**\n * Compute the discovery URL from an issuer URL.\n *\n * Per OIDC Discovery spec, the discovery document is located at:\n * <issuer>/.well-known/openid-configuration\n *\n * Handles trailing slashes correctly.\n */\nexport function computeDiscoveryUrl(issuer: string): string {\n\tconst baseUrl = issuer.endsWith(\"/\") ? issuer.slice(0, -1) : issuer;\n\treturn `${baseUrl}/.well-known/openid-configuration`;\n}\n\n/**\n * Validate a discovery URL before fetching.\n *\n * @param url - The discovery URL to validate\n * @param isTrustedOrigin - Origin verification tester function\n * @throws DiscoveryError if URL is invalid\n */\nexport function validateDiscoveryUrl(\n\turl: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): void {\n\tconst discoveryEndpoint = parseURL(\"discoveryEndpoint\", url).toString();\n\n\tif (!isTrustedOrigin(discoveryEndpoint)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The main discovery endpoint \"${discoveryEndpoint}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ url: discoveryEndpoint },\n\t\t);\n\t}\n}\n\n/**\n * Fetch the OIDC discovery document from the IdP.\n *\n * @param url - The discovery endpoint URL\n * @param timeout - Request timeout in milliseconds\n * @returns The parsed discovery document\n * @throws DiscoveryError on network errors, timeouts, or invalid responses\n */\nexport async function fetchDiscoveryDocument(\n\turl: string,\n\ttimeout: number = DEFAULT_DISCOVERY_TIMEOUT,\n): Promise<OIDCDiscoveryDocument> {\n\ttry {\n\t\tconst response = await betterFetch<OIDCDiscoveryDocument>(url, {\n\t\t\tmethod: \"GET\",\n\t\t\ttimeout,\n\t\t});\n\n\t\tif (response.error) {\n\t\t\tconst { status } = response.error;\n\n\t\t\tif (status === 404) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_not_found\",\n\t\t\t\t\t\"Discovery endpoint not found\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (status === 408) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_unexpected_error\",\n\t\t\t\t`Unexpected discovery error: ${response.error.statusText}`,\n\t\t\t\t{ url, ...response.error },\n\t\t\t);\n\t\t}\n\n\t\tif (!response.data) {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned an empty response\",\n\t\t\t\t{ url },\n\t\t\t);\n\t\t}\n\n\t\tconst data = response.data as OIDCDiscoveryDocument | string;\n\t\tif (typeof data === \"string\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned invalid JSON\",\n\t\t\t\t{ url, bodyPreview: data.slice(0, 200) },\n\t\t\t);\n\t\t}\n\n\t\treturn data;\n\t} catch (error) {\n\t\tif (error instanceof DiscoveryError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// betterFetch throws AbortError on timeout (not returned as response.error)\n\t\t// Check error.name since message varies by runtime\n\t\tif (error instanceof Error && error.name === \"AbortError\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t{\n\t\t\t\t\turl,\n\t\t\t\t\ttimeout,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_unexpected_error\",\n\t\t\t`Unexpected error during discovery: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ url },\n\t\t\t{ cause: error },\n\t\t);\n\t}\n}\n\n/**\n * Validate a discovery document.\n *\n * Checks:\n * 1. All required fields are present\n * 2. Issuer matches the configured issuer (case-sensitive, exact match)\n *\n * Invariant: If this function returns without throwing, the document is safe\n * to use for hydrating OIDC config (required fields present, issuer matches\n * configured value, basic structural sanity verified).\n *\n * @param doc - The discovery document to validate\n * @param configuredIssuer - The expected issuer value\n * @throws DiscoveryError if validation fails\n */\nexport function validateDiscoveryDocument(\n\tdoc: OIDCDiscoveryDocument,\n\tconfiguredIssuer: string,\n): void {\n\tconst missingFields: string[] = [];\n\n\tfor (const field of REQUIRED_DISCOVERY_FIELDS) {\n\t\tif (!doc[field]) {\n\t\t\tmissingFields.push(field);\n\t\t}\n\t}\n\n\tif (missingFields.length > 0) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_incomplete\",\n\t\t\t`Discovery document is missing required fields: ${missingFields.join(\", \")}`,\n\t\t\t{ missingFields },\n\t\t);\n\t}\n\n\tconst discoveredIssuer = doc.issuer.endsWith(\"/\")\n\t\t? doc.issuer.slice(0, -1)\n\t\t: doc.issuer;\n\tconst expectedIssuer = configuredIssuer.endsWith(\"/\")\n\t\t? configuredIssuer.slice(0, -1)\n\t\t: configuredIssuer;\n\n\tif (discoveredIssuer !== expectedIssuer) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"issuer_mismatch\",\n\t\t\t`Discovered issuer \"${doc.issuer}\" does not match configured issuer \"${configuredIssuer}\"`,\n\t\t\t{\n\t\t\t\tdiscovered: doc.issuer,\n\t\t\t\tconfigured: configuredIssuer,\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Normalize URLs in the discovery document.\n *\n * @param document - The discovery document\n * @param issuer - The base issuer URL\n * @param isTrustedOrigin - Origin verification tester function\n * @returns The normalized discovery document\n */\nexport function normalizeDiscoveryUrls(\n\tdocument: OIDCDiscoveryDocument,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): OIDCDiscoveryDocument {\n\tconst doc = { ...document };\n\n\tdoc.token_endpoint = normalizeAndValidateUrl(\n\t\t\"token_endpoint\",\n\t\tdoc.token_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\tdoc.authorization_endpoint = normalizeAndValidateUrl(\n\t\t\"authorization_endpoint\",\n\t\tdoc.authorization_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tdoc.jwks_uri = normalizeAndValidateUrl(\n\t\t\"jwks_uri\",\n\t\tdoc.jwks_uri,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tif (doc.userinfo_endpoint) {\n\t\tdoc.userinfo_endpoint = normalizeAndValidateUrl(\n\t\t\t\"userinfo_endpoint\",\n\t\t\tdoc.userinfo_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.revocation_endpoint) {\n\t\tdoc.revocation_endpoint = normalizeAndValidateUrl(\n\t\t\t\"revocation_endpoint\",\n\t\t\tdoc.revocation_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.end_session_endpoint) {\n\t\tdoc.end_session_endpoint = normalizeAndValidateUrl(\n\t\t\t\"end_session_endpoint\",\n\t\t\tdoc.end_session_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.introspection_endpoint) {\n\t\tdoc.introspection_endpoint = normalizeAndValidateUrl(\n\t\t\t\"introspection_endpoint\",\n\t\t\tdoc.introspection_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\treturn doc;\n}\n\n/**\n * Normalizes and validates a single URL endpoint\n * @param name The url name\n * @param endpoint The url to validate\n * @param issuer The issuer base url\n * @param isTrustedOrigin - Origin verification tester function\n * @returns\n */\nfunction normalizeAndValidateUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): string {\n\tconst url = normalizeUrl(name, endpoint, issuer);\n\n\tif (!isTrustedOrigin(url)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The ${name} \"${url}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ endpoint: name, url },\n\t\t);\n\t}\n\n\treturn url;\n}\n\n/**\n * Normalize a single URL endpoint.\n *\n * @param name - The endpoint name (e.g token_endpoint)\n * @param endpoint - The endpoint URL to normalize\n * @param issuer - The base issuer URL\n * @returns The normalized endpoint URL\n */\nexport function normalizeUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n): string {\n\ttry {\n\t\treturn parseURL(name, endpoint).toString();\n\t} catch {\n\t\t// In case of error, endpoint maybe a relative url\n\t\t// So we try to resolve it relative to the issuer\n\n\t\tconst issuerURL = parseURL(name, issuer);\n\t\tconst basePath = issuerURL.pathname.replace(/\\/+$/, \"\");\n\t\tconst endpointPath = endpoint.replace(/^\\/+/, \"\");\n\n\t\treturn parseURL(\n\t\t\tname,\n\t\t\tbasePath + \"/\" + endpointPath,\n\t\t\tissuerURL.origin,\n\t\t).toString();\n\t}\n}\n\n/**\n * Parses the given URL or throws in case of invalid or unsupported protocols\n *\n * @param name the url name\n * @param endpoint the endpoint url\n * @param [base] optional base path\n * @returns\n */\nfunction parseURL(name: string, endpoint: string, base?: string) {\n\tlet endpointURL: URL | undefined;\n\n\ttry {\n\t\tendpointURL = new URL(endpoint, base);\n\t\tif (endpointURL.protocol === \"http:\" || endpointURL.protocol === \"https:\") {\n\t\t\treturn endpointURL;\n\t\t}\n\t} catch (error) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_invalid_url\",\n\t\t\t`The url \"${name}\" must be valid: ${endpoint}`,\n\t\t\t{\n\t\t\t\turl: endpoint,\n\t\t\t},\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\n\tthrow new DiscoveryError(\n\t\t\"discovery_invalid_url\",\n\t\t`The url \"${name}\" must use the http or https supported protocols: ${endpoint}`,\n\t\t{ url: endpoint, protocol: endpointURL.protocol },\n\t);\n}\n\n/**\n * Select the token endpoint authentication method.\n *\n * @param doc - The discovery document\n * @param existing - Existing authentication method from config\n * @returns The selected authentication method\n */\nexport function selectTokenEndpointAuthMethod(\n\tdoc: OIDCDiscoveryDocument,\n\texisting?: \"client_secret_basic\" | \"client_secret_post\",\n): \"client_secret_basic\" | \"client_secret_post\" {\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\tconst supported = doc.token_endpoint_auth_methods_supported;\n\n\tif (!supported || supported.length === 0) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_basic\")) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_post\")) {\n\t\treturn \"client_secret_post\";\n\t}\n\n\treturn \"client_secret_basic\";\n}\n\n/**\n * Check if a provider configuration needs runtime discovery.\n *\n * Returns true if we need discovery at runtime to complete the token exchange\n * and validation. Specifically checks for:\n * - `tokenEndpoint` - required for exchanging authorization code for tokens\n * - `jwksEndpoint` - required for validating ID token signatures\n *\n * Note: `authorizationEndpoint` is handled separately in the sign-in flow,\n * so it's not checked here.\n *\n * @param config - Partial OIDC config from the provider\n * @returns true if runtime discovery should be performed\n */\nexport function needsRuntimeDiscovery(\n\tconfig: Partial<HydratedOIDCConfig> | undefined,\n): boolean {\n\tif (!config) {\n\t\treturn true;\n\t}\n\n\treturn !config.tokenEndpoint || !config.jwksEndpoint;\n}\n","/**\n * OIDC Discovery Error Mapping\n *\n * Maps DiscoveryError codes to appropriate APIError responses.\n * Used at the boundary between the discovery pipeline and HTTP handlers.\n */\n\nimport { APIError } from \"better-auth/api\";\nimport type { DiscoveryError } from \"./types\";\n\n/**\n * Maps a DiscoveryError to an appropriate APIError for HTTP responses.\n *\n * Error code mapping:\n * - discovery_invalid_url → 400 BAD_REQUEST\n * - discovery_not_found → 400 BAD_REQUEST\n * - discovery_invalid_json → 400 BAD_REQUEST\n * - discovery_incomplete → 400 BAD_REQUEST\n * - issuer_mismatch → 400 BAD_REQUEST\n * - unsupported_token_auth_method → 400 BAD_REQUEST\n * - discovery_timeout → 502 BAD_GATEWAY\n * - discovery_unexpected_error → 502 BAD_GATEWAY\n *\n * @param error - The DiscoveryError to map\n * @returns An APIError with appropriate status and message\n */\nexport function mapDiscoveryErrorToAPIError(error: DiscoveryError): APIError {\n\tswitch (error.code) {\n\t\tcase \"discovery_timeout\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery timed out: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_unexpected_error\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery failed: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_not_found\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery endpoint not found. The issuer may not support OIDC discovery, or the URL is incorrect. ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_url\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Invalid OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_untrusted_origin\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Untrusted OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_json\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery returned invalid data: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_incomplete\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery document is missing required fields: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"issuer_mismatch\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC issuer mismatch: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"unsupported_token_auth_method\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Incompatible OIDC provider: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tdefault: {\n\t\t\t// Exhaustive check - TypeScript will error if we miss a case\n\t\t\tconst _exhaustiveCheck: never = error.code;\n\t\t\treturn new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\tmessage: `Unexpected discovery error: ${error.message}`,\n\t\t\t\tcode: \"discovery_unexpected_error\",\n\t\t\t});\n\t\t}\n\t}\n}\n","import { BetterFetchError, betterFetch } from \"@better-fetch/fetch\";\nimport type { Verification } from \"better-auth\";\nimport {\n\tcreateAuthorizationURL,\n\tgenerateState,\n\tHIDE_METADATA,\n\tparseState,\n\tvalidateAuthorizationCode,\n\tvalidateToken,\n} from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport { handleOAuthUserInfo } from \"better-auth/oauth2\";\nimport { decodeJwt } from \"jose\";\nimport z from \"zod/v4\";\nimport { getVerificationIdentifier } from \"./domain-verification\";\n\nimport { assignOrganizationFromProvider } from \"../linking\";\nimport type { HydratedOIDCConfig } from \"../oidc\";\nimport {\n\tDiscoveryError,\n\tdiscoverOIDCConfig,\n\tmapDiscoveryErrorToAPIError,\n} from \"../oidc\";\nimport type {\n\tOIDCConfig,\n\tSSOOptions,\n\tSSOProvider,\n} from \"../types\";\nimport { domainMatches, safeJsonParse, validateEmailDomain } from \"../utils\";\n\nconst ssoProviderBodySchema = z.object({\n\tproviderId: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The ID of the provider. This is used to identify the provider during login and callback\",\n\t}),\n\tissuer: z.string({}).meta({\n\t\tdescription: \"The issuer of the provider\",\n\t}),\n\tdomain: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The domain(s) of the provider. For enterprise multi-domain SSO where a single IdP serves multiple email domains, use comma-separated values (e.g., 'company.com,subsidiary.com,acquired-company.com')\",\n\t}),\n\toidcConfig: z\n\t\t.object({\n\t\t\tclientId: z.string({}).meta({\n\t\t\t\tdescription: \"The client ID\",\n\t\t\t}),\n\t\t\tclientSecret: z.string({}).meta({\n\t\t\t\tdescription: \"The client secret\",\n\t\t\t}),\n\t\t\tauthorizationEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The authorization endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The token endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tuserInfoEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The user info endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpointAuthentication: z\n\t\t\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t\t\t.optional(),\n\t\t\tjwksEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The JWKS endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tdiscoveryEndpoint: z.string().optional(),\n\t\t\tskipDiscovery: z\n\t\t\t\t.boolean()\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Skip OIDC discovery during registration. When true, you must provide authorizationEndpoint, tokenEndpoint, and jwksEndpoint manually.\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tscopes: z\n\t\t\t\t.array(z.string(), {})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tpkce: z\n\t\t\t\t.boolean({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"Whether to use PKCE for the authorization flow\",\n\t\t\t\t})\n\t\t\t\t.default(true)\n\t\t\t\t.optional(),\n\t\t\tmapping: z\n\t\t\t\t.object({\n\t\t\t\t\tid: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for user ID (defaults to 'sub')\",\n\t\t\t\t\t}),\n\t\t\t\t\temail: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for email (defaults to 'email')\",\n\t\t\t\t\t}),\n\t\t\t\t\temailVerified: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Field mapping for email verification (defaults to 'email_verified')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\tname: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for name (defaults to 'name')\",\n\t\t\t\t\t}),\n\t\t\t\t\timage: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription: \"Field mapping for image (defaults to 'picture')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t}),\n\torganizationId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If organization plugin is enabled, the organization id to link the provider to\",\n\t\t})\n\t\t.optional(),\n\toverrideUserInfo: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Override user info with the provider info. Defaults to false\",\n\t\t})\n\t\t.default(false)\n\t\t.optional(),\n});\n\nexport const registerSSOProvider = <O extends SSOOptions>(options: O) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/register\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: ssoProviderBodySchema,\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"registerSSOProvider\",\n\t\t\t\t\tsummary: \"Register an OIDC provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to register an OIDC provider. This is used to configure the provider and link it to an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"OIDC provider created successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst user = ctx.context.session?.user;\n\t\t\tif (!user) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t\t\t}\n\n\t\t\tconst limit =\n\t\t\t\ttypeof options?.providersLimit === \"function\"\n\t\t\t\t\t? await options.providersLimit(user)\n\t\t\t\t\t: (options?.providersLimit ?? 10);\n\n\t\t\tif (!limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"SSO provider registration is disabled\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst providers = await ctx.context.adapter.findMany({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"userId\", value: user.id }],\n\t\t\t});\n\n\t\t\tif (providers.length >= limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"You have reached the maximum number of SSO providers\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst body = ctx.body;\n\t\t\tconst issuerValidator = z.string().url();\n\t\t\tif (issuerValidator.safeParse(body.issuer).error) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid issuer. Must be a valid URL\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (ctx.body.organizationId && (ctx.context as any).hasPlugin?.(\"organization\")) {\n\t\t\t\tconst organization = await ctx.context.adapter.findOne({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\tvalue: ctx.body.organizationId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tif (!organization) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"You are not a member of the organization\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst existingProvider = await ctx.context.adapter.findOne({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\tvalue: body.providerId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (existingProvider) {\n\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t`SSO provider creation attempt with existing providerId: ${body.providerId}`,\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\tmessage: \"SSO provider with this providerId already exists\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet hydratedOIDCConfig: HydratedOIDCConfig | null = null;\n\t\t\tif (!body.oidcConfig.skipDiscovery) {\n\t\t\t\ttry {\n\t\t\t\t\thydratedOIDCConfig = await discoverOIDCConfig({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\texistingConfig: {\n\t\t\t\t\t\t\tdiscoveryEndpoint: body.oidcConfig.discoveryEndpoint,\n\t\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tisTrustedOrigin: (url: string) => ctx.context.isTrustedOrigin(url),\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof DiscoveryError) {\n\t\t\t\t\t\tthrow mapDiscoveryErrorToAPIError(error);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst buildOIDCConfig = () => {\n\t\t\t\tif (body.oidcConfig.skipDiscovery) {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication ||\n\t\t\t\t\t\t\t\"client_secret_basic\",\n\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\t\tdiscoveryEndpoint:\n\t\t\t\t\t\t\tbody.oidcConfig.discoveryEndpoint ||\n\t\t\t\t\t\t\t`${body.issuer}/.well-known/openid-configuration`,\n\t\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!hydratedOIDCConfig) return null;\n\n\t\t\t\treturn JSON.stringify({\n\t\t\t\t\tissuer: hydratedOIDCConfig.issuer,\n\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\tauthorizationEndpoint: hydratedOIDCConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: hydratedOIDCConfig.tokenEndpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\thydratedOIDCConfig.tokenEndpointAuthentication,\n\t\t\t\t\tjwksEndpoint: hydratedOIDCConfig.jwksEndpoint,\n\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\tdiscoveryEndpoint: hydratedOIDCConfig.discoveryEndpoint,\n\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\tuserInfoEndpoint: hydratedOIDCConfig.userInfoEndpoint,\n\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\tfalse,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst provider = await ctx.context.adapter.create<\n\t\t\t\tRecord<string, any>,\n\t\t\t\tSSOProvider<O>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\tdata: {\n\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\tdomain: body.domain,\n\t\t\t\t\tdomainVerified: false,\n\t\t\t\t\toidcConfig: buildOIDCConfig(),\n\t\t\t\t\torganizationId: body.organizationId,\n\t\t\t\t\tuserId: ctx.context.session.user.id,\n\t\t\t\t\tproviderId: body.providerId,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tlet domainVerificationToken: string | undefined;\n\t\t\tlet domainVerified: boolean | undefined;\n\n\t\t\tif (options?.domainVerification?.enabled) {\n\t\t\t\tdomainVerified = false;\n\t\t\t\tdomainVerificationToken = generateRandomString(24);\n\n\t\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tidentifier: getVerificationIdentifier(options, provider.providerId),\n\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tvalue: domainVerificationToken as string,\n\t\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttype SSOProviderResponse = {\n\t\t\t\tredirectURI: string;\n\t\t\t\toidcConfig: OIDCConfig | null;\n\t\t\t} & Omit<SSOProvider<O>, \"oidcConfig\">;\n\n\t\t\ttype SSOProviderReturn = O[\"domainVerification\"] extends { enabled: true }\n\t\t\t\t? SSOProviderResponse & {\n\t\t\t\t\t\tdomainVerified: boolean;\n\t\t\t\t\t\tdomainVerificationToken: string;\n\t\t\t\t\t}\n\t\t\t\t: SSOProviderResponse;\n\n\t\t\tconst result = {\n\t\t\t\t...provider,\n\t\t\t\toidcConfig: safeJsonParse<OIDCConfig>(\n\t\t\t\t\tprovider.oidcConfig as unknown as string,\n\t\t\t\t),\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\t...(options?.domainVerification?.enabled ? { domainVerified } : {}),\n\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t? { domainVerificationToken }\n\t\t\t\t\t: {}),\n\t\t\t};\n\n\t\t\treturn ctx.json(result as SSOProviderReturn);\n\t\t},\n\t);\n};\n\nconst signInSSOBodySchema = z.object({\n\temail: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided\",\n\t\t})\n\t\t.optional(),\n\torganizationSlug: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The slug of the organization to sign in with\",\n\t\t})\n\t\t.optional(),\n\tproviderId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The ID of the provider to sign in with. This can be provided instead of email or issuer\",\n\t\t})\n\t\t.optional(),\n\tdomain: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The domain of the provider.\",\n\t\t})\n\t\t.optional(),\n\tcallbackURL: z.string({}).meta({\n\t\tdescription: \"The URL to redirect to after login\",\n\t}),\n\terrorCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login\",\n\t\t})\n\t\t.optional(),\n\tnewUserCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login if the user is new\",\n\t\t})\n\t\t.optional(),\n\tscopes: z\n\t\t.array(z.string(), {})\n\t\t.meta({\n\t\t\tdescription: \"Scopes to request from the provider.\",\n\t\t})\n\t\t.optional(),\n\tloginHint: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'.\",\n\t\t})\n\t\t.optional(),\n\trequestSignUp: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const signInSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sign-in/sso\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: signInSSOBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"signInWithSSO\",\n\t\t\t\t\tsummary: \"Sign in with SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to sign in with an SSO provider. It redirects to the provider's authorization URL\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Authorization URL generated successfully for SSO sign-in\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tlet { email, organizationSlug, providerId, domain } = body;\n\t\t\tif (\n\t\t\t\t!options?.defaultSSO?.length &&\n\t\t\t\t!email &&\n\t\t\t\t!organizationSlug &&\n\t\t\t\t!domain &&\n\t\t\t\t!providerId\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"email, organizationSlug, domain or providerId is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tdomain = body.domain || email?.split(\"@\")[1];\n\t\t\tlet orgId = \"\";\n\t\t\tif (organizationSlug) {\n\t\t\t\torgId = await ctx.context.adapter\n\t\t\t\t\t.findOne<{ id: string }>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"slug\",\n\t\t\t\t\t\t\t\tvalue: organizationSlug,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn res.id;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\t// Find matching default SSO provider by providerId\n\t\t\t\tconst matchingDefault = providerId\n\t\t\t\t\t? options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.providerId === providerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t: options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.domain === domain,\n\t\t\t\t\t\t);\n\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\tissuer: matchingDefault.oidcConfig?.issuer || \"\",\n\t\t\t\t\t\tproviderId: matchingDefault.providerId,\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\toidcConfig: matchingDefault.oidcConfig,\n\t\t\t\t\t\tdomain: matchingDefault.domain,\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!providerId && !orgId && !domain) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"providerId, orgId or domain is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Try to find provider in database\n\t\t\tif (!provider) {\n\t\t\t\tconst parseProvider = (res: SSOProvider<SSOOptions> | null) => {\n\t\t\t\t\tif (!res) return null;\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...res,\n\t\t\t\t\t\toidcConfig: res.oidcConfig\n\t\t\t\t\t\t\t? safeJsonParse<OIDCConfig>(\n\t\t\t\t\t\t\t\t\tres.oidcConfig as unknown as string,\n\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\tif (providerId || orgId) {\n\t\t\t\t\t// Exact match for providerId or orgId\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: providerId ? \"providerId\" : \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: providerId || orgId!,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else if (domain) {\n\t\t\t\t\t// For domain lookup, support comma-separated domains\n\t\t\t\t\t// First try exact match (fast path)\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [{ field: \"domain\", value: domain }],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\t// If not found, search all providers for comma-separated domain match\n\t\t\t\t\tif (!provider) {\n\t\t\t\t\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\t\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst matchingProvider = allProviders.find((p) =>\n\t\t\t\t\t\t\tdomainMatches(domain, p.domain),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprovider = parseProvider(matchingProvider ?? null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No provider found for the issuer\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!provider.oidcConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"OIDC provider is not configured\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet finalAuthUrl = provider.oidcConfig.authorizationEndpoint;\n\t\t\tif (!finalAuthUrl && provider.oidcConfig.discoveryEndpoint) {\n\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\tauthorization_endpoint: string;\n\t\t\t\t}>(provider.oidcConfig.discoveryEndpoint, {\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t});\n\t\t\t\tif (discovery.data) {\n\t\t\t\t\tfinalAuthUrl = discovery.data.authorization_endpoint;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!finalAuthUrl) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid OIDC configuration. Authorization URL not found.\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst state = await generateState(ctx, undefined, false);\n\t\t\tconst redirectURI = `${ctx.context.baseURL}/sso/callback/${provider.providerId}`;\n\t\t\tconst authorizationURL = await createAuthorizationURL({\n\t\t\t\tid: provider.issuer,\n\t\t\t\toptions: {\n\t\t\t\t\tclientId: provider.oidcConfig.clientId,\n\t\t\t\t\tclientSecret: provider.oidcConfig.clientSecret,\n\t\t\t\t},\n\t\t\t\tredirectURI,\n\t\t\t\tstate: state.state,\n\t\t\t\tcodeVerifier: provider.oidcConfig.pkce\n\t\t\t\t\t? state.codeVerifier\n\t\t\t\t\t: undefined,\n\t\t\t\tscopes: ctx.body.scopes ||\n\t\t\t\t\tprovider.oidcConfig.scopes || [\n\t\t\t\t\t\t\"openid\",\n\t\t\t\t\t\t\"email\",\n\t\t\t\t\t\t\"profile\",\n\t\t\t\t\t\t\"offline_access\",\n\t\t\t\t\t],\n\t\t\t\tloginHint: ctx.body.loginHint || email,\n\t\t\t\tauthorizationEndpoint: finalAuthUrl,\n\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\turl: authorizationURL.toString(),\n\t\t\t\tredirect: true,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst callbackSSOQuerySchema = z.object({\n\tcode: z.string().optional(),\n\tstate: z.string(),\n\terror: z.string().optional(),\n\terror_description: z.string().optional(),\n});\n\nexport const callbackSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/callback/:providerId\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: callbackSSOQuerySchema,\n\t\t\tallowedMediaTypes: [\n\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\"application/json\",\n\t\t\t],\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSSOCallback\",\n\t\t\t\t\tsummary: \"Callback URL for SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used as the callback URL for SSO providers. It handles the authorization code and exchanges it for an access token\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"302\": {\n\t\t\t\t\t\t\tdescription: \"Redirects to the callback URL\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { code, error, error_description } = ctx.query;\n\t\t\tconst stateData = await parseState(ctx);\n\t\t\tif (!stateData) {\n\t\t\t\tconst errorURL =\n\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t`${ctx.context.baseURL}/error`;\n\t\t\t\tthrow ctx.redirect(`${errorURL}?error=invalid_state`);\n\t\t\t}\n\t\t\tconst { callbackURL, errorURL, newUserURL, requestSignUp } = stateData;\n\t\t\tif (!code || error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=${error}&error_description=${error_description}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\tconst matchingDefault = options.defaultSSO.find(\n\t\t\t\t\t(defaultProvider) =>\n\t\t\t\t\t\tdefaultProvider.providerId === ctx.params.providerId,\n\t\t\t\t);\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\t...matchingDefault,\n\t\t\t\t\t\tissuer: matchingDefault.oidcConfig?.issuer || \"\",\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tprovider = await ctx.context.adapter\n\t\t\t\t\t.findOne<{\n\t\t\t\t\t\toidcConfig: string;\n\t\t\t\t\t}>({\n\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\t\t\tvalue: ctx.params.providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\toidcConfig:\n\t\t\t\t\t\t\t\tsafeJsonParse<OIDCConfig>(res.oidcConfig) || undefined,\n\t\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet config = provider.oidcConfig;\n\n\t\t\tif (!config) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst discovery = await betterFetch<{\n\t\t\t\ttoken_endpoint: string;\n\t\t\t\tuserinfo_endpoint: string;\n\t\t\t\ttoken_endpoint_auth_method:\n\t\t\t\t\t| \"client_secret_basic\"\n\t\t\t\t\t| \"client_secret_post\";\n\t\t\t}>(config.discoveryEndpoint);\n\n\t\t\tif (discovery.data) {\n\t\t\t\tconfig = {\n\t\t\t\t\ttokenEndpoint: discovery.data.token_endpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\tdiscovery.data.token_endpoint_auth_method,\n\t\t\t\t\tuserInfoEndpoint: discovery.data.userinfo_endpoint,\n\t\t\t\t\tscopes: [\"openid\", \"email\", \"profile\", \"offline_access\"],\n\t\t\t\t\t...config,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!config.tokenEndpoint) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=token_endpoint_not_found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst tokenResponse = await validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier: config.pkce ? stateData.codeVerifier : undefined,\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\toptions: {\n\t\t\t\t\tclientId: config.clientId,\n\t\t\t\t\tclientSecret: config.clientSecret,\n\t\t\t\t},\n\t\t\t\ttokenEndpoint: config.tokenEndpoint,\n\t\t\t\tauthentication:\n\t\t\t\t\tconfig.tokenEndpointAuthentication === \"client_secret_post\"\n\t\t\t\t\t\t? \"post\"\n\t\t\t\t\t\t: \"basic\",\n\t\t\t}).catch((e) => {\n\t\t\t\tif (e instanceof BetterFetchError) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=${e.message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\tif (!tokenResponse) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=token_response_not_found`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet userInfo: {\n\t\t\t\tid?: string;\n\t\t\t\temail?: string;\n\t\t\t\tname?: string;\n\t\t\t\timage?: string;\n\t\t\t\temailVerified?: boolean;\n\t\t\t\t[key: string]: any;\n\t\t\t} | null = null;\n\t\t\tif (tokenResponse.idToken) {\n\t\t\t\tconst idToken = decodeJwt(tokenResponse.idToken);\n\t\t\t\tif (!config.jwksEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=jwks_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst verified = await validateToken(\n\t\t\t\t\ttokenResponse.idToken,\n\t\t\t\t\tconfig.jwksEndpoint,\n\t\t\t\t\t{\n\t\t\t\t\t\taudience: config.clientId,\n\t\t\t\t\t\tissuer: provider.issuer,\n\t\t\t\t\t},\n\t\t\t\t).catch((e) => {\n\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\t\tif (!verified) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=token_not_verified`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst mapping = config.mapping || {};\n\t\t\t\tuserInfo = {\n\t\t\t\t\t...Object.fromEntries(\n\t\t\t\t\t\tObject.entries(mapping.extraFields || {}).map(([key, value]) => [\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tverified.payload[value],\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t\tid: idToken[mapping.id || \"sub\"],\n\t\t\t\t\temail: idToken[mapping.email || \"email\"],\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? idToken[mapping.emailVerified || \"email_verified\"]\n\t\t\t\t\t\t: false,\n\t\t\t\t\tname: idToken[mapping.name || \"name\"],\n\t\t\t\t\timage: idToken[mapping.image || \"picture\"],\n\t\t\t\t} as {\n\t\t\t\t\tid?: string;\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!userInfo) {\n\t\t\t\tif (!config.userInfoEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=user_info_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst userInfoResponse = await betterFetch<{\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\tid?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t}>(config.userInfoEndpoint, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer ${tokenResponse.accessToken}`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (userInfoResponse.error) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=${\n\t\t\t\t\t\t\tuserInfoResponse.error.message\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tuserInfo = userInfoResponse.data;\n\t\t\t}\n\n\t\t\tif (!userInfo.email || !userInfo.id) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=missing_user_info`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isTrustedProvider =\n\t\t\t\t\"domainVerified\" in provider &&\n\t\t\t\t(provider as { domainVerified?: boolean }).domainVerified === true &&\n\t\t\t\tvalidateEmailDomain(userInfo.email, provider.domain);\n\n\t\t\tconst linked = await handleOAuthUserInfo(ctx, {\n\t\t\t\tuserInfo: {\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\tname: userInfo.name || \"\",\n\t\t\t\t\tid: userInfo.id,\n\t\t\t\t\timage: userInfo.image,\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? userInfo.emailVerified || false\n\t\t\t\t\t\t: false,\n\t\t\t\t},\n\t\t\t\taccount: {\n\t\t\t\t\tidToken: tokenResponse.idToken,\n\t\t\t\t\taccessToken: tokenResponse.accessToken,\n\t\t\t\t\trefreshToken: tokenResponse.refreshToken,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,\n\t\t\t\t\trefreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,\n\t\t\t\t\tscope: tokenResponse.scopes?.join(\",\"),\n\t\t\t\t},\n\t\t\t\tcallbackURL,\n\t\t\t\tdisableSignUp: options?.disableImplicitSignUp && !requestSignUp,\n\t\t\t\toverrideUserInfo: config.overrideUserInfo,\n\t\t\t\tisTrustedProvider,\n\t\t\t});\n\t\t\tif (linked.error) {\n\t\t\t\tthrow ctx.redirect(`${errorURL || callbackURL}?error=${linked.error}`);\n\t\t\t}\n\t\t\tconst { session, user } = linked.data!;\n\n\t\t\tif (options?.provisionUser && linked.isRegister) {\n\t\t\t\tawait options.provisionUser({\n\t\t\t\t\tuser,\n\t\t\t\t\tuserInfo,\n\t\t\t\t\ttoken: tokenResponse,\n\t\t\t\t\tprovider,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait assignOrganizationFromProvider(ctx as any, {\n\t\t\t\tuser,\n\t\t\t\tprofile: {\n\t\t\t\t\tproviderType: \"oidc\",\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t\trawAttributes: userInfo,\n\t\t\t\t},\n\t\t\t\tprovider,\n\t\t\t\ttoken: tokenResponse,\n\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t});\n\n\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\tsession,\n\t\t\t\tuser,\n\t\t\t});\n\t\t\tlet toRedirectTo: string;\n\t\t\ttry {\n\t\t\t\tconst url = linked.isRegister ? newUserURL || callbackURL : callbackURL;\n\t\t\t\ttoRedirectTo = url.toString();\n\t\t\t} catch {\n\t\t\t\ttoRedirectTo = linked.isRegister\n\t\t\t\t\t? newUserURL || callbackURL\n\t\t\t\t\t: callbackURL;\n\t\t\t}\n\t\t\tthrow ctx.redirect(toRedirectTo);\n\t\t},\n\t);\n};\n","import type { BetterAuthPlugin } from \"better-auth\";\nimport { createAuthMiddleware } from \"better-auth/api\";\nimport { assignOrganizationByDomain } from \"./linking\";\nimport {\n\trequestDomainVerification,\n\tverifyDomain,\n} from \"./routes/domain-verification\";\nimport {\n\tdeleteSSOProvider,\n\tgetSSOProvider,\n\tlistSSOProviders,\n\tupdateSSOProvider,\n} from \"./routes/providers\";\nimport {\n\tcallbackSSO,\n\tregisterSSOProvider,\n\tsignInSSO,\n} from \"./routes/sso\";\n\nimport type { OIDCConfig, SSOOptions, SSOProvider } from \"./types\";\n\nexport type { OIDCConfig, SSOOptions, SSOProvider };\n\nexport {\n\tcomputeDiscoveryUrl,\n\ttype DiscoverOIDCConfigParams,\n\tDiscoveryError,\n\ttype DiscoveryErrorCode,\n\tdiscoverOIDCConfig,\n\tfetchDiscoveryDocument,\n\ttype HydratedOIDCConfig,\n\tneedsRuntimeDiscovery,\n\tnormalizeDiscoveryUrls,\n\tnormalizeUrl,\n\ttype OIDCDiscoveryDocument,\n\tREQUIRED_DISCOVERY_FIELDS,\n\ttype RequiredDiscoveryField,\n\tselectTokenEndpointAuthMethod,\n\tvalidateDiscoveryDocument,\n\tvalidateDiscoveryUrl,\n} from \"./oidc\";\n\ntype DomainVerificationEndpoints = {\n\trequestDomainVerification: ReturnType<typeof requestDomainVerification>;\n\tverifyDomain: ReturnType<typeof verifyDomain>;\n};\n\ntype OIDCSSOEndpoints<O extends SSOOptions> = {\n\tregisterSSOProvider: ReturnType<typeof registerSSOProvider<O>>;\n\tsignInSSO: ReturnType<typeof signInSSO>;\n\tcallbackSSO: ReturnType<typeof callbackSSO>;\n\tlistSSOProviders: ReturnType<typeof listSSOProviders>;\n\tgetSSOProvider: ReturnType<typeof getSSOProvider>;\n\tupdateSSOProvider: ReturnType<typeof updateSSOProvider>;\n\tdeleteSSOProvider: ReturnType<typeof deleteSSOProvider>;\n};\n\nexport type OIDCSSOPlugin<O extends SSOOptions> = {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O> &\n\t\t(O extends { domainVerification: { enabled: true } }\n\t\t\t? DomainVerificationEndpoints\n\t\t\t: {});\n};\n\nexport function oidcSso<\n\tO extends SSOOptions & {\n\t\tdomainVerification?: { enabled: true };\n\t},\n>(\n\toptions?: O | undefined,\n): {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O> & DomainVerificationEndpoints;\n\tschema: NonNullable<BetterAuthPlugin[\"schema\"]>;\n\toptions: O;\n};\nexport function oidcSso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O>;\n};\n\nexport function oidcSso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): BetterAuthPlugin {\n\tconst optionsWithStore = options as O;\n\n\tlet endpoints = {\n\t\tregisterSSOProvider: registerSSOProvider(optionsWithStore),\n\t\tsignInSSO: signInSSO(optionsWithStore),\n\t\tcallbackSSO: callbackSSO(optionsWithStore),\n\t\tlistSSOProviders: listSSOProviders(),\n\t\tgetSSOProvider: getSSOProvider(),\n\t\tupdateSSOProvider: updateSSOProvider(optionsWithStore),\n\t\tdeleteSSOProvider: deleteSSOProvider(),\n\t};\n\n\tif (options?.domainVerification?.enabled) {\n\t\tconst domainVerificationEndpoints = {\n\t\t\trequestDomainVerification: requestDomainVerification(optionsWithStore),\n\t\t\tverifyDomain: verifyDomain(optionsWithStore),\n\t\t};\n\n\t\tendpoints = {\n\t\t\t...endpoints,\n\t\t\t...domainVerificationEndpoints,\n\t\t};\n\t}\n\n\treturn {\n\t\tid: \"oidc-sso\",\n\t\tendpoints,\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn context.path?.startsWith(\"/callback/\") ?? false;\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst newSession = ctx.context.newSession;\n\t\t\t\t\t\tif (!newSession?.user) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait assignOrganizationByDomain(ctx, {\n\t\t\t\t\t\t\tuser: newSession.user,\n\t\t\t\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t\t\t\t\tdomainVerification: options?.domainVerification,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tschema: {\n\t\t\tssoProvider: {\n\t\t\t\tmodelName: options?.modelName ?? \"ssoProvider\",\n\t\t\t\tfields: {\n\t\t\t\t\tissuer: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.issuer ?? \"issuer\",\n\t\t\t\t\t},\n\t\t\t\t\toidcConfig: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.oidcConfig ?? \"oidcConfig\",\n\t\t\t\t\t},\n\t\t\t\t\tuserId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\treferences: {\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfieldName: options?.fields?.userId ?? \"userId\",\n\t\t\t\t\t},\n\t\t\t\t\tproviderId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tunique: true,\n\t\t\t\t\t\tfieldName: options?.fields?.providerId ?? \"providerId\",\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.organizationId ?? \"organizationId\",\n\t\t\t\t\t},\n\t\t\t\t\tdomain: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.domain ?? \"domain\",\n\t\t\t\t\t},\n\t\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t\t? { domainVerified: { type: \"boolean\", required: false } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\toptions: options as NoInfer<O>,\n\t} satisfies BetterAuthPlugin;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["z"],"sources":["../src/utils.ts","../src/linking/org-assignment.ts","../src/routes/domain-verification.ts","../src/routes/schemas.ts","../src/routes/providers.ts","../src/oidc/types.ts","../src/oidc/discovery.ts","../src/oidc/errors.ts","../src/routes/sso.ts","../src/index.ts"],"sourcesContent":["/**\n * Safely parses a value that might be a JSON string or already a parsed object.\n * This handles cases where ORMs like Drizzle might return already parsed objects\n * instead of JSON strings from TEXT/JSON columns.\n *\n * @param value - The value to parse (string, object, null, or undefined)\n * @returns The parsed object or null\n * @throws Error if string parsing fails\n */\nexport function safeJsonParse<T>(\n\tvalue: string | T | null | undefined,\n): T | null {\n\tif (!value) return null;\n\n\tif (typeof value === \"object\") {\n\t\treturn value as T;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse JSON: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Checks if a domain matches any domain in a comma-separated list.\n */\nexport const domainMatches = (searchDomain: string, domainList: string) => {\n\tconst search = searchDomain.toLowerCase();\n\tconst domains = domainList\n\t\t.split(\",\")\n\t\t.map((d) => d.trim().toLowerCase())\n\t\t.filter(Boolean);\n\treturn domains.some((d) => search === d || search.endsWith(`.${d}`));\n};\n\n/**\n * Validates email domain against allowed domain(s).\n * Supports comma-separated domains for multi-domain SSO.\n */\nexport const validateEmailDomain = (email: string, domain: string) => {\n\tconst emailDomain = email.split(\"@\")[1]?.toLowerCase();\n\tif (!emailDomain || !domain) {\n\t\treturn false;\n\t}\n\treturn domainMatches(emailDomain, domain);\n};\n\nexport function maskClientId(clientId: string): string {\n\tif (clientId.length <= 4) {\n\t\treturn \"****\";\n\t}\n\treturn `****${clientId.slice(-4)}`;\n}\n","import type { GenericEndpointContext, OAuth2Tokens, User } from \"better-auth\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\nimport { domainMatches } from \"../utils\";\nimport type { NormalizedSSOProfile } from \"./types\";\n\nexport interface OrganizationProvisioningOptions {\n\tdisabled?: boolean;\n\tdefaultRole?: string;\n\tgetRole?: (data: {\n\t\tuser: User & Record<string, any>;\n\t\tuserInfo: Record<string, any>;\n\t\ttoken?: OAuth2Tokens;\n\t\tprovider: SSOProvider<SSOOptions>;\n\t}) => Promise<string>;\n}\n\nexport interface AssignOrganizationFromProviderOptions {\n\tuser: User;\n\tprofile: NormalizedSSOProfile;\n\tprovider: SSOProvider<SSOOptions>;\n\ttoken?: OAuth2Tokens;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n}\n\n/**\n * Assigns a user to an organization based on the SSO provider's organizationId.\n * Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.\n */\nexport async function assignOrganizationFromProvider(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationFromProviderOptions,\n): Promise<void> {\n\tconst { user, profile, provider, token, provisioningOptions } = options;\n\n\tif (!provider.organizationId) {\n\t\treturn;\n\t}\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: profile.rawAttributes || {},\n\t\t\t\ttoken,\n\t\t\t\tprovider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: provider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n\nexport interface AssignOrganizationByDomainOptions {\n\tuser: User;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n\tdomainVerification?: {\n\t\tenabled?: boolean;\n\t};\n}\n\n/**\n * Assigns a user to an organization based on their email domain.\n * Looks up SSO providers that match the user's email domain and assigns\n * the user to the associated organization.\n *\n * This enables domain-based org assignment for non-SSO sign-in methods\n * (e.g., Google OAuth with @acme.com email gets added to Acme's org).\n */\nexport async function assignOrganizationByDomain(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationByDomainOptions,\n): Promise<void> {\n\tconst { user, provisioningOptions, domainVerification } = options;\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst domain = user.email.split(\"@\")[1];\n\tif (!domain) {\n\t\treturn;\n\t}\n\n\t// Support comma-separated domains for multi-domain SSO\n\t// First try exact match (fast path)\n\tconst whereClause: { field: string; value: string | boolean }[] = [\n\t\t{ field: \"domain\", value: domain },\n\t];\n\n\tif (domainVerification?.enabled) {\n\t\twhereClause.push({ field: \"domainVerified\", value: true });\n\t}\n\n\tlet ssoProvider = await ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: whereClause,\n\t});\n\n\t// If not found, search all providers for comma-separated domain match\n\tif (!ssoProvider) {\n\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\tSSOProvider<SSOOptions>\n\t\t>({\n\t\t\tmodel: \"ssoProvider\",\n\t\t\twhere: domainVerification?.enabled\n\t\t\t\t? [{ field: \"domainVerified\", value: true }]\n\t\t\t\t: [],\n\t\t});\n\t\tssoProvider =\n\t\t\tallProviders.find((p) => domainMatches(domain, p.domain)) ?? null;\n\t}\n\n\tif (!ssoProvider || !ssoProvider.organizationId) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: ssoProvider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: {},\n\t\t\t\tprovider: ssoProvider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: ssoProvider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n","import type { Verification } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport * as z from \"zod/v4\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\n\nconst DNS_LABEL_MAX_LENGTH = 63;\nconst DEFAULT_TOKEN_PREFIX = \"better-auth-token\";\n\nconst domainVerificationBodySchema = z.object({\n\tproviderId: z.string(),\n});\n\nexport function getVerificationIdentifier(\n\toptions: SSOOptions,\n\tproviderId: string,\n): string {\n\tconst tokenPrefix =\n\t\toptions.domainVerification?.tokenPrefix || DEFAULT_TOKEN_PREFIX;\n\treturn `_${tokenPrefix}-${providerId}`;\n}\n\n/**\n * DNS-over-HTTPS TXT record lookup using Cloudflare's resolver.\n * Replaces node:dns/promises for edge/serverless compatibility.\n */\nasync function resolveTxtDoH(hostname: string): Promise<string[]> {\n\tconst url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=TXT`;\n\tconst response = await fetch(url, {\n\t\theaders: { Accept: \"application/dns-json\" },\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`DoH request failed with status ${response.status}`);\n\t}\n\n\tconst data = (await response.json()) as {\n\t\tAnswer?: Array<{ type: number; data: string }>;\n\t};\n\n\tif (!data.Answer) {\n\t\treturn [];\n\t}\n\n\treturn data.Answer.filter((record) => record.type === 16).map((record) =>\n\t\t// TXT records come quoted from DoH JSON API — strip surrounding quotes\n\t\trecord.data.replace(/^\"|\"$/g, \"\"),\n\t);\n}\n\nexport const requestDomainVerification = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/request-domain-verification\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Request a domain verification\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Request a domain verification for the given SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription: \"Domain has already been verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"201\": {\n\t\t\t\t\t\t\tdescription: \"Domain submitted for verification\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst identifier = getVerificationIdentifier(\n\t\t\t\toptions,\n\t\t\t\tprovider.providerId,\n\t\t\t);\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: identifier,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (activeVerification) {\n\t\t\t\tctx.setStatus(201);\n\t\t\t\treturn ctx.json({ domainVerificationToken: activeVerification.value });\n\t\t\t}\n\n\t\t\tconst domainVerificationToken = generateRandomString(24);\n\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\tmodel: \"verification\",\n\t\t\t\tdata: {\n\t\t\t\t\tidentifier,\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tvalue: domainVerificationToken,\n\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(201);\n\t\t\treturn ctx.json({\n\t\t\t\tdomainVerificationToken,\n\t\t\t});\n\t\t},\n\t);\n};\n\nexport const verifyDomain = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/verify-domain\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify the provider domain ownership\",\n\t\t\t\t\tdescription: \"Verify the provider domain ownership via DNS records\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Domain has already been verified or no pending verification exists\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"502\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Unable to verify domain ownership due to upstream validator error\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"204\": {\n\t\t\t\t\t\t\tdescription: \"Domain ownership was verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst identifier = getVerificationIdentifier(\n\t\t\t\toptions,\n\t\t\t\tprovider.providerId,\n\t\t\t);\n\n\t\t\tif (identifier.length > DNS_LABEL_MAX_LENGTH) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `Verification identifier exceeds the DNS label limit of ${DNS_LABEL_MAX_LENGTH} characters`,\n\t\t\t\t\tcode: \"IDENTIFIER_TOO_LONG\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: identifier,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (!activeVerification) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No pending domain verification exists\",\n\t\t\t\t\tcode: \"NO_PENDING_VERIFICATION\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet records: string[] = [];\n\n\t\t\ttry {\n\t\t\t\tconst hostname = new URL(provider.domain).hostname;\n\t\t\t\trecords = await resolveTxtDoH(`${identifier}.${hostname}`);\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\"DNS resolution failure while validating domain ownership\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst record = records.find((record) =>\n\t\t\t\trecord.includes(\n\t\t\t\t\t`${activeVerification.identifier}=${activeVerification.value}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (!record) {\n\t\t\t\tthrow new APIError(\"BAD_GATEWAY\", {\n\t\t\t\t\tmessage: \"Unable to verify domain ownership. Try again later\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFICATION_FAILED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update<SSOProvider<SSOOptions>>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: provider.providerId }],\n\t\t\t\tupdate: {\n\t\t\t\t\tdomainVerified: true,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(204);\n\t\t\treturn;\n\t\t},\n\t);\n};\n","import z from \"zod/v4\";\n\nconst oidcMappingSchema = z\n\t.object({\n\t\tid: z.string().optional(),\n\t\temail: z.string().optional(),\n\t\temailVerified: z.string().optional(),\n\t\tname: z.string().optional(),\n\t\timage: z.string().optional(),\n\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t})\n\t.optional();\n\nexport const oidcConfigSchema = z.object({\n\tclientId: z.string().optional(),\n\tclientSecret: z.string().optional(),\n\tauthorizationEndpoint: z.string().url().optional(),\n\ttokenEndpoint: z.string().url().optional(),\n\tuserInfoEndpoint: z.string().url().optional(),\n\ttokenEndpointAuthentication: z\n\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t.optional(),\n\tjwksEndpoint: z.string().url().optional(),\n\tdiscoveryEndpoint: z.string().url().optional(),\n\tscopes: z.array(z.string()).optional(),\n\tpkce: z.boolean().optional(),\n\toverrideUserInfo: z.boolean().optional(),\n\tmapping: oidcMappingSchema,\n});\n\nexport const updateSSOProviderBodySchema = z.object({\n\tissuer: z.string().url().optional(),\n\tdomain: z.string().optional(),\n\toidcConfig: oidcConfigSchema.optional(),\n});\n","import type { AuthContext } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport z from \"zod/v4\";\nimport type { Member, OIDCConfig, SSOOptions } from \"../types\";\nimport { maskClientId, safeJsonParse } from \"../utils\";\nimport { updateSSOProviderBodySchema } from \"./schemas\";\n\ninterface SSOProviderRecord {\n\tid: string;\n\tproviderId: string;\n\tissuer: string;\n\tdomain: string;\n\torganizationId?: string | null;\n\tdomainVerified?: boolean;\n\tuserId: string;\n\toidcConfig?: string | null;\n}\n\nconst ADMIN_ROLES = [\"owner\", \"admin\"];\n\nasync function isOrgAdmin(\n\tctx: {\n\t\tcontext: {\n\t\t\tadapter: {\n\t\t\t\tfindOne: <T>(query: {\n\t\t\t\t\tmodel: string;\n\t\t\t\t\twhere: { field: string; value: string }[];\n\t\t\t\t}) => Promise<T | null>;\n\t\t\t};\n\t\t};\n\t},\n\tuserId: string,\n\torganizationId: string,\n): Promise<boolean> {\n\tconst member = await ctx.context.adapter.findOne<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationId },\n\t\t],\n\t});\n\tif (!member) return false;\n\tconst roles = member.role.split(\",\");\n\treturn roles.some((r) => ADMIN_ROLES.includes(r.trim()));\n}\n\nasync function batchCheckOrgAdmin(\n\tctx: {\n\t\tcontext: AuthContext;\n\t},\n\tuserId: string,\n\torganizationIds: string[],\n): Promise<Set<string>> {\n\tif (organizationIds.length === 0) {\n\t\treturn new Set();\n\t}\n\n\tconst members = await ctx.context.adapter.findMany<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationIds, operator: \"in\" },\n\t\t],\n\t});\n\n\tconst adminOrgIds = new Set<string>();\n\tfor (const member of members) {\n\t\tconst roles = member.role.split(\",\");\n\t\tif (roles.some((r: string) => ADMIN_ROLES.includes(r.trim()))) {\n\t\t\tadminOrgIds.add(member.organizationId);\n\t\t}\n\t}\n\n\treturn adminOrgIds;\n}\n\nfunction sanitizeProvider(\n\tprovider: {\n\t\tproviderId: string;\n\t\tissuer: string;\n\t\tdomain: string;\n\t\torganizationId?: string | null;\n\t\tdomainVerified?: boolean;\n\t\toidcConfig?: string | OIDCConfig | null;\n\t},\n) {\n\tlet oidcConfig: OIDCConfig | null = null;\n\n\ttry {\n\t\toidcConfig = safeJsonParse<OIDCConfig>(provider.oidcConfig as string);\n\t} catch {\n\t\toidcConfig = null;\n\t}\n\n\treturn {\n\t\tproviderId: provider.providerId,\n\t\ttype: \"oidc\" as const,\n\t\tissuer: provider.issuer,\n\t\tdomain: provider.domain,\n\t\torganizationId: provider.organizationId || null,\n\t\tdomainVerified: provider.domainVerified ?? false,\n\t\toidcConfig: oidcConfig\n\t\t\t? {\n\t\t\t\t\tdiscoveryEndpoint: oidcConfig.discoveryEndpoint,\n\t\t\t\t\tclientIdLastFour: maskClientId(oidcConfig.clientId),\n\t\t\t\t\tpkce: oidcConfig.pkce,\n\t\t\t\t\tauthorizationEndpoint: oidcConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: oidcConfig.tokenEndpoint,\n\t\t\t\t\tuserInfoEndpoint: oidcConfig.userInfoEndpoint,\n\t\t\t\t\tjwksEndpoint: oidcConfig.jwksEndpoint,\n\t\t\t\t\tscopes: oidcConfig.scopes,\n\t\t\t\t\ttokenEndpointAuthentication: oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n\nexport const listSSOProviders = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listSSOProviders\",\n\t\t\t\t\tsummary: \"List SSO providers\",\n\t\t\t\t\tdescription: \"Returns a list of SSO providers the user has access to\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"List of SSO providers\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst userId = ctx.context.session.user.id;\n\n\t\t\tconst allProviders =\n\t\t\t\tawait ctx.context.adapter.findMany<SSOProviderRecord>({\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t});\n\n\t\t\tconst userOwnedProviders = allProviders.filter(\n\t\t\t\t(p) => p.userId === userId && !p.organizationId,\n\t\t\t);\n\n\t\t\tconst orgProviders = allProviders.filter(\n\t\t\t\t(p) => p.organizationId !== null && p.organizationId !== undefined,\n\t\t\t);\n\n\t\t\tconst orgPluginEnabled = !!(ctx.context as any).hasPlugin?.(\"organization\");\n\n\t\t\tlet accessibleProviders: typeof userOwnedProviders = [\n\t\t\t\t...userOwnedProviders,\n\t\t\t];\n\n\t\t\tif (orgPluginEnabled && orgProviders.length > 0) {\n\t\t\t\tconst orgIds = [\n\t\t\t\t\t...new Set(\n\t\t\t\t\t\torgProviders\n\t\t\t\t\t\t\t.map((p) => p.organizationId)\n\t\t\t\t\t\t\t.filter((id): id is string => id !== null && id !== undefined),\n\t\t\t\t\t),\n\t\t\t\t];\n\n\t\t\t\tconst adminOrgIds = await batchCheckOrgAdmin(ctx, userId, orgIds);\n\n\t\t\t\tconst orgAccessibleProviders = orgProviders.filter(\n\t\t\t\t\t(provider) =>\n\t\t\t\t\t\tprovider.organizationId && adminOrgIds.has(provider.organizationId),\n\t\t\t\t);\n\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...orgAccessibleProviders,\n\t\t\t\t];\n\t\t\t} else if (!orgPluginEnabled) {\n\t\t\t\tconst userOwnedOrgProviders = orgProviders.filter(\n\t\t\t\t\t(p) => p.userId === userId,\n\t\t\t\t);\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...userOwnedOrgProviders,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tconst providers = accessibleProviders.map((p) =>\n\t\t\t\tsanitizeProvider(p),\n\t\t\t);\n\n\t\t\treturn ctx.json({ providers });\n\t\t},\n\t);\n};\n\nconst getSSOProviderQuerySchema = z.object({\n\tproviderId: z.string(),\n});\n\nasync function checkProviderAccess(\n\tctx: {\n\t\tcontext: AuthContext & {\n\t\t\tsession: { user: { id: string } };\n\t\t};\n\t},\n\tproviderId: string,\n) {\n\tconst userId = ctx.context.session.user.id;\n\n\tconst provider = await ctx.context.adapter.findOne<SSOProviderRecord>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t});\n\n\tif (!provider) {\n\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\tmessage: \"Provider not found\",\n\t\t});\n\t}\n\n\tlet hasAccess = false;\n\tif (provider.organizationId) {\n\t\tif ((ctx.context as any).hasPlugin?.(\"organization\")) {\n\t\t\thasAccess = await isOrgAdmin(ctx, userId, provider.organizationId);\n\t\t} else {\n\t\t\thasAccess = provider.userId === userId;\n\t\t}\n\t} else {\n\t\thasAccess = provider.userId === userId;\n\t}\n\n\tif (!hasAccess) {\n\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\tmessage: \"You don't have access to this provider\",\n\t\t});\n\t}\n\n\treturn provider;\n}\n\nexport const getSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/get-provider\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tquery: getSSOProviderQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"getSSOProvider\",\n\t\t\t\t\tsummary: \"Get SSO provider details\",\n\t\t\t\t\tdescription: \"Returns sanitized details for a specific SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider details\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.query;\n\n\t\t\tconst provider = await checkProviderAccess(ctx, providerId);\n\n\t\t\treturn ctx.json(sanitizeProvider(provider));\n\t\t},\n\t);\n};\n\nfunction parseAndValidateConfig<T>(\n\tconfigString: string | null | undefined,\n\tconfigType: \"OIDC\",\n): T {\n\tlet config: T | null = null;\n\ttry {\n\t\tconfig = safeJsonParse<T>(configString as string);\n\t} catch {\n\t\tconfig = null;\n\t}\n\tif (!config) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: `Cannot update ${configType} config for a provider that doesn't have ${configType} configured`,\n\t\t});\n\t}\n\treturn config;\n}\n\nfunction mergeOIDCConfig(\n\tcurrent: OIDCConfig,\n\tupdates: Partial<OIDCConfig>,\n\tissuer: string,\n): OIDCConfig {\n\treturn {\n\t\t...current,\n\t\t...updates,\n\t\tissuer,\n\t\tpkce: updates.pkce ?? current.pkce ?? true,\n\t\tclientId: updates.clientId ?? current.clientId,\n\t\tclientSecret: updates.clientSecret ?? current.clientSecret,\n\t\tdiscoveryEndpoint: updates.discoveryEndpoint ?? current.discoveryEndpoint,\n\t\tmapping: updates.mapping ?? current.mapping,\n\t\tscopes: updates.scopes ?? current.scopes,\n\t\tauthorizationEndpoint:\n\t\t\tupdates.authorizationEndpoint ?? current.authorizationEndpoint,\n\t\ttokenEndpoint: updates.tokenEndpoint ?? current.tokenEndpoint,\n\t\tuserInfoEndpoint: updates.userInfoEndpoint ?? current.userInfoEndpoint,\n\t\tjwksEndpoint: updates.jwksEndpoint ?? current.jwksEndpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\tupdates.tokenEndpointAuthentication ??\n\t\t\tcurrent.tokenEndpointAuthentication,\n\t};\n}\n\nexport const updateSSOProvider = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/update-provider\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: updateSSOProviderBodySchema.extend({\n\t\t\t\tproviderId: z.string(),\n\t\t\t}),\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"updateSSOProvider\",\n\t\t\t\t\tsummary: \"Update SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Partially update an SSO provider. Only provided fields are updated. If domain changes, domainVerified is reset to false.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider updated successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId, ...body } = ctx.body;\n\n\t\t\tconst { issuer, domain, oidcConfig } = body;\n\t\t\tif (!issuer && !domain && !oidcConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"No fields provided for update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst existingProvider = await checkProviderAccess(ctx, providerId);\n\n\t\t\tconst updateData: Partial<SSOProviderRecord> = {};\n\n\t\t\tif (body.issuer !== undefined) {\n\t\t\t\tupdateData.issuer = body.issuer;\n\t\t\t}\n\n\t\t\tif (body.domain !== undefined) {\n\t\t\t\tupdateData.domain = body.domain;\n\t\t\t\tif (body.domain !== existingProvider.domain) {\n\t\t\t\t\tupdateData.domainVerified = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (body.oidcConfig) {\n\t\t\t\tconst currentOidcConfig = parseAndValidateConfig<OIDCConfig>(\n\t\t\t\t\texistingProvider.oidcConfig,\n\t\t\t\t\t\"OIDC\",\n\t\t\t\t);\n\n\t\t\t\tconst updatedOidcConfig = mergeOIDCConfig(\n\t\t\t\t\tcurrentOidcConfig,\n\t\t\t\t\tbody.oidcConfig,\n\t\t\t\t\tupdateData.issuer ||\n\t\t\t\t\t\tcurrentOidcConfig.issuer ||\n\t\t\t\t\t\texistingProvider.issuer,\n\t\t\t\t);\n\n\t\t\t\tupdateData.oidcConfig = JSON.stringify(updatedOidcConfig);\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\tupdate: updateData,\n\t\t\t});\n\n\t\t\tconst fullProvider = await ctx.context.adapter.findOne<SSOProviderRecord>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!fullProvider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found after update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(sanitizeProvider(fullProvider));\n\t\t},\n\t);\n};\n\nexport const deleteSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/delete-provider\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: z.object({\n\t\t\t\tproviderId: z.string(),\n\t\t\t}),\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"deleteSSOProvider\",\n\t\t\t\t\tsummary: \"Delete SSO provider\",\n\t\t\t\t\tdescription: \"Deletes an SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider deleted successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.body;\n\n\t\t\tawait checkProviderAccess(ctx, providerId);\n\n\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t});\n\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","/**\n * OIDC Discovery Types\n *\n * Types for the OIDC discovery document and hydrated configuration.\n * Based on OpenID Connect Discovery 1.0 specification.\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\n/**\n * Raw OIDC Discovery Document as returned by the IdP's\n * .well-known/openid-configuration endpoint.\n *\n * Required fields for Better Auth's OIDC support:\n * - issuer\n * - authorization_endpoint\n * - token_endpoint\n * - jwks_uri (required for ID token validation)\n *\n */\nexport interface OIDCDiscoveryDocument {\n\t/** REQUIRED. URL using the https scheme that the OP asserts as its Issuer Identifier. */\n\tissuer: string;\n\n\t/** REQUIRED. URL of the OP's OAuth 2.0 Authorization Endpoint. */\n\tauthorization_endpoint: string;\n\n\t/**\n\t * REQUIRED (spec says \"unless only implicit flow is used\").\n\t * URL of the OP's OAuth 2.0 Token Endpoint.\n\t * We only support authorization code flow.\n\t */\n\ttoken_endpoint: string;\n\n\t/** REQUIRED. URL of the OP's JSON Web Key Set document for ID token validation. */\n\tjwks_uri: string;\n\n\t/** RECOMMENDED. URL of the OP's UserInfo Endpoint. */\n\tuserinfo_endpoint?: string;\n\n\t/**\n\t * OPTIONAL. JSON array containing a list of Client Authentication methods\n\t * supported by this Token Endpoint.\n\t * Default: [\"client_secret_basic\"]\n\t */\n\ttoken_endpoint_auth_methods_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 scope values that this server supports. */\n\tscopes_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 response_type values that this OP supports. */\n\tresponse_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the Subject Identifier types that this OP supports. */\n\tsubject_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the JWS signing algorithms supported by the OP. */\n\tid_token_signing_alg_values_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the claim names that the OP may supply values for. */\n\tclaims_supported?: string[];\n\n\t/** OPTIONAL. URL of a page containing human-readable information about the OP. */\n\tservice_documentation?: string;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the claims parameter. */\n\tclaims_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter. */\n\trequest_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter. */\n\trequest_uri_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP requires any request_uri values to be pre-registered. */\n\trequire_request_uri_registration?: boolean;\n\n\t/** OPTIONAL. URL of the OP's end session endpoint. */\n\tend_session_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's revocation endpoint. */\n\trevocation_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's introspection endpoint. */\n\tintrospection_endpoint?: string;\n\n\t/** OPTIONAL. JSON array of PKCE code challenge methods supported (e.g., \"S256\", \"plain\"). */\n\tcode_challenge_methods_supported?: string[];\n\n\t/** Allow additional fields from the discovery document */\n\t[key: string]: unknown;\n}\n\n/**\n * Error codes for OIDC discovery operations.\n */\nexport type DiscoveryErrorCode =\n\t/** Request to discovery endpoint timed out */\n\t| \"discovery_timeout\"\n\t/** Discovery endpoint returned 404 or similar */\n\t| \"discovery_not_found\"\n\t/** Discovery endpoint returned invalid JSON */\n\t| \"discovery_invalid_json\"\n\t/** Discovery URL is invalid or malformed */\n\t| \"discovery_invalid_url\"\n\t/** Discovery URL is not trusted by the trusted origins configuration */\n\t| \"discovery_untrusted_origin\"\n\t/** Discovery document issuer doesn't match configured issuer */\n\t| \"issuer_mismatch\"\n\t/** Discovery document is missing required fields */\n\t| \"discovery_incomplete\"\n\t/** IdP only advertises token auth methods that Better Auth doesn't currently support */\n\t| \"unsupported_token_auth_method\"\n\t/** Catch-all for unexpected errors */\n\t| \"discovery_unexpected_error\";\n\n/**\n * Custom error class for OIDC discovery failures.\n * Can be caught and mapped to APIError at the edge.\n */\nexport class DiscoveryError extends Error {\n\tpublic readonly code: DiscoveryErrorCode;\n\tpublic readonly details?: Record<string, unknown>;\n\n\tconstructor(\n\t\tcode: DiscoveryErrorCode,\n\t\tmessage: string,\n\t\tdetails?: Record<string, unknown>,\n\t\toptions?: { cause?: unknown },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"DiscoveryError\";\n\t\tthis.code = code;\n\t\tthis.details = details;\n\n\t\t// Maintains proper stack trace for where the error was thrown\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, DiscoveryError);\n\t\t}\n\t}\n}\n\n/**\n * Hydrated OIDC configuration after discovery.\n * This is the normalized shape that gets persisted to the database\n * or merged into provider config at runtime.\n *\n * Field names are camelCase to match Better Auth conventions.\n */\nexport interface HydratedOIDCConfig {\n\t/** The issuer URL (validated to match configured issuer) */\n\tissuer: string;\n\n\t/** The discovery endpoint URL */\n\tdiscoveryEndpoint: string;\n\n\t/** URL of the authorization endpoint */\n\tauthorizationEndpoint: string;\n\n\t/** URL of the token endpoint */\n\ttokenEndpoint: string;\n\n\t/** URL of the JWKS endpoint */\n\tjwksEndpoint: string;\n\n\t/** URL of the userinfo endpoint (optional) */\n\tuserInfoEndpoint?: string;\n\n\t/** Token endpoint authentication method */\n\ttokenEndpointAuthentication?: \"client_secret_basic\" | \"client_secret_post\";\n\n\t/** Scopes supported by the IdP */\n\tscopesSupported?: string[];\n}\n\n/**\n * Parameters for the discoverOIDCConfig function.\n */\nexport interface DiscoverOIDCConfigParams {\n\t/** The issuer URL to discover configuration from */\n\tissuer: string;\n\n\t/**\n\t * Optional existing configuration.\n\t * Values provided here will override discovered values.\n\t */\n\texistingConfig?: Partial<HydratedOIDCConfig>;\n\n\t/**\n\t * Optional custom discovery endpoint URL.\n\t * If not provided, defaults to <issuer>/.well-known/openid-configuration\n\t */\n\tdiscoveryEndpoint?: string;\n\n\t/**\n\t * Optional timeout in milliseconds for the discovery request.\n\t * @default 10000 (10 seconds)\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Trusted origin predicate. See \"trustedOrigins\" option\n\t * @param url the url to test\n\t * @returns {boolean} return true for urls that belong to a trusted origin and false otherwise\n\t */\n\tisTrustedOrigin: (url: string) => boolean;\n}\n\n/**\n * Required fields that must be present in a valid discovery document.\n */\nexport const REQUIRED_DISCOVERY_FIELDS = [\n\t\"issuer\",\n\t\"authorization_endpoint\",\n\t\"token_endpoint\",\n\t\"jwks_uri\",\n] as const;\n\nexport type RequiredDiscoveryField = (typeof REQUIRED_DISCOVERY_FIELDS)[number];\n","/**\n * OIDC Discovery Pipeline\n *\n * Implements OIDC discovery document fetching, validation, and hydration.\n * This module is used both at provider registration time (to persist validated config)\n * and at runtime (to hydrate legacy providers that are missing metadata).\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\nimport { betterFetch } from \"@better-fetch/fetch\";\nimport type {\n\tDiscoverOIDCConfigParams,\n\tHydratedOIDCConfig,\n\tOIDCDiscoveryDocument,\n} from \"./types\";\nimport { DiscoveryError, REQUIRED_DISCOVERY_FIELDS } from \"./types\";\n\n/** Default timeout for discovery requests (10 seconds) */\nconst DEFAULT_DISCOVERY_TIMEOUT = 10000;\n\n/**\n * Main entry point: Discover and hydrate OIDC configuration from an issuer.\n *\n * This function:\n * 1. Computes the discovery URL from the issuer\n * 2. Validates the discovery URL\n * 3. Fetches the discovery document\n * 4. Validates the discovery document (issuer match + required fields)\n * 5. Normalizes URLs\n * 6. Selects token endpoint auth method\n * 7. Merges with existing config (existing values take precedence)\n *\n * @param params - Discovery parameters\n * @param isTrustedOrigin - Origin verification tester function\n * @returns Hydrated OIDC configuration ready for persistence\n * @throws DiscoveryError on any failure\n */\nexport async function discoverOIDCConfig(\n\tparams: DiscoverOIDCConfigParams,\n): Promise<HydratedOIDCConfig> {\n\tconst {\n\t\tissuer,\n\t\texistingConfig,\n\t\ttimeout = DEFAULT_DISCOVERY_TIMEOUT,\n\t} = params;\n\n\tconst discoveryUrl =\n\t\tparams.discoveryEndpoint ||\n\t\texistingConfig?.discoveryEndpoint ||\n\t\tcomputeDiscoveryUrl(issuer);\n\n\tvalidateDiscoveryUrl(discoveryUrl, params.isTrustedOrigin);\n\n\tconst discoveryDoc = await fetchDiscoveryDocument(discoveryUrl, timeout);\n\n\tvalidateDiscoveryDocument(discoveryDoc, issuer);\n\n\tconst normalizedDoc = normalizeDiscoveryUrls(\n\t\tdiscoveryDoc,\n\t\tissuer,\n\t\tparams.isTrustedOrigin,\n\t);\n\n\tconst tokenEndpointAuth = selectTokenEndpointAuthMethod(\n\t\tnormalizedDoc,\n\t\texistingConfig?.tokenEndpointAuthentication,\n\t);\n\n\tconst hydratedConfig: HydratedOIDCConfig = {\n\t\tissuer: existingConfig?.issuer ?? normalizedDoc.issuer,\n\t\tdiscoveryEndpoint: existingConfig?.discoveryEndpoint ?? discoveryUrl,\n\t\tauthorizationEndpoint:\n\t\t\texistingConfig?.authorizationEndpoint ??\n\t\t\tnormalizedDoc.authorization_endpoint,\n\t\ttokenEndpoint:\n\t\t\texistingConfig?.tokenEndpoint ?? normalizedDoc.token_endpoint,\n\t\tjwksEndpoint: existingConfig?.jwksEndpoint ?? normalizedDoc.jwks_uri,\n\t\tuserInfoEndpoint:\n\t\t\texistingConfig?.userInfoEndpoint ?? normalizedDoc.userinfo_endpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\texistingConfig?.tokenEndpointAuthentication ?? tokenEndpointAuth,\n\t\tscopesSupported:\n\t\t\texistingConfig?.scopesSupported ?? normalizedDoc.scopes_supported,\n\t};\n\n\treturn hydratedConfig;\n}\n\n/**\n * Compute the discovery URL from an issuer URL.\n *\n * Per OIDC Discovery spec, the discovery document is located at:\n * <issuer>/.well-known/openid-configuration\n *\n * Handles trailing slashes correctly.\n */\nexport function computeDiscoveryUrl(issuer: string): string {\n\tconst baseUrl = issuer.endsWith(\"/\") ? issuer.slice(0, -1) : issuer;\n\treturn `${baseUrl}/.well-known/openid-configuration`;\n}\n\n/**\n * Validate a discovery URL before fetching.\n *\n * @param url - The discovery URL to validate\n * @param isTrustedOrigin - Origin verification tester function\n * @throws DiscoveryError if URL is invalid\n */\nexport function validateDiscoveryUrl(\n\turl: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): void {\n\tconst discoveryEndpoint = parseURL(\"discoveryEndpoint\", url).toString();\n\n\tif (!isTrustedOrigin(discoveryEndpoint)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The main discovery endpoint \"${discoveryEndpoint}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ url: discoveryEndpoint },\n\t\t);\n\t}\n}\n\n/**\n * Fetch the OIDC discovery document from the IdP.\n *\n * @param url - The discovery endpoint URL\n * @param timeout - Request timeout in milliseconds\n * @returns The parsed discovery document\n * @throws DiscoveryError on network errors, timeouts, or invalid responses\n */\nexport async function fetchDiscoveryDocument(\n\turl: string,\n\ttimeout: number = DEFAULT_DISCOVERY_TIMEOUT,\n): Promise<OIDCDiscoveryDocument> {\n\ttry {\n\t\tconst response = await betterFetch<OIDCDiscoveryDocument>(url, {\n\t\t\tmethod: \"GET\",\n\t\t\ttimeout,\n\t\t});\n\n\t\tif (response.error) {\n\t\t\tconst { status } = response.error;\n\n\t\t\tif (status === 404) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_not_found\",\n\t\t\t\t\t\"Discovery endpoint not found\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (status === 408) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_unexpected_error\",\n\t\t\t\t`Unexpected discovery error: ${response.error.statusText}`,\n\t\t\t\t{ url, ...response.error },\n\t\t\t);\n\t\t}\n\n\t\tif (!response.data) {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned an empty response\",\n\t\t\t\t{ url },\n\t\t\t);\n\t\t}\n\n\t\tconst data = response.data as OIDCDiscoveryDocument | string;\n\t\tif (typeof data === \"string\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned invalid JSON\",\n\t\t\t\t{ url, bodyPreview: data.slice(0, 200) },\n\t\t\t);\n\t\t}\n\n\t\treturn data;\n\t} catch (error) {\n\t\tif (error instanceof DiscoveryError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// betterFetch throws AbortError on timeout (not returned as response.error)\n\t\t// Check error.name since message varies by runtime\n\t\tif (error instanceof Error && error.name === \"AbortError\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t{\n\t\t\t\t\turl,\n\t\t\t\t\ttimeout,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_unexpected_error\",\n\t\t\t`Unexpected error during discovery: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ url },\n\t\t\t{ cause: error },\n\t\t);\n\t}\n}\n\n/**\n * Validate a discovery document.\n *\n * Checks:\n * 1. All required fields are present\n * 2. Issuer matches the configured issuer (case-sensitive, exact match)\n *\n * Invariant: If this function returns without throwing, the document is safe\n * to use for hydrating OIDC config (required fields present, issuer matches\n * configured value, basic structural sanity verified).\n *\n * @param doc - The discovery document to validate\n * @param configuredIssuer - The expected issuer value\n * @throws DiscoveryError if validation fails\n */\nexport function validateDiscoveryDocument(\n\tdoc: OIDCDiscoveryDocument,\n\tconfiguredIssuer: string,\n): void {\n\tconst missingFields: string[] = [];\n\n\tfor (const field of REQUIRED_DISCOVERY_FIELDS) {\n\t\tif (!doc[field]) {\n\t\t\tmissingFields.push(field);\n\t\t}\n\t}\n\n\tif (missingFields.length > 0) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_incomplete\",\n\t\t\t`Discovery document is missing required fields: ${missingFields.join(\", \")}`,\n\t\t\t{ missingFields },\n\t\t);\n\t}\n\n\tconst discoveredIssuer = doc.issuer.endsWith(\"/\")\n\t\t? doc.issuer.slice(0, -1)\n\t\t: doc.issuer;\n\tconst expectedIssuer = configuredIssuer.endsWith(\"/\")\n\t\t? configuredIssuer.slice(0, -1)\n\t\t: configuredIssuer;\n\n\tif (discoveredIssuer !== expectedIssuer) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"issuer_mismatch\",\n\t\t\t`Discovered issuer \"${doc.issuer}\" does not match configured issuer \"${configuredIssuer}\"`,\n\t\t\t{\n\t\t\t\tdiscovered: doc.issuer,\n\t\t\t\tconfigured: configuredIssuer,\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Normalize URLs in the discovery document.\n *\n * @param document - The discovery document\n * @param issuer - The base issuer URL\n * @param isTrustedOrigin - Origin verification tester function\n * @returns The normalized discovery document\n */\nexport function normalizeDiscoveryUrls(\n\tdocument: OIDCDiscoveryDocument,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): OIDCDiscoveryDocument {\n\tconst doc = { ...document };\n\n\tdoc.token_endpoint = normalizeAndValidateUrl(\n\t\t\"token_endpoint\",\n\t\tdoc.token_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\tdoc.authorization_endpoint = normalizeAndValidateUrl(\n\t\t\"authorization_endpoint\",\n\t\tdoc.authorization_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tdoc.jwks_uri = normalizeAndValidateUrl(\n\t\t\"jwks_uri\",\n\t\tdoc.jwks_uri,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tif (doc.userinfo_endpoint) {\n\t\tdoc.userinfo_endpoint = normalizeAndValidateUrl(\n\t\t\t\"userinfo_endpoint\",\n\t\t\tdoc.userinfo_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.revocation_endpoint) {\n\t\tdoc.revocation_endpoint = normalizeAndValidateUrl(\n\t\t\t\"revocation_endpoint\",\n\t\t\tdoc.revocation_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.end_session_endpoint) {\n\t\tdoc.end_session_endpoint = normalizeAndValidateUrl(\n\t\t\t\"end_session_endpoint\",\n\t\t\tdoc.end_session_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.introspection_endpoint) {\n\t\tdoc.introspection_endpoint = normalizeAndValidateUrl(\n\t\t\t\"introspection_endpoint\",\n\t\t\tdoc.introspection_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\treturn doc;\n}\n\n/**\n * Normalizes and validates a single URL endpoint\n * @param name The url name\n * @param endpoint The url to validate\n * @param issuer The issuer base url\n * @param isTrustedOrigin - Origin verification tester function\n * @returns\n */\nfunction normalizeAndValidateUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): string {\n\tconst url = normalizeUrl(name, endpoint, issuer);\n\n\tif (!isTrustedOrigin(url)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The ${name} \"${url}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ endpoint: name, url },\n\t\t);\n\t}\n\n\treturn url;\n}\n\n/**\n * Normalize a single URL endpoint.\n *\n * @param name - The endpoint name (e.g token_endpoint)\n * @param endpoint - The endpoint URL to normalize\n * @param issuer - The base issuer URL\n * @returns The normalized endpoint URL\n */\nexport function normalizeUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n): string {\n\ttry {\n\t\treturn parseURL(name, endpoint).toString();\n\t} catch {\n\t\t// In case of error, endpoint maybe a relative url\n\t\t// So we try to resolve it relative to the issuer\n\n\t\tconst issuerURL = parseURL(name, issuer);\n\t\tconst basePath = issuerURL.pathname.replace(/\\/+$/, \"\");\n\t\tconst endpointPath = endpoint.replace(/^\\/+/, \"\");\n\n\t\treturn parseURL(\n\t\t\tname,\n\t\t\tbasePath + \"/\" + endpointPath,\n\t\t\tissuerURL.origin,\n\t\t).toString();\n\t}\n}\n\n/**\n * Parses the given URL or throws in case of invalid or unsupported protocols\n *\n * @param name the url name\n * @param endpoint the endpoint url\n * @param [base] optional base path\n * @returns\n */\nfunction parseURL(name: string, endpoint: string, base?: string) {\n\tlet endpointURL: URL | undefined;\n\n\ttry {\n\t\tendpointURL = new URL(endpoint, base);\n\t\tif (endpointURL.protocol === \"http:\" || endpointURL.protocol === \"https:\") {\n\t\t\treturn endpointURL;\n\t\t}\n\t} catch (error) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_invalid_url\",\n\t\t\t`The url \"${name}\" must be valid: ${endpoint}`,\n\t\t\t{\n\t\t\t\turl: endpoint,\n\t\t\t},\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\n\tthrow new DiscoveryError(\n\t\t\"discovery_invalid_url\",\n\t\t`The url \"${name}\" must use the http or https supported protocols: ${endpoint}`,\n\t\t{ url: endpoint, protocol: endpointURL.protocol },\n\t);\n}\n\n/**\n * Select the token endpoint authentication method.\n *\n * @param doc - The discovery document\n * @param existing - Existing authentication method from config\n * @returns The selected authentication method\n */\nexport function selectTokenEndpointAuthMethod(\n\tdoc: OIDCDiscoveryDocument,\n\texisting?: \"client_secret_basic\" | \"client_secret_post\",\n): \"client_secret_basic\" | \"client_secret_post\" {\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\tconst supported = doc.token_endpoint_auth_methods_supported;\n\n\tif (!supported || supported.length === 0) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_basic\")) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_post\")) {\n\t\treturn \"client_secret_post\";\n\t}\n\n\treturn \"client_secret_basic\";\n}\n\n/**\n * Check if a provider configuration needs runtime discovery.\n *\n * Returns true if we need discovery at runtime to complete the token exchange\n * and validation. Specifically checks for:\n * - `tokenEndpoint` - required for exchanging authorization code for tokens\n * - `jwksEndpoint` - required for validating ID token signatures\n *\n * Note: `authorizationEndpoint` is handled separately in the sign-in flow,\n * so it's not checked here.\n *\n * @param config - Partial OIDC config from the provider\n * @returns true if runtime discovery should be performed\n */\nexport function needsRuntimeDiscovery(\n\tconfig: Partial<HydratedOIDCConfig> | undefined,\n): boolean {\n\tif (!config) {\n\t\treturn true;\n\t}\n\n\treturn !config.tokenEndpoint || !config.jwksEndpoint;\n}\n","/**\n * OIDC Discovery Error Mapping\n *\n * Maps DiscoveryError codes to appropriate APIError responses.\n * Used at the boundary between the discovery pipeline and HTTP handlers.\n */\n\nimport { APIError } from \"better-auth/api\";\nimport type { DiscoveryError } from \"./types\";\n\n/**\n * Maps a DiscoveryError to an appropriate APIError for HTTP responses.\n *\n * Error code mapping:\n * - discovery_invalid_url → 400 BAD_REQUEST\n * - discovery_not_found → 400 BAD_REQUEST\n * - discovery_invalid_json → 400 BAD_REQUEST\n * - discovery_incomplete → 400 BAD_REQUEST\n * - issuer_mismatch → 400 BAD_REQUEST\n * - unsupported_token_auth_method → 400 BAD_REQUEST\n * - discovery_timeout → 502 BAD_GATEWAY\n * - discovery_unexpected_error → 502 BAD_GATEWAY\n *\n * @param error - The DiscoveryError to map\n * @returns An APIError with appropriate status and message\n */\nexport function mapDiscoveryErrorToAPIError(error: DiscoveryError): APIError {\n\tswitch (error.code) {\n\t\tcase \"discovery_timeout\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery timed out: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_unexpected_error\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery failed: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_not_found\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery endpoint not found. The issuer may not support OIDC discovery, or the URL is incorrect. ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_url\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Invalid OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_untrusted_origin\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Untrusted OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_json\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery returned invalid data: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_incomplete\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery document is missing required fields: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"issuer_mismatch\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC issuer mismatch: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"unsupported_token_auth_method\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Incompatible OIDC provider: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tdefault: {\n\t\t\t// Exhaustive check - TypeScript will error if we miss a case\n\t\t\tconst _exhaustiveCheck: never = error.code;\n\t\t\treturn new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\tmessage: `Unexpected discovery error: ${error.message}`,\n\t\t\t\tcode: \"discovery_unexpected_error\",\n\t\t\t});\n\t\t}\n\t}\n}\n","import { BetterFetchError, betterFetch } from \"@better-fetch/fetch\";\nimport type { Verification } from \"better-auth\";\nimport {\n\tcreateAuthorizationURL,\n\tgenerateState,\n\tHIDE_METADATA,\n\tparseState,\n\tvalidateAuthorizationCode,\n\tvalidateToken,\n} from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport { handleOAuthUserInfo } from \"better-auth/oauth2\";\nimport { decodeJwt } from \"jose\";\nimport z from \"zod/v4\";\nimport { getVerificationIdentifier } from \"./domain-verification\";\n\nimport { assignOrganizationFromProvider } from \"../linking\";\nimport type { HydratedOIDCConfig } from \"../oidc\";\nimport {\n\tDiscoveryError,\n\tdiscoverOIDCConfig,\n\tmapDiscoveryErrorToAPIError,\n} from \"../oidc\";\nimport type {\n\tOIDCConfig,\n\tSSOOptions,\n\tSSOProvider,\n} from \"../types\";\nimport { domainMatches, safeJsonParse, validateEmailDomain } from \"../utils\";\n\nconst ssoProviderBodySchema = z.object({\n\tproviderId: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The ID of the provider. This is used to identify the provider during login and callback\",\n\t}),\n\tissuer: z.string({}).meta({\n\t\tdescription: \"The issuer of the provider\",\n\t}),\n\tdomain: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The domain(s) of the provider. For enterprise multi-domain SSO where a single IdP serves multiple email domains, use comma-separated values (e.g., 'company.com,subsidiary.com,acquired-company.com')\",\n\t}),\n\toidcConfig: z\n\t\t.object({\n\t\t\tclientId: z.string({}).meta({\n\t\t\t\tdescription: \"The client ID\",\n\t\t\t}),\n\t\t\tclientSecret: z.string({}).meta({\n\t\t\t\tdescription: \"The client secret\",\n\t\t\t}),\n\t\t\tauthorizationEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The authorization endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The token endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tuserInfoEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The user info endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpointAuthentication: z\n\t\t\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t\t\t.optional(),\n\t\t\tjwksEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The JWKS endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tdiscoveryEndpoint: z.string().optional(),\n\t\t\tskipDiscovery: z\n\t\t\t\t.boolean()\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Skip OIDC discovery during registration. When true, you must provide authorizationEndpoint, tokenEndpoint, and jwksEndpoint manually.\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tscopes: z\n\t\t\t\t.array(z.string(), {})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tpkce: z\n\t\t\t\t.boolean({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"Whether to use PKCE for the authorization flow\",\n\t\t\t\t})\n\t\t\t\t.default(true)\n\t\t\t\t.optional(),\n\t\t\tmapping: z\n\t\t\t\t.object({\n\t\t\t\t\tid: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for user ID (defaults to 'sub')\",\n\t\t\t\t\t}),\n\t\t\t\t\temail: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for email (defaults to 'email')\",\n\t\t\t\t\t}),\n\t\t\t\t\temailVerified: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Field mapping for email verification (defaults to 'email_verified')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\tname: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for name (defaults to 'name')\",\n\t\t\t\t\t}),\n\t\t\t\t\timage: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription: \"Field mapping for image (defaults to 'picture')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t}),\n\torganizationId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If organization plugin is enabled, the organization id to link the provider to\",\n\t\t})\n\t\t.optional(),\n\toverrideUserInfo: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Override user info with the provider info. Defaults to false\",\n\t\t})\n\t\t.default(false)\n\t\t.optional(),\n});\n\nexport const registerSSOProvider = <O extends SSOOptions>(options: O) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/register\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: ssoProviderBodySchema,\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"registerSSOProvider\",\n\t\t\t\t\tsummary: \"Register an OIDC provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to register an OIDC provider. This is used to configure the provider and link it to an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"OIDC provider created successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst user = ctx.context.session?.user;\n\t\t\tif (!user) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t\t\t}\n\n\t\t\tconst limit =\n\t\t\t\ttypeof options?.providersLimit === \"function\"\n\t\t\t\t\t? await options.providersLimit(user)\n\t\t\t\t\t: (options?.providersLimit ?? 10);\n\n\t\t\tif (!limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"SSO provider registration is disabled\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst providers = await ctx.context.adapter.findMany({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"userId\", value: user.id }],\n\t\t\t});\n\n\t\t\tif (providers.length >= limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"You have reached the maximum number of SSO providers\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst body = ctx.body;\n\t\t\tconst issuerValidator = z.string().url();\n\t\t\tif (issuerValidator.safeParse(body.issuer).error) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid issuer. Must be a valid URL\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (ctx.body.organizationId && (ctx.context as any).hasPlugin?.(\"organization\")) {\n\t\t\t\tconst organization = await ctx.context.adapter.findOne({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\tvalue: ctx.body.organizationId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tif (!organization) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"You are not a member of the organization\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst existingProvider = await ctx.context.adapter.findOne({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\tvalue: body.providerId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (existingProvider) {\n\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t`SSO provider creation attempt with existing providerId: ${body.providerId}`,\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\tmessage: \"SSO provider with this providerId already exists\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet hydratedOIDCConfig: HydratedOIDCConfig | null = null;\n\t\t\tif (!body.oidcConfig.skipDiscovery) {\n\t\t\t\ttry {\n\t\t\t\t\thydratedOIDCConfig = await discoverOIDCConfig({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\texistingConfig: {\n\t\t\t\t\t\t\tdiscoveryEndpoint: body.oidcConfig.discoveryEndpoint,\n\t\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tisTrustedOrigin: () => true,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof DiscoveryError) {\n\t\t\t\t\t\tthrow mapDiscoveryErrorToAPIError(error);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst buildOIDCConfig = () => {\n\t\t\t\tif (body.oidcConfig.skipDiscovery) {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication ||\n\t\t\t\t\t\t\t\"client_secret_basic\",\n\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\t\tdiscoveryEndpoint:\n\t\t\t\t\t\t\tbody.oidcConfig.discoveryEndpoint ||\n\t\t\t\t\t\t\t`${body.issuer}/.well-known/openid-configuration`,\n\t\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!hydratedOIDCConfig) return null;\n\n\t\t\t\treturn JSON.stringify({\n\t\t\t\t\tissuer: hydratedOIDCConfig.issuer,\n\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\tauthorizationEndpoint: hydratedOIDCConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: hydratedOIDCConfig.tokenEndpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\thydratedOIDCConfig.tokenEndpointAuthentication,\n\t\t\t\t\tjwksEndpoint: hydratedOIDCConfig.jwksEndpoint,\n\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\tdiscoveryEndpoint: hydratedOIDCConfig.discoveryEndpoint,\n\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\tuserInfoEndpoint: hydratedOIDCConfig.userInfoEndpoint,\n\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\tfalse,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst provider = await ctx.context.adapter.create<\n\t\t\t\tRecord<string, any>,\n\t\t\t\tSSOProvider<O>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\tdata: {\n\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\tdomain: body.domain,\n\t\t\t\t\tdomainVerified: false,\n\t\t\t\t\toidcConfig: buildOIDCConfig(),\n\t\t\t\t\torganizationId: body.organizationId,\n\t\t\t\t\tuserId: ctx.context.session.user.id,\n\t\t\t\t\tproviderId: body.providerId,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tlet domainVerificationToken: string | undefined;\n\t\t\tlet domainVerified: boolean | undefined;\n\n\t\t\tif (options?.domainVerification?.enabled) {\n\t\t\t\tdomainVerified = false;\n\t\t\t\tdomainVerificationToken = generateRandomString(24);\n\n\t\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tidentifier: getVerificationIdentifier(options, provider.providerId),\n\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tvalue: domainVerificationToken as string,\n\t\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttype SSOProviderResponse = {\n\t\t\t\tredirectURI: string;\n\t\t\t\toidcConfig: OIDCConfig | null;\n\t\t\t} & Omit<SSOProvider<O>, \"oidcConfig\">;\n\n\t\t\ttype SSOProviderReturn = O[\"domainVerification\"] extends { enabled: true }\n\t\t\t\t? SSOProviderResponse & {\n\t\t\t\t\t\tdomainVerified: boolean;\n\t\t\t\t\t\tdomainVerificationToken: string;\n\t\t\t\t\t}\n\t\t\t\t: SSOProviderResponse;\n\n\t\t\tconst result = {\n\t\t\t\t...provider,\n\t\t\t\toidcConfig: safeJsonParse<OIDCConfig>(\n\t\t\t\t\tprovider.oidcConfig as unknown as string,\n\t\t\t\t),\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\t...(options?.domainVerification?.enabled ? { domainVerified } : {}),\n\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t? { domainVerificationToken }\n\t\t\t\t\t: {}),\n\t\t\t};\n\n\t\t\treturn ctx.json(result as SSOProviderReturn);\n\t\t},\n\t);\n};\n\nconst signInSSOBodySchema = z.object({\n\temail: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided\",\n\t\t})\n\t\t.optional(),\n\torganizationSlug: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The slug of the organization to sign in with\",\n\t\t})\n\t\t.optional(),\n\tproviderId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The ID of the provider to sign in with. This can be provided instead of email or issuer\",\n\t\t})\n\t\t.optional(),\n\tdomain: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The domain of the provider.\",\n\t\t})\n\t\t.optional(),\n\tcallbackURL: z.string({}).meta({\n\t\tdescription: \"The URL to redirect to after login\",\n\t}),\n\terrorCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login\",\n\t\t})\n\t\t.optional(),\n\tnewUserCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login if the user is new\",\n\t\t})\n\t\t.optional(),\n\tscopes: z\n\t\t.array(z.string(), {})\n\t\t.meta({\n\t\t\tdescription: \"Scopes to request from the provider.\",\n\t\t})\n\t\t.optional(),\n\tloginHint: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'.\",\n\t\t})\n\t\t.optional(),\n\trequestSignUp: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const signInSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sign-in/sso\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: signInSSOBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"signInWithSSO\",\n\t\t\t\t\tsummary: \"Sign in with SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to sign in with an SSO provider. It redirects to the provider's authorization URL\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Authorization URL generated successfully for SSO sign-in\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tlet { email, organizationSlug, providerId, domain } = body;\n\t\t\tif (\n\t\t\t\t!options?.defaultSSO?.length &&\n\t\t\t\t!email &&\n\t\t\t\t!organizationSlug &&\n\t\t\t\t!domain &&\n\t\t\t\t!providerId\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"email, organizationSlug, domain or providerId is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tdomain = body.domain || email?.split(\"@\")[1];\n\t\t\tlet orgId = \"\";\n\t\t\tif (organizationSlug) {\n\t\t\t\torgId = await ctx.context.adapter\n\t\t\t\t\t.findOne<{ id: string }>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"slug\",\n\t\t\t\t\t\t\t\tvalue: organizationSlug,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn res.id;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\t// Find matching default SSO provider by providerId\n\t\t\t\tconst matchingDefault = providerId\n\t\t\t\t\t? options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.providerId === providerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t: options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.domain === domain,\n\t\t\t\t\t\t);\n\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\tissuer: matchingDefault.oidcConfig?.issuer || \"\",\n\t\t\t\t\t\tproviderId: matchingDefault.providerId,\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\toidcConfig: matchingDefault.oidcConfig,\n\t\t\t\t\t\tdomain: matchingDefault.domain,\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!providerId && !orgId && !domain) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"providerId, orgId or domain is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Try to find provider in database\n\t\t\tif (!provider) {\n\t\t\t\tconst parseProvider = (res: SSOProvider<SSOOptions> | null) => {\n\t\t\t\t\tif (!res) return null;\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...res,\n\t\t\t\t\t\toidcConfig: res.oidcConfig\n\t\t\t\t\t\t\t? safeJsonParse<OIDCConfig>(\n\t\t\t\t\t\t\t\t\tres.oidcConfig as unknown as string,\n\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\tif (providerId || orgId) {\n\t\t\t\t\t// Exact match for providerId or orgId\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: providerId ? \"providerId\" : \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: providerId || orgId!,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else if (domain) {\n\t\t\t\t\t// For domain lookup, support comma-separated domains\n\t\t\t\t\t// First try exact match (fast path)\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [{ field: \"domain\", value: domain }],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\t// If not found, search all providers for comma-separated domain match\n\t\t\t\t\tif (!provider) {\n\t\t\t\t\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\t\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst matchingProvider = allProviders.find((p) =>\n\t\t\t\t\t\t\tdomainMatches(domain, p.domain),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprovider = parseProvider(matchingProvider ?? null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No provider found for the issuer\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!provider.oidcConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"OIDC provider is not configured\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet finalAuthUrl = provider.oidcConfig.authorizationEndpoint;\n\t\t\tif (!finalAuthUrl && provider.oidcConfig.discoveryEndpoint) {\n\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\tauthorization_endpoint: string;\n\t\t\t\t}>(provider.oidcConfig.discoveryEndpoint, {\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t});\n\t\t\t\tif (discovery.data) {\n\t\t\t\t\tfinalAuthUrl = discovery.data.authorization_endpoint;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!finalAuthUrl) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid OIDC configuration. Authorization URL not found.\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst state = await generateState(ctx, undefined, false);\n\t\t\tconst redirectURI = `${ctx.context.baseURL}/sso/callback/${provider.providerId}`;\n\t\t\tconst authorizationURL = await createAuthorizationURL({\n\t\t\t\tid: provider.issuer,\n\t\t\t\toptions: {\n\t\t\t\t\tclientId: provider.oidcConfig.clientId,\n\t\t\t\t\tclientSecret: provider.oidcConfig.clientSecret,\n\t\t\t\t},\n\t\t\t\tredirectURI,\n\t\t\t\tstate: state.state,\n\t\t\t\tcodeVerifier: provider.oidcConfig.pkce\n\t\t\t\t\t? state.codeVerifier\n\t\t\t\t\t: undefined,\n\t\t\t\tscopes: ctx.body.scopes ||\n\t\t\t\t\tprovider.oidcConfig.scopes || [\n\t\t\t\t\t\t\"openid\",\n\t\t\t\t\t\t\"email\",\n\t\t\t\t\t\t\"profile\",\n\t\t\t\t\t\t\"offline_access\",\n\t\t\t\t\t],\n\t\t\t\tloginHint: ctx.body.loginHint || email,\n\t\t\t\tauthorizationEndpoint: finalAuthUrl,\n\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\turl: authorizationURL.toString(),\n\t\t\t\tredirect: true,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst callbackSSOQuerySchema = z.object({\n\tcode: z.string().optional(),\n\tstate: z.string(),\n\terror: z.string().optional(),\n\terror_description: z.string().optional(),\n});\n\nexport const callbackSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/callback/:providerId\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: callbackSSOQuerySchema,\n\t\t\tallowedMediaTypes: [\n\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\"application/json\",\n\t\t\t],\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSSOCallback\",\n\t\t\t\t\tsummary: \"Callback URL for SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used as the callback URL for SSO providers. It handles the authorization code and exchanges it for an access token\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"302\": {\n\t\t\t\t\t\t\tdescription: \"Redirects to the callback URL\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { code, error, error_description } = ctx.query;\n\t\t\tconst stateData = await parseState(ctx);\n\t\t\tif (!stateData) {\n\t\t\t\tconst errorURL =\n\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t`${ctx.context.baseURL}/error`;\n\t\t\t\tthrow ctx.redirect(`${errorURL}?error=invalid_state`);\n\t\t\t}\n\t\t\tconst { callbackURL, errorURL, newUserURL, requestSignUp } = stateData;\n\t\t\tif (!code || error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=${error}&error_description=${error_description}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\tconst matchingDefault = options.defaultSSO.find(\n\t\t\t\t\t(defaultProvider) =>\n\t\t\t\t\t\tdefaultProvider.providerId === ctx.params.providerId,\n\t\t\t\t);\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\t...matchingDefault,\n\t\t\t\t\t\tissuer: matchingDefault.oidcConfig?.issuer || \"\",\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tprovider = await ctx.context.adapter\n\t\t\t\t\t.findOne<{\n\t\t\t\t\t\toidcConfig: string;\n\t\t\t\t\t}>({\n\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\t\t\tvalue: ctx.params.providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\toidcConfig:\n\t\t\t\t\t\t\t\tsafeJsonParse<OIDCConfig>(res.oidcConfig) || undefined,\n\t\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet config = provider.oidcConfig;\n\n\t\t\tif (!config) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst discovery = await betterFetch<{\n\t\t\t\ttoken_endpoint: string;\n\t\t\t\tuserinfo_endpoint: string;\n\t\t\t\ttoken_endpoint_auth_method:\n\t\t\t\t\t| \"client_secret_basic\"\n\t\t\t\t\t| \"client_secret_post\";\n\t\t\t}>(config.discoveryEndpoint);\n\n\t\t\tif (discovery.data) {\n\t\t\t\tconfig = {\n\t\t\t\t\ttokenEndpoint: discovery.data.token_endpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\tdiscovery.data.token_endpoint_auth_method,\n\t\t\t\t\tuserInfoEndpoint: discovery.data.userinfo_endpoint,\n\t\t\t\t\tscopes: [\"openid\", \"email\", \"profile\", \"offline_access\"],\n\t\t\t\t\t...config,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!config.tokenEndpoint) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=token_endpoint_not_found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst tokenResponse = await validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier: config.pkce ? stateData.codeVerifier : undefined,\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\toptions: {\n\t\t\t\t\tclientId: config.clientId,\n\t\t\t\t\tclientSecret: config.clientSecret,\n\t\t\t\t},\n\t\t\t\ttokenEndpoint: config.tokenEndpoint,\n\t\t\t\tauthentication:\n\t\t\t\t\tconfig.tokenEndpointAuthentication === \"client_secret_post\"\n\t\t\t\t\t\t? \"post\"\n\t\t\t\t\t\t: \"basic\",\n\t\t\t}).catch((e) => {\n\t\t\t\tif (e instanceof BetterFetchError) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=${e.message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\tif (!tokenResponse) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=token_response_not_found`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet userInfo: {\n\t\t\t\tid?: string;\n\t\t\t\temail?: string;\n\t\t\t\tname?: string;\n\t\t\t\timage?: string;\n\t\t\t\temailVerified?: boolean;\n\t\t\t\t[key: string]: any;\n\t\t\t} | null = null;\n\t\t\tif (tokenResponse.idToken) {\n\t\t\t\tconst idToken = decodeJwt(tokenResponse.idToken);\n\t\t\t\tif (!config.jwksEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=jwks_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst verified = await validateToken(\n\t\t\t\t\ttokenResponse.idToken,\n\t\t\t\t\tconfig.jwksEndpoint,\n\t\t\t\t\t{\n\t\t\t\t\t\taudience: config.clientId,\n\t\t\t\t\t\tissuer: provider.issuer,\n\t\t\t\t\t},\n\t\t\t\t).catch((e) => {\n\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\t\tif (!verified) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=token_not_verified`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst mapping = config.mapping || {};\n\t\t\t\tuserInfo = {\n\t\t\t\t\t...Object.fromEntries(\n\t\t\t\t\t\tObject.entries(mapping.extraFields || {}).map(([key, value]) => [\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tverified.payload[value],\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t\tid: idToken[mapping.id || \"sub\"],\n\t\t\t\t\temail: idToken[mapping.email || \"email\"],\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? idToken[mapping.emailVerified || \"email_verified\"]\n\t\t\t\t\t\t: false,\n\t\t\t\t\tname: idToken[mapping.name || \"name\"],\n\t\t\t\t\timage: idToken[mapping.image || \"picture\"],\n\t\t\t\t} as {\n\t\t\t\t\tid?: string;\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!userInfo) {\n\t\t\t\tif (!config.userInfoEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=user_info_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst userInfoResponse = await betterFetch<{\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\tid?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t}>(config.userInfoEndpoint, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer ${tokenResponse.accessToken}`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (userInfoResponse.error) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=${\n\t\t\t\t\t\t\tuserInfoResponse.error.message\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tuserInfo = userInfoResponse.data;\n\t\t\t}\n\n\t\t\tif (!userInfo.email || !userInfo.id) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=invalid_provider&error_description=missing_user_info`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isTrustedProvider =\n\t\t\t\t\"domainVerified\" in provider &&\n\t\t\t\t(provider as { domainVerified?: boolean }).domainVerified === true &&\n\t\t\t\tvalidateEmailDomain(userInfo.email, provider.domain);\n\n\t\t\tconst linked = await handleOAuthUserInfo(ctx, {\n\t\t\t\tuserInfo: {\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\tname: userInfo.name || \"\",\n\t\t\t\t\tid: userInfo.id,\n\t\t\t\t\timage: userInfo.image,\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? userInfo.emailVerified || false\n\t\t\t\t\t\t: false,\n\t\t\t\t},\n\t\t\t\taccount: {\n\t\t\t\t\tidToken: tokenResponse.idToken,\n\t\t\t\t\taccessToken: tokenResponse.accessToken,\n\t\t\t\t\trefreshToken: tokenResponse.refreshToken,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,\n\t\t\t\t\trefreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,\n\t\t\t\t\tscope: tokenResponse.scopes?.join(\",\"),\n\t\t\t\t},\n\t\t\t\tcallbackURL,\n\t\t\t\tdisableSignUp: options?.disableImplicitSignUp && !requestSignUp,\n\t\t\t\toverrideUserInfo: config.overrideUserInfo,\n\t\t\t\tisTrustedProvider,\n\t\t\t});\n\t\t\tif (linked.error) {\n\t\t\t\tthrow ctx.redirect(`${errorURL || callbackURL}?error=${linked.error}`);\n\t\t\t}\n\t\t\tconst { session, user } = linked.data!;\n\n\t\t\tif (options?.provisionUser && linked.isRegister) {\n\t\t\t\tawait options.provisionUser({\n\t\t\t\t\tuser,\n\t\t\t\t\tuserInfo,\n\t\t\t\t\ttoken: tokenResponse,\n\t\t\t\t\tprovider,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait assignOrganizationFromProvider(ctx as any, {\n\t\t\t\tuser,\n\t\t\t\tprofile: {\n\t\t\t\t\tproviderType: \"oidc\",\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t\trawAttributes: userInfo,\n\t\t\t\t},\n\t\t\t\tprovider,\n\t\t\t\ttoken: tokenResponse,\n\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t});\n\n\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\tsession,\n\t\t\t\tuser,\n\t\t\t});\n\t\t\tlet toRedirectTo: string;\n\t\t\ttry {\n\t\t\t\tconst url = linked.isRegister ? newUserURL || callbackURL : callbackURL;\n\t\t\t\ttoRedirectTo = url.toString();\n\t\t\t} catch {\n\t\t\t\ttoRedirectTo = linked.isRegister\n\t\t\t\t\t? newUserURL || callbackURL\n\t\t\t\t\t: callbackURL;\n\t\t\t}\n\t\t\tthrow ctx.redirect(toRedirectTo);\n\t\t},\n\t);\n};\n","import type { BetterAuthPlugin } from \"better-auth\";\nimport { createAuthMiddleware } from \"better-auth/api\";\nimport { assignOrganizationByDomain } from \"./linking\";\nimport {\n\trequestDomainVerification,\n\tverifyDomain,\n} from \"./routes/domain-verification\";\nimport {\n\tdeleteSSOProvider,\n\tgetSSOProvider,\n\tlistSSOProviders,\n\tupdateSSOProvider,\n} from \"./routes/providers\";\nimport {\n\tcallbackSSO,\n\tregisterSSOProvider,\n\tsignInSSO,\n} from \"./routes/sso\";\n\nimport type { OIDCConfig, SSOOptions, SSOProvider } from \"./types\";\n\nexport type { OIDCConfig, SSOOptions, SSOProvider };\n\nexport {\n\tcomputeDiscoveryUrl,\n\ttype DiscoverOIDCConfigParams,\n\tDiscoveryError,\n\ttype DiscoveryErrorCode,\n\tdiscoverOIDCConfig,\n\tfetchDiscoveryDocument,\n\ttype HydratedOIDCConfig,\n\tneedsRuntimeDiscovery,\n\tnormalizeDiscoveryUrls,\n\tnormalizeUrl,\n\ttype OIDCDiscoveryDocument,\n\tREQUIRED_DISCOVERY_FIELDS,\n\ttype RequiredDiscoveryField,\n\tselectTokenEndpointAuthMethod,\n\tvalidateDiscoveryDocument,\n\tvalidateDiscoveryUrl,\n} from \"./oidc\";\n\ntype DomainVerificationEndpoints = {\n\trequestDomainVerification: ReturnType<typeof requestDomainVerification>;\n\tverifyDomain: ReturnType<typeof verifyDomain>;\n};\n\ntype OIDCSSOEndpoints<O extends SSOOptions> = {\n\tregisterSSOProvider: ReturnType<typeof registerSSOProvider<O>>;\n\tsignInSSO: ReturnType<typeof signInSSO>;\n\tcallbackSSO: ReturnType<typeof callbackSSO>;\n\tlistSSOProviders: ReturnType<typeof listSSOProviders>;\n\tgetSSOProvider: ReturnType<typeof getSSOProvider>;\n\tupdateSSOProvider: ReturnType<typeof updateSSOProvider>;\n\tdeleteSSOProvider: ReturnType<typeof deleteSSOProvider>;\n};\n\nexport type OIDCSSOPlugin<O extends SSOOptions> = {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O> &\n\t\t(O extends { domainVerification: { enabled: true } }\n\t\t\t? DomainVerificationEndpoints\n\t\t\t: {});\n};\n\nexport function oidcSso<\n\tO extends SSOOptions & {\n\t\tdomainVerification?: { enabled: true };\n\t},\n>(\n\toptions?: O | undefined,\n): {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O> & DomainVerificationEndpoints;\n\tschema: NonNullable<BetterAuthPlugin[\"schema\"]>;\n\toptions: O;\n};\nexport function oidcSso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): {\n\tid: \"oidc-sso\";\n\tendpoints: OIDCSSOEndpoints<O>;\n};\n\nexport function oidcSso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): BetterAuthPlugin {\n\tconst optionsWithStore = options as O;\n\n\tlet endpoints = {\n\t\tregisterSSOProvider: registerSSOProvider(optionsWithStore),\n\t\tsignInSSO: signInSSO(optionsWithStore),\n\t\tcallbackSSO: callbackSSO(optionsWithStore),\n\t\tlistSSOProviders: listSSOProviders(),\n\t\tgetSSOProvider: getSSOProvider(),\n\t\tupdateSSOProvider: updateSSOProvider(optionsWithStore),\n\t\tdeleteSSOProvider: deleteSSOProvider(),\n\t};\n\n\tif (options?.domainVerification?.enabled) {\n\t\tconst domainVerificationEndpoints = {\n\t\t\trequestDomainVerification: requestDomainVerification(optionsWithStore),\n\t\t\tverifyDomain: verifyDomain(optionsWithStore),\n\t\t};\n\n\t\tendpoints = {\n\t\t\t...endpoints,\n\t\t\t...domainVerificationEndpoints,\n\t\t};\n\t}\n\n\treturn {\n\t\tid: \"oidc-sso\",\n\t\tendpoints,\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn context.path?.startsWith(\"/callback/\") ?? false;\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst newSession = ctx.context.newSession;\n\t\t\t\t\t\tif (!newSession?.user) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait assignOrganizationByDomain(ctx, {\n\t\t\t\t\t\t\tuser: newSession.user,\n\t\t\t\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t\t\t\t\tdomainVerification: options?.domainVerification,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tschema: {\n\t\t\tssoProvider: {\n\t\t\t\tmodelName: options?.modelName ?? \"ssoProvider\",\n\t\t\t\tfields: {\n\t\t\t\t\tissuer: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.issuer ?? \"issuer\",\n\t\t\t\t\t},\n\t\t\t\t\toidcConfig: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.oidcConfig ?? \"oidcConfig\",\n\t\t\t\t\t},\n\t\t\t\t\tuserId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\treferences: {\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfieldName: options?.fields?.userId ?? \"userId\",\n\t\t\t\t\t},\n\t\t\t\t\tproviderId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tunique: true,\n\t\t\t\t\t\tfieldName: options?.fields?.providerId ?? \"providerId\",\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.organizationId ?? \"organizationId\",\n\t\t\t\t\t},\n\t\t\t\t\tdomain: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.domain ?? \"domain\",\n\t\t\t\t\t},\n\t\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t\t? { domainVerified: { type: \"boolean\", required: false } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\toptions: options as NoInfer<O>,\n\t} satisfies BetterAuthPlugin;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startino/better-auth-oidc",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",