@better-auth/sso 1.5.0-beta.8 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/types.ts DELETED
@@ -1,357 +0,0 @@
1
- import type { Awaitable, OAuth2Tokens, User } from "better-auth";
2
- import type { AlgorithmValidationOptions } from "./saml/algorithms";
3
-
4
- export interface OIDCMapping {
5
- id?: string | undefined;
6
- email?: string | undefined;
7
- emailVerified?: string | undefined;
8
- name?: string | undefined;
9
- image?: string | undefined;
10
- extraFields?: Record<string, string> | undefined;
11
- }
12
-
13
- export interface SAMLMapping {
14
- id?: string | undefined;
15
- email?: string | undefined;
16
- emailVerified?: string | undefined;
17
- name?: string | undefined;
18
- firstName?: string | undefined;
19
- lastName?: string | undefined;
20
- extraFields?: Record<string, string> | undefined;
21
- }
22
-
23
- export interface OIDCConfig {
24
- issuer: string;
25
- pkce: boolean;
26
- clientId: string;
27
- clientSecret: string;
28
- authorizationEndpoint?: string | undefined;
29
- discoveryEndpoint: string;
30
- userInfoEndpoint?: string | undefined;
31
- scopes?: string[] | undefined;
32
- overrideUserInfo?: boolean | undefined;
33
- tokenEndpoint?: string | undefined;
34
- tokenEndpointAuthentication?:
35
- | ("client_secret_post" | "client_secret_basic")
36
- | undefined;
37
- jwksEndpoint?: string | undefined;
38
- mapping?: OIDCMapping | undefined;
39
- }
40
-
41
- export interface SAMLConfig {
42
- issuer: string;
43
- entryPoint: string;
44
- cert: string;
45
- callbackUrl: string;
46
- audience?: string | undefined;
47
- idpMetadata?:
48
- | {
49
- metadata?: string;
50
- entityID?: string;
51
- entityURL?: string;
52
- redirectURL?: string;
53
- cert?: string;
54
- privateKey?: string;
55
- privateKeyPass?: string;
56
- isAssertionEncrypted?: boolean;
57
- encPrivateKey?: string;
58
- encPrivateKeyPass?: string;
59
- singleSignOnService?: Array<{
60
- Binding: string;
61
- Location: string;
62
- }>;
63
- }
64
- | undefined;
65
- spMetadata: {
66
- metadata?: string | undefined;
67
- entityID?: string | undefined;
68
- binding?: string | undefined;
69
- privateKey?: string | undefined;
70
- privateKeyPass?: string | undefined;
71
- isAssertionEncrypted?: boolean | undefined;
72
- encPrivateKey?: string | undefined;
73
- encPrivateKeyPass?: string | undefined;
74
- };
75
- wantAssertionsSigned?: boolean | undefined;
76
- signatureAlgorithm?: string | undefined;
77
- digestAlgorithm?: string | undefined;
78
- identifierFormat?: string | undefined;
79
- privateKey?: string | undefined;
80
- decryptionPvk?: string | undefined;
81
- additionalParams?: Record<string, any> | undefined;
82
- mapping?: SAMLMapping | undefined;
83
- }
84
-
85
- type BaseSSOProvider = {
86
- issuer: string;
87
- oidcConfig?: OIDCConfig | undefined;
88
- samlConfig?: SAMLConfig | undefined;
89
- userId: string;
90
- providerId: string;
91
- organizationId?: string | undefined;
92
- domain: string;
93
- };
94
-
95
- export type SSOProvider<O extends SSOOptions> =
96
- O["domainVerification"] extends { enabled: true }
97
- ? {
98
- domainVerified: boolean;
99
- } & BaseSSOProvider
100
- : BaseSSOProvider;
101
-
102
- export interface SSOOptions {
103
- /**
104
- * custom function to provision a user when they sign in with an SSO provider.
105
- */
106
- provisionUser?:
107
- | ((data: {
108
- /**
109
- * The user object from the database
110
- */
111
- user: User & Record<string, any>;
112
- /**
113
- * The user info object from the provider
114
- */
115
- userInfo: Record<string, any>;
116
- /**
117
- * The OAuth2 tokens from the provider
118
- */
119
- token?: OAuth2Tokens;
120
- /**
121
- * The SSO provider
122
- */
123
- provider: SSOProvider<SSOOptions>;
124
- }) => Awaitable<void>)
125
- | undefined;
126
- /**
127
- * Organization provisioning options
128
- */
129
- organizationProvisioning?:
130
- | {
131
- disabled?: boolean;
132
- defaultRole?: "member" | "admin";
133
- getRole?: (data: {
134
- /**
135
- * The user object from the database
136
- */
137
- user: User & Record<string, any>;
138
- /**
139
- * The user info object from the provider
140
- */
141
- userInfo: Record<string, any>;
142
- /**
143
- * The OAuth2 tokens from the provider
144
- */
145
- token?: OAuth2Tokens;
146
- /**
147
- * The SSO provider
148
- */
149
- provider: SSOProvider<SSOOptions>;
150
- }) => Promise<"member" | "admin">;
151
- }
152
- | undefined;
153
- /**
154
- * Default SSO provider configurations for testing.
155
- * These will take the precedence over the database providers.
156
- */
157
- defaultSSO?:
158
- | Array<{
159
- /**
160
- * The domain to match for this default provider.
161
- * This is only used to match incoming requests to this default provider.
162
- */
163
- domain: string;
164
- /**
165
- * The provider ID to use
166
- */
167
- providerId: string;
168
- /**
169
- * SAML configuration
170
- */
171
- samlConfig?: SAMLConfig;
172
- /**
173
- * OIDC configuration
174
- */
175
- oidcConfig?: OIDCConfig;
176
- }>
177
- | undefined;
178
- /**
179
- * Override user info with the provider info.
180
- * @default false
181
- */
182
- defaultOverrideUserInfo?: boolean | undefined;
183
- /**
184
- * Disable implicit sign up for new users. When set to true for the provider,
185
- * sign-in need to be called with with requestSignUp as true to create new users.
186
- */
187
- disableImplicitSignUp?: boolean | undefined;
188
- /**
189
- * The model name for the SSO provider table. Defaults to "ssoProvider".
190
- */
191
- modelName?: string;
192
- /**
193
- * Map fields
194
- *
195
- * @example
196
- * ```ts
197
- * {
198
- * samlConfig: "saml_config"
199
- * }
200
- * ```
201
- */
202
- fields?: {
203
- issuer?: string | undefined;
204
- oidcConfig?: string | undefined;
205
- samlConfig?: string | undefined;
206
- userId?: string | undefined;
207
- providerId?: string | undefined;
208
- organizationId?: string | undefined;
209
- domain?: string | undefined;
210
- };
211
- /**
212
- * Configure the maximum number of SSO providers a user can register.
213
- * You can also pass a function that returns a number.
214
- * Set to 0 to disable SSO provider registration.
215
- *
216
- * @example
217
- * ```ts
218
- * providersLimit: async (user) => {
219
- * const plan = await getUserPlan(user);
220
- * return plan.name === "pro" ? 10 : 1;
221
- * }
222
- * ```
223
- * @default 10
224
- */
225
- providersLimit?: (number | ((user: User) => Awaitable<number>)) | undefined;
226
- /**
227
- * Trust the email verified flag from the provider.
228
- *
229
- * ⚠️ Use this with caution — it can lead to account takeover if misused. Only enable it if users **cannot freely register new providers**. You can
230
- * prevent that by using `disabledPaths` or other safeguards to block provider registration from the client.
231
- *
232
- * If you want to allow account linking for specific trusted providers, enable the `accountLinking` option in your auth config and specify those
233
- * providers in the `trustedProviders` list.
234
- *
235
- * @default false
236
- *
237
- * @deprecated This option is discouraged for new projects. Relying on provider-level `email_verified` is a weaker
238
- * trust signal compared to using `trustedProviders` in `accountLinking` or enabling `domainVerification` for SSO.
239
- * Existing configurations will continue to work, but new integrations should use explicit trust mechanisms.
240
- * This option may be removed in a future major version.
241
- */
242
- trustEmailVerified?: boolean | undefined;
243
- /**
244
- * Enable domain verification on SSO providers
245
- *
246
- * When this option is enabled, new SSO providers will require the associated domain to be verified by the owner
247
- * prior to allowing sign-ins.
248
- */
249
- domainVerification?: {
250
- /**
251
- * Enables or disables the domain verification feature
252
- */
253
- enabled?: boolean;
254
- /**
255
- * Prefix used to generate the domain verification token
256
- *
257
- * @default "better-auth-token-"
258
- */
259
- tokenPrefix?: string;
260
- };
261
- /**
262
- * SAML security options for AuthnRequest/InResponseTo validation.
263
- * This prevents unsolicited responses, replay attacks, and cross-provider injection.
264
- */
265
- saml?: {
266
- /**
267
- * Enable InResponseTo validation for SP-initiated SAML flows.
268
- * When enabled, AuthnRequest IDs are tracked and validated against SAML responses.
269
- *
270
- * Storage behavior:
271
- * - Uses `secondaryStorage` (e.g., Redis) if configured in your auth options
272
- * - Falls back to the verification table in the database otherwise
273
- *
274
- * This works correctly in serverless environments without any additional configuration.
275
- *
276
- * @default false
277
- */
278
- enableInResponseToValidation?: boolean;
279
- /**
280
- * Allow IdP-initiated SSO (unsolicited SAML responses).
281
- * When true, responses without InResponseTo are accepted.
282
- * When false, all responses must correlate to a stored AuthnRequest.
283
- *
284
- * Only applies when InResponseTo validation is enabled.
285
- *
286
- * @default true
287
- */
288
- allowIdpInitiated?: boolean;
289
- /**
290
- * TTL for AuthnRequest records in milliseconds.
291
- * Requests older than this will be rejected.
292
- *
293
- * Only applies when InResponseTo validation is enabled.
294
- *
295
- * @default 300000 (5 minutes)
296
- */
297
- requestTTL?: number;
298
- /**
299
- * Clock skew tolerance for SAML assertion timestamp validation in milliseconds.
300
- * Allows for minor time differences between IdP and SP servers.
301
- *
302
- * Defaults to 300000 (5 minutes) to accommodate:
303
- * - Network latency and processing time
304
- * - Clock synchronization differences (NTP drift)
305
- * - Distributed systems across timezones
306
- *
307
- * For stricter security, reduce to 1-2 minutes (60000-120000).
308
- * For highly distributed systems, increase up to 10 minutes (600000).
309
- *
310
- * @default 300000 (5 minutes)
311
- */
312
- clockSkew?: number;
313
- /**
314
- * Require timestamp conditions (NotBefore/NotOnOrAfter) in SAML assertions.
315
- * When enabled, assertions without timestamp conditions will be rejected.
316
- *
317
- * When disabled (default), assertions without timestamps are accepted
318
- * but a warning is logged.
319
- *
320
- * **SAML Spec Notes:**
321
- * - SAML 2.0 Core: Timestamps are OPTIONAL
322
- * - SAML2Int (enterprise profile): Timestamps are REQUIRED
323
- *
324
- * **Recommendation:** Enable for enterprise/production deployments
325
- * where your IdP follows SAML2Int (Okta, Azure AD, OneLogin, etc.)
326
- *
327
- * @default false
328
- */
329
- requireTimestamps?: boolean;
330
- /**
331
- * Algorithm validation options for SAML responses.
332
- *
333
- * Controls behavior when deprecated algorithms (SHA-1, RSA1_5, 3DES)
334
- * are detected in SAML responses.
335
- *
336
- * @example
337
- * ```ts
338
- * algorithms: {
339
- * onDeprecated: "reject" // Reject deprecated algorithms
340
- * }
341
- * ```
342
- */
343
- algorithms?: AlgorithmValidationOptions;
344
- /**
345
- * Maximum allowed size for SAML responses in bytes.
346
- *
347
- * @default 262144 (256KB)
348
- */
349
- maxResponseSize?: number;
350
- /**
351
- * Maximum allowed size for IdP metadata XML in bytes.
352
- *
353
- * @default 102400 (100KB)
354
- */
355
- maxMetadataSize?: number;
356
- };
357
- }
package/src/utils.ts DELETED
@@ -1,41 +0,0 @@
1
- /**
2
- * Safely parses a value that might be a JSON string or already a parsed object.
3
- * This handles cases where ORMs like Drizzle might return already parsed objects
4
- * instead of JSON strings from TEXT/JSON columns.
5
- *
6
- * @param value - The value to parse (string, object, null, or undefined)
7
- * @returns The parsed object or null
8
- * @throws Error if string parsing fails
9
- */
10
- export function safeJsonParse<T>(
11
- value: string | T | null | undefined,
12
- ): T | null {
13
- if (!value) return null;
14
-
15
- if (typeof value === "object") {
16
- return value as T;
17
- }
18
-
19
- if (typeof value === "string") {
20
- try {
21
- return JSON.parse(value) as T;
22
- } catch (error) {
23
- throw new Error(
24
- `Failed to parse JSON: ${error instanceof Error ? error.message : "Unknown error"}`,
25
- );
26
- }
27
- }
28
-
29
- return null;
30
- }
31
-
32
- export const validateEmailDomain = (email: string, domain: string) => {
33
- const emailDomain = email.split("@")[1]?.toLowerCase();
34
- const providerDomain = domain.toLowerCase();
35
- if (!emailDomain || !providerDomain) {
36
- return false;
37
- }
38
- return (
39
- emailDomain === providerDomain || emailDomain.endsWith(`.${providerDomain}`)
40
- );
41
- };
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "lib": ["esnext", "dom", "dom.iterable"]
5
- },
6
- "references": [
7
- {
8
- "path": "../better-auth/tsconfig.json"
9
- },
10
- {
11
- "path": "../core/tsconfig.json"
12
- }
13
- ]
14
- }
package/tsdown.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from "tsdown";
2
-
3
- export default defineConfig({
4
- dts: { build: true, incremental: true },
5
- format: ["esm"],
6
- entry: ["./src/index.ts", "./src/client.ts"],
7
- external: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"],
8
- });
package/vitest.config.ts DELETED
@@ -1,3 +0,0 @@
1
- import { defineProject } from "vitest/config";
2
-
3
- export default defineProject({});