@better-auth/sso 1.4.5 → 1.4.6-beta.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,16 +1,16 @@
1
1
 
2
- > @better-auth/sso@1.4.5 build /home/runner/work/better-auth/better-auth/packages/sso
2
+ > @better-auth/sso@1.4.6-beta.3 build /home/runner/work/better-auth/better-auth/packages/sso
3
3
  > tsdown
4
4
 
5
- ℹ tsdown v0.16.6 powered by rolldown v1.0.0-beta.51
6
- ℹ Using tsdown config: /home/runner/work/better-auth/better-auth/packages/sso/tsdown.config.ts
5
+ ℹ tsdown v0.17.0 powered by rolldown v1.0.0-beta.53
6
+ ℹ config file: /home/runner/work/better-auth/better-auth/packages/sso/tsdown.config.ts
7
7
  ℹ entry: src/index.ts, src/client.ts
8
8
  ℹ tsconfig: tsconfig.json
9
9
  ℹ Build start
10
- ℹ dist/index.mjs 58.49 kB │ gzip: 10.33 kB
10
+ ℹ dist/index.mjs 58.73 kB │ gzip: 10.41 kB
11
11
  ℹ dist/client.mjs  0.15 kB │ gzip: 0.14 kB
12
12
  ℹ dist/client.d.mts  0.49 kB │ gzip: 0.30 kB
13
13
  ℹ dist/index.d.mts  0.21 kB │ gzip: 0.15 kB
14
14
  ℹ dist/index-D-JmJR9N.d.mts 25.42 kB │ gzip: 3.95 kB
15
- ℹ 5 files, total: 84.77 kB
16
- ✔ Build complete in 11028ms
15
+ ℹ 5 files, total: 85.01 kB
16
+ ✔ Build complete in 10982ms
package/dist/index.mjs CHANGED
@@ -10,10 +10,11 @@ import { handleOAuthUserInfo } from "better-auth/oauth2";
10
10
  import { decodeJwt } from "jose";
11
11
 
12
12
  //#region src/routes/domain-verification.ts
13
+ const domainVerificationBodySchema = z.object({ providerId: z.string() });
13
14
  const requestDomainVerification = (options) => {
14
15
  return createAuthEndpoint("/sso/request-domain-verification", {
15
16
  method: "POST",
16
- body: z.object({ providerId: z.string() }),
17
+ body: domainVerificationBodySchema,
17
18
  metadata: { openapi: {
18
19
  summary: "Request a domain verification",
19
20
  description: "Request a domain verification for the given SSO provider",
@@ -90,7 +91,7 @@ const requestDomainVerification = (options) => {
90
91
  const verifyDomain = (options) => {
91
92
  return createAuthEndpoint("/sso/verify-domain", {
92
93
  method: "POST",
93
- body: z.object({ providerId: z.string() }),
94
+ body: domainVerificationBodySchema,
94
95
  metadata: { openapi: {
95
96
  summary: "Verify the provider domain ownership",
96
97
  description: "Verify the provider domain ownership via DNS records",
@@ -208,13 +209,14 @@ function safeJsonParse(value) {
208
209
  }
209
210
  return null;
210
211
  }
212
+ const spMetadataQuerySchema = z.object({
213
+ providerId: z.string(),
214
+ format: z.enum(["xml", "json"]).default("xml")
215
+ });
211
216
  const spMetadata = () => {
212
217
  return createAuthEndpoint("/sso/saml2/sp/metadata", {
213
218
  method: "GET",
214
- query: z.object({
215
- providerId: z.string(),
216
- format: z.enum(["xml", "json"]).default("xml")
217
- }),
219
+ query: spMetadataQuerySchema,
218
220
  metadata: { openapi: {
219
221
  operationId: "getSSOServiceProviderMetadata",
220
222
  summary: "Get Service Provider metadata",
@@ -244,82 +246,83 @@ const spMetadata = () => {
244
246
  return new Response(sp.getMetadata(), { headers: { "Content-Type": "application/xml" } });
245
247
  });
246
248
  };
249
+ const ssoProviderBodySchema = z.object({
250
+ providerId: z.string({}).meta({ description: "The ID of the provider. This is used to identify the provider during login and callback" }),
251
+ issuer: z.string({}).meta({ description: "The issuer of the provider" }),
252
+ domain: z.string({}).meta({ description: "The domain of the provider. This is used for email matching" }),
253
+ oidcConfig: z.object({
254
+ clientId: z.string({}).meta({ description: "The client ID" }),
255
+ clientSecret: z.string({}).meta({ description: "The client secret" }),
256
+ authorizationEndpoint: z.string({}).meta({ description: "The authorization endpoint" }).optional(),
257
+ tokenEndpoint: z.string({}).meta({ description: "The token endpoint" }).optional(),
258
+ userInfoEndpoint: z.string({}).meta({ description: "The user info endpoint" }).optional(),
259
+ tokenEndpointAuthentication: z.enum(["client_secret_post", "client_secret_basic"]).optional(),
260
+ jwksEndpoint: z.string({}).meta({ description: "The JWKS endpoint" }).optional(),
261
+ discoveryEndpoint: z.string().optional(),
262
+ scopes: z.array(z.string(), {}).meta({ description: "The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']" }).optional(),
263
+ pkce: z.boolean({}).meta({ description: "Whether to use PKCE for the authorization flow" }).default(true).optional(),
264
+ mapping: z.object({
265
+ id: z.string({}).meta({ description: "Field mapping for user ID (defaults to 'sub')" }),
266
+ email: z.string({}).meta({ description: "Field mapping for email (defaults to 'email')" }),
267
+ emailVerified: z.string({}).meta({ description: "Field mapping for email verification (defaults to 'email_verified')" }).optional(),
268
+ name: z.string({}).meta({ description: "Field mapping for name (defaults to 'name')" }),
269
+ image: z.string({}).meta({ description: "Field mapping for image (defaults to 'picture')" }).optional(),
270
+ extraFields: z.record(z.string(), z.any()).optional()
271
+ }).optional()
272
+ }).optional(),
273
+ samlConfig: z.object({
274
+ entryPoint: z.string({}).meta({ description: "The entry point of the provider" }),
275
+ cert: z.string({}).meta({ description: "The certificate of the provider" }),
276
+ callbackUrl: z.string({}).meta({ description: "The callback URL of the provider" }),
277
+ audience: z.string().optional(),
278
+ idpMetadata: z.object({
279
+ metadata: z.string().optional(),
280
+ entityID: z.string().optional(),
281
+ cert: z.string().optional(),
282
+ privateKey: z.string().optional(),
283
+ privateKeyPass: z.string().optional(),
284
+ isAssertionEncrypted: z.boolean().optional(),
285
+ encPrivateKey: z.string().optional(),
286
+ encPrivateKeyPass: z.string().optional(),
287
+ singleSignOnService: z.array(z.object({
288
+ Binding: z.string().meta({ description: "The binding type for the SSO service" }),
289
+ Location: z.string().meta({ description: "The URL for the SSO service" })
290
+ })).optional().meta({ description: "Single Sign-On service configuration" })
291
+ }).optional(),
292
+ spMetadata: z.object({
293
+ metadata: z.string().optional(),
294
+ entityID: z.string().optional(),
295
+ binding: z.string().optional(),
296
+ privateKey: z.string().optional(),
297
+ privateKeyPass: z.string().optional(),
298
+ isAssertionEncrypted: z.boolean().optional(),
299
+ encPrivateKey: z.string().optional(),
300
+ encPrivateKeyPass: z.string().optional()
301
+ }),
302
+ wantAssertionsSigned: z.boolean().optional(),
303
+ signatureAlgorithm: z.string().optional(),
304
+ digestAlgorithm: z.string().optional(),
305
+ identifierFormat: z.string().optional(),
306
+ privateKey: z.string().optional(),
307
+ decryptionPvk: z.string().optional(),
308
+ additionalParams: z.record(z.string(), z.any()).optional(),
309
+ mapping: z.object({
310
+ id: z.string({}).meta({ description: "Field mapping for user ID (defaults to 'nameID')" }),
311
+ email: z.string({}).meta({ description: "Field mapping for email (defaults to 'email')" }),
312
+ emailVerified: z.string({}).meta({ description: "Field mapping for email verification" }).optional(),
313
+ name: z.string({}).meta({ description: "Field mapping for name (defaults to 'displayName')" }),
314
+ firstName: z.string({}).meta({ description: "Field mapping for first name (defaults to 'givenName')" }).optional(),
315
+ lastName: z.string({}).meta({ description: "Field mapping for last name (defaults to 'surname')" }).optional(),
316
+ extraFields: z.record(z.string(), z.any()).optional()
317
+ }).optional()
318
+ }).optional(),
319
+ organizationId: z.string({}).meta({ description: "If organization plugin is enabled, the organization id to link the provider to" }).optional(),
320
+ overrideUserInfo: z.boolean({}).meta({ description: "Override user info with the provider info. Defaults to false" }).default(false).optional()
321
+ });
247
322
  const registerSSOProvider = (options) => {
248
323
  return createAuthEndpoint("/sso/register", {
249
324
  method: "POST",
250
- body: z.object({
251
- providerId: z.string({}).meta({ description: "The ID of the provider. This is used to identify the provider during login and callback" }),
252
- issuer: z.string({}).meta({ description: "The issuer of the provider" }),
253
- domain: z.string({}).meta({ description: "The domain of the provider. This is used for email matching" }),
254
- oidcConfig: z.object({
255
- clientId: z.string({}).meta({ description: "The client ID" }),
256
- clientSecret: z.string({}).meta({ description: "The client secret" }),
257
- authorizationEndpoint: z.string({}).meta({ description: "The authorization endpoint" }).optional(),
258
- tokenEndpoint: z.string({}).meta({ description: "The token endpoint" }).optional(),
259
- userInfoEndpoint: z.string({}).meta({ description: "The user info endpoint" }).optional(),
260
- tokenEndpointAuthentication: z.enum(["client_secret_post", "client_secret_basic"]).optional(),
261
- jwksEndpoint: z.string({}).meta({ description: "The JWKS endpoint" }).optional(),
262
- discoveryEndpoint: z.string().optional(),
263
- scopes: z.array(z.string(), {}).meta({ description: "The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']" }).optional(),
264
- pkce: z.boolean({}).meta({ description: "Whether to use PKCE for the authorization flow" }).default(true).optional(),
265
- mapping: z.object({
266
- id: z.string({}).meta({ description: "Field mapping for user ID (defaults to 'sub')" }),
267
- email: z.string({}).meta({ description: "Field mapping for email (defaults to 'email')" }),
268
- emailVerified: z.string({}).meta({ description: "Field mapping for email verification (defaults to 'email_verified')" }).optional(),
269
- name: z.string({}).meta({ description: "Field mapping for name (defaults to 'name')" }),
270
- image: z.string({}).meta({ description: "Field mapping for image (defaults to 'picture')" }).optional(),
271
- extraFields: z.record(z.string(), z.any()).optional()
272
- }).optional()
273
- }).optional(),
274
- samlConfig: z.object({
275
- entryPoint: z.string({}).meta({ description: "The entry point of the provider" }),
276
- cert: z.string({}).meta({ description: "The certificate of the provider" }),
277
- callbackUrl: z.string({}).meta({ description: "The callback URL of the provider" }),
278
- audience: z.string().optional(),
279
- idpMetadata: z.object({
280
- metadata: z.string().optional(),
281
- entityID: z.string().optional(),
282
- cert: z.string().optional(),
283
- privateKey: z.string().optional(),
284
- privateKeyPass: z.string().optional(),
285
- isAssertionEncrypted: z.boolean().optional(),
286
- encPrivateKey: z.string().optional(),
287
- encPrivateKeyPass: z.string().optional(),
288
- singleSignOnService: z.array(z.object({
289
- Binding: z.string().meta({ description: "The binding type for the SSO service" }),
290
- Location: z.string().meta({ description: "The URL for the SSO service" })
291
- })).optional().meta({ description: "Single Sign-On service configuration" })
292
- }).optional(),
293
- spMetadata: z.object({
294
- metadata: z.string().optional(),
295
- entityID: z.string().optional(),
296
- binding: z.string().optional(),
297
- privateKey: z.string().optional(),
298
- privateKeyPass: z.string().optional(),
299
- isAssertionEncrypted: z.boolean().optional(),
300
- encPrivateKey: z.string().optional(),
301
- encPrivateKeyPass: z.string().optional()
302
- }),
303
- wantAssertionsSigned: z.boolean().optional(),
304
- signatureAlgorithm: z.string().optional(),
305
- digestAlgorithm: z.string().optional(),
306
- identifierFormat: z.string().optional(),
307
- privateKey: z.string().optional(),
308
- decryptionPvk: z.string().optional(),
309
- additionalParams: z.record(z.string(), z.any()).optional(),
310
- mapping: z.object({
311
- id: z.string({}).meta({ description: "Field mapping for user ID (defaults to 'nameID')" }),
312
- email: z.string({}).meta({ description: "Field mapping for email (defaults to 'email')" }),
313
- emailVerified: z.string({}).meta({ description: "Field mapping for email verification" }).optional(),
314
- name: z.string({}).meta({ description: "Field mapping for name (defaults to 'displayName')" }),
315
- firstName: z.string({}).meta({ description: "Field mapping for first name (defaults to 'givenName')" }).optional(),
316
- lastName: z.string({}).meta({ description: "Field mapping for last name (defaults to 'surname')" }).optional(),
317
- extraFields: z.record(z.string(), z.any()).optional()
318
- }).optional()
319
- }).optional(),
320
- organizationId: z.string({}).meta({ description: "If organization plugin is enabled, the organization id to link the provider to" }).optional(),
321
- overrideUserInfo: z.boolean({}).meta({ description: "Override user info with the provider info. Defaults to false" }).default(false).optional()
322
- }),
325
+ body: ssoProviderBodySchema,
323
326
  use: [sessionMiddleware],
324
327
  metadata: { openapi: {
325
328
  operationId: "registerSSOProvider",
@@ -592,22 +595,23 @@ const registerSSOProvider = (options) => {
592
595
  });
593
596
  });
594
597
  };
598
+ const signInSSOBodySchema = z.object({
599
+ email: z.string({}).meta({ description: "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" }).optional(),
600
+ organizationSlug: z.string({}).meta({ description: "The slug of the organization to sign in with" }).optional(),
601
+ providerId: z.string({}).meta({ description: "The ID of the provider to sign in with. This can be provided instead of email or issuer" }).optional(),
602
+ domain: z.string({}).meta({ description: "The domain of the provider." }).optional(),
603
+ callbackURL: z.string({}).meta({ description: "The URL to redirect to after login" }),
604
+ errorCallbackURL: z.string({}).meta({ description: "The URL to redirect to after login" }).optional(),
605
+ newUserCallbackURL: z.string({}).meta({ description: "The URL to redirect to after login if the user is new" }).optional(),
606
+ scopes: z.array(z.string(), {}).meta({ description: "Scopes to request from the provider." }).optional(),
607
+ loginHint: z.string({}).meta({ description: "Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'." }).optional(),
608
+ requestSignUp: z.boolean({}).meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider" }).optional(),
609
+ providerType: z.enum(["oidc", "saml"]).optional()
610
+ });
595
611
  const signInSSO = (options) => {
596
612
  return createAuthEndpoint("/sign-in/sso", {
597
613
  method: "POST",
598
- body: z.object({
599
- email: z.string({}).meta({ description: "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" }).optional(),
600
- organizationSlug: z.string({}).meta({ description: "The slug of the organization to sign in with" }).optional(),
601
- providerId: z.string({}).meta({ description: "The ID of the provider to sign in with. This can be provided instead of email or issuer" }).optional(),
602
- domain: z.string({}).meta({ description: "The domain of the provider." }).optional(),
603
- callbackURL: z.string({}).meta({ description: "The URL to redirect to after login" }),
604
- errorCallbackURL: z.string({}).meta({ description: "The URL to redirect to after login" }).optional(),
605
- newUserCallbackURL: z.string({}).meta({ description: "The URL to redirect to after login if the user is new" }).optional(),
606
- scopes: z.array(z.string(), {}).meta({ description: "Scopes to request from the provider." }).optional(),
607
- loginHint: z.string({}).meta({ description: "Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'." }).optional(),
608
- requestSignUp: z.boolean({}).meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider" }).optional(),
609
- providerType: z.enum(["oidc", "saml"]).optional()
610
- }),
614
+ body: signInSSOBodySchema,
611
615
  metadata: { openapi: {
612
616
  operationId: "signInWithSSO",
613
617
  summary: "Sign in with SSO provider",
@@ -781,15 +785,16 @@ const signInSSO = (options) => {
781
785
  throw new APIError("BAD_REQUEST", { message: "Invalid SSO provider" });
782
786
  });
783
787
  };
788
+ const callbackSSOQuerySchema = z.object({
789
+ code: z.string().optional(),
790
+ state: z.string(),
791
+ error: z.string().optional(),
792
+ error_description: z.string().optional()
793
+ });
784
794
  const callbackSSO = (options) => {
785
795
  return createAuthEndpoint("/sso/callback/:providerId", {
786
796
  method: "GET",
787
- query: z.object({
788
- code: z.string().optional(),
789
- state: z.string(),
790
- error: z.string().optional(),
791
- error_description: z.string().optional()
792
- }),
797
+ query: callbackSSOQuerySchema,
793
798
  allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
794
799
  metadata: {
795
800
  isAction: false,
@@ -966,13 +971,14 @@ const callbackSSO = (options) => {
966
971
  throw ctx.redirect(toRedirectTo);
967
972
  });
968
973
  };
974
+ const callbackSSOSAMLBodySchema = z.object({
975
+ SAMLResponse: z.string(),
976
+ RelayState: z.string().optional()
977
+ });
969
978
  const callbackSSOSAML = (options) => {
970
979
  return createAuthEndpoint("/sso/saml2/callback/:providerId", {
971
980
  method: "POST",
972
- body: z.object({
973
- SAMLResponse: z.string(),
974
- RelayState: z.string().optional()
975
- }),
981
+ body: callbackSSOSAMLBodySchema,
976
982
  metadata: {
977
983
  isAction: false,
978
984
  allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
@@ -1186,14 +1192,16 @@ const callbackSSOSAML = (options) => {
1186
1192
  throw ctx.redirect(callbackUrl);
1187
1193
  });
1188
1194
  };
1195
+ const acsEndpointParamsSchema = z.object({ providerId: z.string().optional() });
1196
+ const acsEndpointBodySchema = z.object({
1197
+ SAMLResponse: z.string(),
1198
+ RelayState: z.string().optional()
1199
+ });
1189
1200
  const acsEndpoint = (options) => {
1190
1201
  return createAuthEndpoint("/sso/saml2/sp/acs/:providerId", {
1191
1202
  method: "POST",
1192
- params: z.object({ providerId: z.string().optional() }),
1193
- body: z.object({
1194
- SAMLResponse: z.string(),
1195
- RelayState: z.string().optional()
1196
- }),
1203
+ params: acsEndpointParamsSchema,
1204
+ body: acsEndpointBodySchema,
1197
1205
  metadata: {
1198
1206
  isAction: false,
1199
1207
  allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@better-auth/sso",
3
3
  "author": "Bereket Engida",
4
- "version": "1.4.5",
4
+ "version": "1.4.6-beta.3",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
7
7
  "homepage": "https://www.better-auth.com/docs/plugins/sso",
@@ -64,11 +64,11 @@
64
64
  "body-parser": "^2.2.1",
65
65
  "express": "^5.1.0",
66
66
  "oauth2-mock-server": "^8.2.0",
67
- "tsdown": "^0.16.0",
68
- "better-auth": "1.4.5"
67
+ "tsdown": "^0.17.0",
68
+ "better-auth": "1.4.6-beta.3"
69
69
  },
70
70
  "peerDependencies": {
71
- "better-auth": "1.4.5"
71
+ "better-auth": "1.4.6-beta.3"
72
72
  },
73
73
  "scripts": {
74
74
  "test": "vitest",
@@ -8,14 +8,16 @@ import { generateRandomString } from "better-auth/crypto";
8
8
  import * as z from "zod/v4";
9
9
  import type { SSOOptions, SSOProvider } from "../types";
10
10
 
11
+ const domainVerificationBodySchema = z.object({
12
+ providerId: z.string(),
13
+ });
14
+
11
15
  export const requestDomainVerification = (options: SSOOptions) => {
12
16
  return createAuthEndpoint(
13
17
  "/sso/request-domain-verification",
14
18
  {
15
19
  method: "POST",
16
- body: z.object({
17
- providerId: z.string(),
18
- }),
20
+ body: domainVerificationBodySchema,
19
21
  metadata: {
20
22
  openapi: {
21
23
  summary: "Request a domain verification",
@@ -127,9 +129,7 @@ export const verifyDomain = (options: SSOOptions) => {
127
129
  "/sso/verify-domain",
128
130
  {
129
131
  method: "POST",
130
- body: z.object({
131
- providerId: z.string(),
132
- }),
132
+ body: domainVerificationBodySchema,
133
133
  metadata: {
134
134
  openapi: {
135
135
  summary: "Verify the provider domain ownership",
package/src/routes/sso.ts CHANGED
@@ -52,15 +52,17 @@ function safeJsonParse<T>(value: string | T | null | undefined): T | null {
52
52
  return null;
53
53
  }
54
54
 
55
+ const spMetadataQuerySchema = z.object({
56
+ providerId: z.string(),
57
+ format: z.enum(["xml", "json"]).default("xml"),
58
+ });
59
+
55
60
  export const spMetadata = () => {
56
61
  return createAuthEndpoint(
57
62
  "/sso/saml2/sp/metadata",
58
63
  {
59
64
  method: "GET",
60
- query: z.object({
61
- providerId: z.string(),
62
- format: z.enum(["xml", "json"]).default("xml"),
63
- }),
65
+ query: spMetadataQuerySchema,
64
66
  metadata: {
65
67
  openapi: {
66
68
  operationId: "getSSOServiceProviderMetadata",
@@ -128,213 +130,211 @@ export const spMetadata = () => {
128
130
  );
129
131
  };
130
132
 
131
- export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
132
- return createAuthEndpoint(
133
- "/sso/register",
134
- {
135
- method: "POST",
136
- body: z.object({
137
- providerId: z.string({}).meta({
138
- description:
139
- "The ID of the provider. This is used to identify the provider during login and callback",
140
- }),
141
- issuer: z.string({}).meta({
142
- description: "The issuer of the provider",
143
- }),
144
- domain: z.string({}).meta({
133
+ const ssoProviderBodySchema = z.object({
134
+ providerId: z.string({}).meta({
135
+ description:
136
+ "The ID of the provider. This is used to identify the provider during login and callback",
137
+ }),
138
+ issuer: z.string({}).meta({
139
+ description: "The issuer of the provider",
140
+ }),
141
+ domain: z.string({}).meta({
142
+ description: "The domain of the provider. This is used for email matching",
143
+ }),
144
+ oidcConfig: z
145
+ .object({
146
+ clientId: z.string({}).meta({
147
+ description: "The client ID",
148
+ }),
149
+ clientSecret: z.string({}).meta({
150
+ description: "The client secret",
151
+ }),
152
+ authorizationEndpoint: z
153
+ .string({})
154
+ .meta({
155
+ description: "The authorization endpoint",
156
+ })
157
+ .optional(),
158
+ tokenEndpoint: z
159
+ .string({})
160
+ .meta({
161
+ description: "The token endpoint",
162
+ })
163
+ .optional(),
164
+ userInfoEndpoint: z
165
+ .string({})
166
+ .meta({
167
+ description: "The user info endpoint",
168
+ })
169
+ .optional(),
170
+ tokenEndpointAuthentication: z
171
+ .enum(["client_secret_post", "client_secret_basic"])
172
+ .optional(),
173
+ jwksEndpoint: z
174
+ .string({})
175
+ .meta({
176
+ description: "The JWKS endpoint",
177
+ })
178
+ .optional(),
179
+ discoveryEndpoint: z.string().optional(),
180
+ scopes: z
181
+ .array(z.string(), {})
182
+ .meta({
145
183
  description:
146
- "The domain of the provider. This is used for email matching",
147
- }),
148
- oidcConfig: z
149
- .object({
150
- clientId: z.string({}).meta({
151
- description: "The client ID",
152
- }),
153
- clientSecret: z.string({}).meta({
154
- description: "The client secret",
155
- }),
156
- authorizationEndpoint: z
157
- .string({})
158
- .meta({
159
- description: "The authorization endpoint",
160
- })
161
- .optional(),
162
- tokenEndpoint: z
163
- .string({})
164
- .meta({
165
- description: "The token endpoint",
166
- })
167
- .optional(),
168
- userInfoEndpoint: z
169
- .string({})
170
- .meta({
171
- description: "The user info endpoint",
172
- })
173
- .optional(),
174
- tokenEndpointAuthentication: z
175
- .enum(["client_secret_post", "client_secret_basic"])
176
- .optional(),
177
- jwksEndpoint: z
178
- .string({})
179
- .meta({
180
- description: "The JWKS endpoint",
181
- })
182
- .optional(),
183
- discoveryEndpoint: z.string().optional(),
184
- scopes: z
185
- .array(z.string(), {})
186
- .meta({
187
- description:
188
- "The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']",
189
- })
190
- .optional(),
191
- pkce: z
192
- .boolean({})
193
- .meta({
194
- description: "Whether to use PKCE for the authorization flow",
195
- })
196
- .default(true)
197
- .optional(),
198
- mapping: z
199
- .object({
200
- id: z.string({}).meta({
201
- description: "Field mapping for user ID (defaults to 'sub')",
202
- }),
203
- email: z.string({}).meta({
204
- description: "Field mapping for email (defaults to 'email')",
184
+ "The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']",
185
+ })
186
+ .optional(),
187
+ pkce: z
188
+ .boolean({})
189
+ .meta({
190
+ description: "Whether to use PKCE for the authorization flow",
191
+ })
192
+ .default(true)
193
+ .optional(),
194
+ mapping: z
195
+ .object({
196
+ id: z.string({}).meta({
197
+ description: "Field mapping for user ID (defaults to 'sub')",
198
+ }),
199
+ email: z.string({}).meta({
200
+ description: "Field mapping for email (defaults to 'email')",
201
+ }),
202
+ emailVerified: z
203
+ .string({})
204
+ .meta({
205
+ description:
206
+ "Field mapping for email verification (defaults to 'email_verified')",
207
+ })
208
+ .optional(),
209
+ name: z.string({}).meta({
210
+ description: "Field mapping for name (defaults to 'name')",
211
+ }),
212
+ image: z
213
+ .string({})
214
+ .meta({
215
+ description: "Field mapping for image (defaults to 'picture')",
216
+ })
217
+ .optional(),
218
+ extraFields: z.record(z.string(), z.any()).optional(),
219
+ })
220
+ .optional(),
221
+ })
222
+ .optional(),
223
+ samlConfig: z
224
+ .object({
225
+ entryPoint: z.string({}).meta({
226
+ description: "The entry point of the provider",
227
+ }),
228
+ cert: z.string({}).meta({
229
+ description: "The certificate of the provider",
230
+ }),
231
+ callbackUrl: z.string({}).meta({
232
+ description: "The callback URL of the provider",
233
+ }),
234
+ audience: z.string().optional(),
235
+ idpMetadata: z
236
+ .object({
237
+ metadata: z.string().optional(),
238
+ entityID: z.string().optional(),
239
+ cert: z.string().optional(),
240
+ privateKey: z.string().optional(),
241
+ privateKeyPass: z.string().optional(),
242
+ isAssertionEncrypted: z.boolean().optional(),
243
+ encPrivateKey: z.string().optional(),
244
+ encPrivateKeyPass: z.string().optional(),
245
+ singleSignOnService: z
246
+ .array(
247
+ z.object({
248
+ Binding: z.string().meta({
249
+ description: "The binding type for the SSO service",
205
250
  }),
206
- emailVerified: z
207
- .string({})
208
- .meta({
209
- description:
210
- "Field mapping for email verification (defaults to 'email_verified')",
211
- })
212
- .optional(),
213
- name: z.string({}).meta({
214
- description: "Field mapping for name (defaults to 'name')",
251
+ Location: z.string().meta({
252
+ description: "The URL for the SSO service",
215
253
  }),
216
- image: z
217
- .string({})
218
- .meta({
219
- description:
220
- "Field mapping for image (defaults to 'picture')",
221
- })
222
- .optional(),
223
- extraFields: z.record(z.string(), z.any()).optional(),
224
- })
225
- .optional(),
226
- })
227
- .optional(),
228
- samlConfig: z
229
- .object({
230
- entryPoint: z.string({}).meta({
231
- description: "The entry point of the provider",
232
- }),
233
- cert: z.string({}).meta({
234
- description: "The certificate of the provider",
235
- }),
236
- callbackUrl: z.string({}).meta({
237
- description: "The callback URL of the provider",
238
- }),
239
- audience: z.string().optional(),
240
- idpMetadata: z
241
- .object({
242
- metadata: z.string().optional(),
243
- entityID: z.string().optional(),
244
- cert: z.string().optional(),
245
- privateKey: z.string().optional(),
246
- privateKeyPass: z.string().optional(),
247
- isAssertionEncrypted: z.boolean().optional(),
248
- encPrivateKey: z.string().optional(),
249
- encPrivateKeyPass: z.string().optional(),
250
- singleSignOnService: z
251
- .array(
252
- z.object({
253
- Binding: z.string().meta({
254
- description: "The binding type for the SSO service",
255
- }),
256
- Location: z.string().meta({
257
- description: "The URL for the SSO service",
258
- }),
259
- }),
260
- )
261
- .optional()
262
- .meta({
263
- description: "Single Sign-On service configuration",
264
- }),
265
- })
266
- .optional(),
267
- spMetadata: z.object({
268
- metadata: z.string().optional(),
269
- entityID: z.string().optional(),
270
- binding: z.string().optional(),
271
- privateKey: z.string().optional(),
272
- privateKeyPass: z.string().optional(),
273
- isAssertionEncrypted: z.boolean().optional(),
274
- encPrivateKey: z.string().optional(),
275
- encPrivateKeyPass: z.string().optional(),
254
+ }),
255
+ )
256
+ .optional()
257
+ .meta({
258
+ description: "Single Sign-On service configuration",
276
259
  }),
277
- wantAssertionsSigned: z.boolean().optional(),
278
- signatureAlgorithm: z.string().optional(),
279
- digestAlgorithm: z.string().optional(),
280
- identifierFormat: z.string().optional(),
281
- privateKey: z.string().optional(),
282
- decryptionPvk: z.string().optional(),
283
- additionalParams: z.record(z.string(), z.any()).optional(),
284
- mapping: z
285
- .object({
286
- id: z.string({}).meta({
287
- description:
288
- "Field mapping for user ID (defaults to 'nameID')",
289
- }),
290
- email: z.string({}).meta({
291
- description: "Field mapping for email (defaults to 'email')",
292
- }),
293
- emailVerified: z
294
- .string({})
295
- .meta({
296
- description: "Field mapping for email verification",
297
- })
298
- .optional(),
299
- name: z.string({}).meta({
300
- description:
301
- "Field mapping for name (defaults to 'displayName')",
302
- }),
303
- firstName: z
304
- .string({})
305
- .meta({
306
- description:
307
- "Field mapping for first name (defaults to 'givenName')",
308
- })
309
- .optional(),
310
- lastName: z
311
- .string({})
312
- .meta({
313
- description:
314
- "Field mapping for last name (defaults to 'surname')",
315
- })
316
- .optional(),
317
- extraFields: z.record(z.string(), z.any()).optional(),
318
- })
319
- .optional(),
320
- })
321
- .optional(),
322
- organizationId: z
323
- .string({})
324
- .meta({
325
- description:
326
- "If organization plugin is enabled, the organization id to link the provider to",
327
- })
328
- .optional(),
329
- overrideUserInfo: z
330
- .boolean({})
331
- .meta({
332
- description:
333
- "Override user info with the provider info. Defaults to false",
334
- })
335
- .default(false)
336
- .optional(),
260
+ })
261
+ .optional(),
262
+ spMetadata: z.object({
263
+ metadata: z.string().optional(),
264
+ entityID: z.string().optional(),
265
+ binding: z.string().optional(),
266
+ privateKey: z.string().optional(),
267
+ privateKeyPass: z.string().optional(),
268
+ isAssertionEncrypted: z.boolean().optional(),
269
+ encPrivateKey: z.string().optional(),
270
+ encPrivateKeyPass: z.string().optional(),
337
271
  }),
272
+ wantAssertionsSigned: z.boolean().optional(),
273
+ signatureAlgorithm: z.string().optional(),
274
+ digestAlgorithm: z.string().optional(),
275
+ identifierFormat: z.string().optional(),
276
+ privateKey: z.string().optional(),
277
+ decryptionPvk: z.string().optional(),
278
+ additionalParams: z.record(z.string(), z.any()).optional(),
279
+ mapping: z
280
+ .object({
281
+ id: z.string({}).meta({
282
+ description: "Field mapping for user ID (defaults to 'nameID')",
283
+ }),
284
+ email: z.string({}).meta({
285
+ description: "Field mapping for email (defaults to 'email')",
286
+ }),
287
+ emailVerified: z
288
+ .string({})
289
+ .meta({
290
+ description: "Field mapping for email verification",
291
+ })
292
+ .optional(),
293
+ name: z.string({}).meta({
294
+ description: "Field mapping for name (defaults to 'displayName')",
295
+ }),
296
+ firstName: z
297
+ .string({})
298
+ .meta({
299
+ description:
300
+ "Field mapping for first name (defaults to 'givenName')",
301
+ })
302
+ .optional(),
303
+ lastName: z
304
+ .string({})
305
+ .meta({
306
+ description:
307
+ "Field mapping for last name (defaults to 'surname')",
308
+ })
309
+ .optional(),
310
+ extraFields: z.record(z.string(), z.any()).optional(),
311
+ })
312
+ .optional(),
313
+ })
314
+ .optional(),
315
+ organizationId: z
316
+ .string({})
317
+ .meta({
318
+ description:
319
+ "If organization plugin is enabled, the organization id to link the provider to",
320
+ })
321
+ .optional(),
322
+ overrideUserInfo: z
323
+ .boolean({})
324
+ .meta({
325
+ description:
326
+ "Override user info with the provider info. Defaults to false",
327
+ })
328
+ .default(false)
329
+ .optional(),
330
+ });
331
+
332
+ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
333
+ return createAuthEndpoint(
334
+ "/sso/register",
335
+ {
336
+ method: "POST",
337
+ body: ssoProviderBodySchema,
338
338
  use: [sessionMiddleware],
339
339
  metadata: {
340
340
  openapi: {
@@ -699,76 +699,77 @@ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
699
699
  );
700
700
  };
701
701
 
702
+ const signInSSOBodySchema = z.object({
703
+ email: z
704
+ .string({})
705
+ .meta({
706
+ description:
707
+ "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",
708
+ })
709
+ .optional(),
710
+ organizationSlug: z
711
+ .string({})
712
+ .meta({
713
+ description: "The slug of the organization to sign in with",
714
+ })
715
+ .optional(),
716
+ providerId: z
717
+ .string({})
718
+ .meta({
719
+ description:
720
+ "The ID of the provider to sign in with. This can be provided instead of email or issuer",
721
+ })
722
+ .optional(),
723
+ domain: z
724
+ .string({})
725
+ .meta({
726
+ description: "The domain of the provider.",
727
+ })
728
+ .optional(),
729
+ callbackURL: z.string({}).meta({
730
+ description: "The URL to redirect to after login",
731
+ }),
732
+ errorCallbackURL: z
733
+ .string({})
734
+ .meta({
735
+ description: "The URL to redirect to after login",
736
+ })
737
+ .optional(),
738
+ newUserCallbackURL: z
739
+ .string({})
740
+ .meta({
741
+ description: "The URL to redirect to after login if the user is new",
742
+ })
743
+ .optional(),
744
+ scopes: z
745
+ .array(z.string(), {})
746
+ .meta({
747
+ description: "Scopes to request from the provider.",
748
+ })
749
+ .optional(),
750
+ loginHint: z
751
+ .string({})
752
+ .meta({
753
+ description:
754
+ "Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'.",
755
+ })
756
+ .optional(),
757
+ requestSignUp: z
758
+ .boolean({})
759
+ .meta({
760
+ description:
761
+ "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider",
762
+ })
763
+ .optional(),
764
+ providerType: z.enum(["oidc", "saml"]).optional(),
765
+ });
766
+
702
767
  export const signInSSO = (options?: SSOOptions) => {
703
768
  return createAuthEndpoint(
704
769
  "/sign-in/sso",
705
770
  {
706
771
  method: "POST",
707
- body: z.object({
708
- email: z
709
- .string({})
710
- .meta({
711
- description:
712
- "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",
713
- })
714
- .optional(),
715
- organizationSlug: z
716
- .string({})
717
- .meta({
718
- description: "The slug of the organization to sign in with",
719
- })
720
- .optional(),
721
- providerId: z
722
- .string({})
723
- .meta({
724
- description:
725
- "The ID of the provider to sign in with. This can be provided instead of email or issuer",
726
- })
727
- .optional(),
728
- domain: z
729
- .string({})
730
- .meta({
731
- description: "The domain of the provider.",
732
- })
733
- .optional(),
734
- callbackURL: z.string({}).meta({
735
- description: "The URL to redirect to after login",
736
- }),
737
- errorCallbackURL: z
738
- .string({})
739
- .meta({
740
- description: "The URL to redirect to after login",
741
- })
742
- .optional(),
743
- newUserCallbackURL: z
744
- .string({})
745
- .meta({
746
- description:
747
- "The URL to redirect to after login if the user is new",
748
- })
749
- .optional(),
750
- scopes: z
751
- .array(z.string(), {})
752
- .meta({
753
- description: "Scopes to request from the provider.",
754
- })
755
- .optional(),
756
- loginHint: z
757
- .string({})
758
- .meta({
759
- description:
760
- "Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'.",
761
- })
762
- .optional(),
763
- requestSignUp: z
764
- .boolean({})
765
- .meta({
766
- description:
767
- "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider",
768
- })
769
- .optional(),
770
- providerType: z.enum(["oidc", "saml"]).optional(),
771
- }),
772
+ body: signInSSOBodySchema,
772
773
  metadata: {
773
774
  openapi: {
774
775
  operationId: "signInWithSSO",
@@ -1101,17 +1102,19 @@ export const signInSSO = (options?: SSOOptions) => {
1101
1102
  );
1102
1103
  };
1103
1104
 
1105
+ const callbackSSOQuerySchema = z.object({
1106
+ code: z.string().optional(),
1107
+ state: z.string(),
1108
+ error: z.string().optional(),
1109
+ error_description: z.string().optional(),
1110
+ });
1111
+
1104
1112
  export const callbackSSO = (options?: SSOOptions) => {
1105
1113
  return createAuthEndpoint(
1106
1114
  "/sso/callback/:providerId",
1107
1115
  {
1108
1116
  method: "GET",
1109
- query: z.object({
1110
- code: z.string().optional(),
1111
- state: z.string(),
1112
- error: z.string().optional(),
1113
- error_description: z.string().optional(),
1114
- }),
1117
+ query: callbackSSOQuerySchema,
1115
1118
  allowedMediaTypes: [
1116
1119
  "application/x-www-form-urlencoded",
1117
1120
  "application/json",
@@ -1468,15 +1471,17 @@ export const callbackSSO = (options?: SSOOptions) => {
1468
1471
  );
1469
1472
  };
1470
1473
 
1474
+ const callbackSSOSAMLBodySchema = z.object({
1475
+ SAMLResponse: z.string(),
1476
+ RelayState: z.string().optional(),
1477
+ });
1478
+
1471
1479
  export const callbackSSOSAML = (options?: SSOOptions) => {
1472
1480
  return createAuthEndpoint(
1473
1481
  "/sso/saml2/callback/:providerId",
1474
1482
  {
1475
1483
  method: "POST",
1476
- body: z.object({
1477
- SAMLResponse: z.string(),
1478
- RelayState: z.string().optional(),
1479
- }),
1484
+ body: callbackSSOSAMLBodySchema,
1480
1485
  metadata: {
1481
1486
  isAction: false,
1482
1487
  allowedMediaTypes: [
@@ -1815,18 +1820,22 @@ export const callbackSSOSAML = (options?: SSOOptions) => {
1815
1820
  );
1816
1821
  };
1817
1822
 
1823
+ const acsEndpointParamsSchema = z.object({
1824
+ providerId: z.string().optional(),
1825
+ });
1826
+
1827
+ const acsEndpointBodySchema = z.object({
1828
+ SAMLResponse: z.string(),
1829
+ RelayState: z.string().optional(),
1830
+ });
1831
+
1818
1832
  export const acsEndpoint = (options?: SSOOptions) => {
1819
1833
  return createAuthEndpoint(
1820
1834
  "/sso/saml2/sp/acs/:providerId",
1821
1835
  {
1822
1836
  method: "POST",
1823
- params: z.object({
1824
- providerId: z.string().optional(),
1825
- }),
1826
- body: z.object({
1827
- SAMLResponse: z.string(),
1828
- RelayState: z.string().optional(),
1829
- }),
1837
+ params: acsEndpointParamsSchema,
1838
+ body: acsEndpointBodySchema,
1830
1839
  metadata: {
1831
1840
  isAction: false,
1832
1841
  allowedMediaTypes: [