@better-auth/sso 1.8.0-beta.8 → 1.8.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.mts +1 -3
- package/dist/client.mjs +2 -3
- package/dist/{index-BvKzNnKP.d.mts → index-BkypgbRt.d.mts} +94 -28
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +225 -118
- package/dist/version-D4csRLQZ.mjs +3 -0
- package/package.json +8 -8
- package/dist/version-WwwFpRb_.mjs +0 -5
package/dist/client.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as SSOPlugin } from "./index-
|
|
1
|
+
import { t as SSOPlugin } from "./index-BkypgbRt.mjs";
|
|
2
2
|
import { DBFieldAttribute } from "better-auth/db";
|
|
3
|
-
//#region src/client.d.ts
|
|
4
3
|
interface SSOClientOptions {
|
|
5
4
|
domainVerification?: {
|
|
6
5
|
enabled: boolean;
|
|
@@ -29,5 +28,4 @@ export declare const ssoClient: <CO extends SSOClientOptions>(options?: CO | und
|
|
|
29
28
|
"/sso/get-provider": "GET";
|
|
30
29
|
};
|
|
31
30
|
};
|
|
32
|
-
//#endregion
|
|
33
31
|
export {};
|
package/dist/client.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
2
|
-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-D4csRLQZ.mjs";
|
|
2
|
+
// src/client.ts
|
|
3
3
|
const ssoClient = (options) => {
|
|
4
4
|
return {
|
|
5
5
|
id: "sso-client",
|
|
@@ -11,5 +11,4 @@ const ssoClient = (options) => {
|
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
-
//#endregion
|
|
15
14
|
export { ssoClient };
|
|
@@ -6,7 +6,6 @@ import { DBFieldAttribute, FieldAttributeToObject, InferAdditionalFieldsFromPlug
|
|
|
6
6
|
import { Awaitable, DBTransactionAdapter, OAuth2Tokens, User } from "better-auth";
|
|
7
7
|
import "@better-auth/core/db/adapter";
|
|
8
8
|
import "@better-auth/core";
|
|
9
|
-
//#region src/saml/algorithms.d.ts
|
|
10
9
|
declare const SignatureAlgorithm: {
|
|
11
10
|
readonly RSA_SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
|
|
12
11
|
readonly RSA_SHA256: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
|
|
@@ -44,8 +43,6 @@ interface AlgorithmValidationOptions {
|
|
|
44
43
|
allowedKeyEncryptionAlgorithms?: string[];
|
|
45
44
|
allowedDataEncryptionAlgorithms?: string[];
|
|
46
45
|
}
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region src/types.d.ts
|
|
49
46
|
interface OIDCMapping {
|
|
50
47
|
email?: string | undefined;
|
|
51
48
|
emailVerified?: string | undefined;
|
|
@@ -238,7 +235,13 @@ type SSOUserResolution = {
|
|
|
238
235
|
code: string;
|
|
239
236
|
message?: string | undefined;
|
|
240
237
|
};
|
|
241
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* Normalized provider attributes available to an SSO user resolver. When
|
|
240
|
+
* `mapProfileToUser` is set, `email`, `name`, and `image` hold its result,
|
|
241
|
+
* and `emailVerified` is `false` if it changed the email. The provider's own
|
|
242
|
+
* values remain in `providerClaims`, `verifiedIdTokenClaims`, or
|
|
243
|
+
* `providerAttributes`.
|
|
244
|
+
*/
|
|
242
245
|
type SSOProviderUserProfile = {
|
|
243
246
|
email: string;
|
|
244
247
|
emailVerified: boolean;
|
|
@@ -294,6 +297,68 @@ type SSOUserResolutionInput = SSOOIDCUserResolutionInput | SSOSAMLUserResolution
|
|
|
294
297
|
interface SSOUserResolutionContext {
|
|
295
298
|
database: DBTransactionAdapter;
|
|
296
299
|
}
|
|
300
|
+
interface BaseSSOProfileMappingInput {
|
|
301
|
+
providerId: string;
|
|
302
|
+
accountKey: BaseSSOUserResolutionInput["accountKey"];
|
|
303
|
+
/**
|
|
304
|
+
* Profile fields read from the provider with the provider's `mapping`.
|
|
305
|
+
* `email` is absent when the provider sent no usable email.
|
|
306
|
+
*/
|
|
307
|
+
providerUser: {
|
|
308
|
+
email?: string | undefined;
|
|
309
|
+
/** Always `false` unless `trustEmailVerified` is enabled. */
|
|
310
|
+
emailVerified: boolean;
|
|
311
|
+
name?: string | undefined;
|
|
312
|
+
image?: string | undefined;
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/** OIDC profile data available to `mapProfileToUser`. */
|
|
316
|
+
interface SSOOIDCProfileMappingInput extends BaseSSOProfileMappingInput {
|
|
317
|
+
protocol: "oidc";
|
|
318
|
+
/** Raw claims from UserInfo, or the verified ID Token when UserInfo is absent. */
|
|
319
|
+
providerClaims: Record<string, unknown>;
|
|
320
|
+
/** Claims from the cryptographically verified ID Token, or `{}` when none was issued. */
|
|
321
|
+
verifiedIdTokenClaims: Record<string, unknown>;
|
|
322
|
+
}
|
|
323
|
+
/** SAML profile data available to `mapProfileToUser`. */
|
|
324
|
+
interface SSOSAMLProfileMappingInput extends BaseSSOProfileMappingInput {
|
|
325
|
+
protocol: "saml";
|
|
326
|
+
/**
|
|
327
|
+
* Attributes from the verified assertion. Multi-valued attributes remain
|
|
328
|
+
* arrays and all scalar values remain strings.
|
|
329
|
+
*/
|
|
330
|
+
providerAttributes: Record<string, string | readonly string[]>;
|
|
331
|
+
/** The verified assertion's NameID, which is also `accountKey.accountId`. */
|
|
332
|
+
nameID: string;
|
|
333
|
+
/** The NameID `Format` attribute, when the assertion sets one. */
|
|
334
|
+
nameIDFormat?: string | undefined;
|
|
335
|
+
}
|
|
336
|
+
/** Provider profile data available to `mapProfileToUser`. */
|
|
337
|
+
type SSOProfileMappingInput = SSOOIDCProfileMappingInput | SSOSAMLProfileMappingInput;
|
|
338
|
+
/**
|
|
339
|
+
* User fields an application can set from `mapProfileToUser`. Omitted or
|
|
340
|
+
* `undefined` fields keep the provider's value. Other keys are ignored; set
|
|
341
|
+
* additional user fields with the provider's `mapping.extraFields` or with
|
|
342
|
+
* `provisionUser`.
|
|
343
|
+
*/
|
|
344
|
+
interface SSOMappedUser {
|
|
345
|
+
/** The account subject always comes from the provider's `sub` claim or NameID. */
|
|
346
|
+
id?: never;
|
|
347
|
+
/**
|
|
348
|
+
* An email that differs from the provider's must be an email address and is
|
|
349
|
+
* stored unverified. Return `null` to refuse the email the provider sent;
|
|
350
|
+
* sign-in then fails with `missing_user_info`.
|
|
351
|
+
*/
|
|
352
|
+
email?: string | null | undefined;
|
|
353
|
+
/**
|
|
354
|
+
* Not settable. The stored email is verified only when the provider asserts
|
|
355
|
+
* it with `trustEmailVerified` on. Domain verification lets an unverified
|
|
356
|
+
* email link to an existing user; it does not mark the email verified.
|
|
357
|
+
*/
|
|
358
|
+
emailVerified?: never;
|
|
359
|
+
name?: string | undefined;
|
|
360
|
+
image?: string | undefined;
|
|
361
|
+
}
|
|
297
362
|
interface BaseSSOProviderMutationGuardInput {
|
|
298
363
|
provider: {
|
|
299
364
|
id: string;
|
|
@@ -323,13 +388,33 @@ interface SSOProviderMutationGuardContext {
|
|
|
323
388
|
database: DBTransactionAdapter;
|
|
324
389
|
}
|
|
325
390
|
interface SSOOptions {
|
|
391
|
+
/**
|
|
392
|
+
* Map a provider profile to user fields before the user is looked up or
|
|
393
|
+
* created.
|
|
394
|
+
*
|
|
395
|
+
* Runs on every OIDC and SAML sign-in, after the provider's `mapping` is
|
|
396
|
+
* applied. Use it for providers that send no email: sign-in fails when
|
|
397
|
+
* neither the provider nor this callback yields one. A SAML NameID is used
|
|
398
|
+
* as the email only when its `Format` is
|
|
399
|
+
* `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`. If the callback
|
|
400
|
+
* throws or returns an invalid result, sign-in redirects with
|
|
401
|
+
* `SSO_PROFILE_MAPPING_FAILED`.
|
|
402
|
+
*
|
|
403
|
+
* The returned email is used to find, link, and create the user, and to
|
|
404
|
+
* decide whether the provider's verified domain trusts it. An email that
|
|
405
|
+
* differs from the provider's is stored unverified. Return only addresses
|
|
406
|
+
* the provider vouches for, or a placeholder built with
|
|
407
|
+
* `createPlaceholderEmail` from `@better-auth/core/utils/email`.
|
|
408
|
+
*/
|
|
409
|
+
mapProfileToUser?: ((input: SSOProfileMappingInput) => Awaitable<SSOMappedUser>) | undefined;
|
|
326
410
|
/**
|
|
327
411
|
* Resolve a verified provider identity to a Better Auth user.
|
|
328
412
|
*
|
|
329
413
|
* For OIDC, `accountKey` is derived from the validated ID Token. For SAML,
|
|
330
414
|
* it contains the verified IdP entity ID and signed NameID. Profile fields,
|
|
331
415
|
* raw OIDC claims, and SAML assertion attributes are protocol-accepted
|
|
332
|
-
* provider data and may require application-level validation.
|
|
416
|
+
* provider data and may require application-level validation. When
|
|
417
|
+
* `mapProfileToUser` is set, `providerUser` holds the mapped profile.
|
|
333
418
|
*
|
|
334
419
|
* The callback runs on every SSO sign-in inside the same native database
|
|
335
420
|
* transaction as account finalization and session creation.
|
|
@@ -683,8 +768,6 @@ interface SSOOptions {
|
|
|
683
768
|
idpInitiatedCallbackUrl?: string | undefined;
|
|
684
769
|
};
|
|
685
770
|
}
|
|
686
|
-
//#endregion
|
|
687
|
-
//#region src/routes/domain-verification.d.ts
|
|
688
771
|
declare const requestDomainVerification: (options: SSOOptions) => import("better-call").StrictEndpoint<"/sso/request-domain-verification", {
|
|
689
772
|
method: "POST";
|
|
690
773
|
body: z.ZodObject<{
|
|
@@ -782,16 +865,12 @@ declare const verifyDomain: (options: SSOOptions) => import("better-call").Stric
|
|
|
782
865
|
};
|
|
783
866
|
}>>[];
|
|
784
867
|
}, void>;
|
|
785
|
-
//#endregion
|
|
786
|
-
//#region src/utils.d.ts
|
|
787
868
|
declare function parseCertificate(certPem: string): {
|
|
788
869
|
fingerprintSha256: string;
|
|
789
870
|
notBefore: string;
|
|
790
871
|
notAfter: string;
|
|
791
872
|
publicKeyAlgorithm: string;
|
|
792
873
|
};
|
|
793
|
-
//#endregion
|
|
794
|
-
//#region src/routes/providers.d.ts
|
|
795
874
|
type ParsedCert = ReturnType<typeof parseCertificate>;
|
|
796
875
|
type SanitizedCert = ParsedCert | {
|
|
797
876
|
error: string;
|
|
@@ -1150,8 +1229,6 @@ declare const deleteSSOProvider: (options?: SSOOptions) => import("better-call")
|
|
|
1150
1229
|
}, {
|
|
1151
1230
|
success: boolean;
|
|
1152
1231
|
}>;
|
|
1153
|
-
//#endregion
|
|
1154
|
-
//#region src/routes/sso.d.ts
|
|
1155
1232
|
declare const spMetadata: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/saml2/sp/metadata", {
|
|
1156
1233
|
method: "GET";
|
|
1157
1234
|
query: z.ZodObject<{
|
|
@@ -1200,7 +1277,9 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => import(
|
|
|
1200
1277
|
}>>[];
|
|
1201
1278
|
metadata: {
|
|
1202
1279
|
$Infer: {
|
|
1203
|
-
body:
|
|
1280
|
+
body: {
|
|
1281
|
+
[x: string]: any;
|
|
1282
|
+
} & SSOProviderAdditionalFieldsInput<O>;
|
|
1204
1283
|
};
|
|
1205
1284
|
openapi: {
|
|
1206
1285
|
operationId: string;
|
|
@@ -1627,8 +1706,6 @@ declare const initiateSLO: (options?: SSOOptions) => import("better-call").Stric
|
|
|
1627
1706
|
readonly scope: "server";
|
|
1628
1707
|
};
|
|
1629
1708
|
}, never>;
|
|
1630
|
-
//#endregion
|
|
1631
|
-
//#region src/constants.d.ts
|
|
1632
1709
|
/**
|
|
1633
1710
|
* Default clock skew tolerance (5 minutes).
|
|
1634
1711
|
* Allows for minor time differences between IdP and SP servers.
|
|
@@ -1649,8 +1726,6 @@ declare const DEFAULT_MAX_SAML_RESPONSE_SIZE: number;
|
|
|
1649
1726
|
* Protects against oversized metadata documents.
|
|
1650
1727
|
*/
|
|
1651
1728
|
declare const DEFAULT_MAX_SAML_METADATA_SIZE: number;
|
|
1652
|
-
//#endregion
|
|
1653
|
-
//#region src/routes/helpers.d.ts
|
|
1654
1729
|
interface SAMLServiceProviderPolicy {
|
|
1655
1730
|
/** Effective assertion-signing requirement advertised by SP metadata. */
|
|
1656
1731
|
wantAssertionsSigned: boolean;
|
|
@@ -1668,8 +1743,6 @@ declare function deriveSAMLServiceProviderPolicy(config: Pick<SAMLConfig, "spMet
|
|
|
1668
1743
|
* parsing and manual-configuration validation as SAML authentication.
|
|
1669
1744
|
*/
|
|
1670
1745
|
declare function deriveSAMLIdentityProviderEntityID(config: SAMLConfig): string;
|
|
1671
|
-
//#endregion
|
|
1672
|
-
//#region src/saml/timestamp.d.ts
|
|
1673
1746
|
interface TimestampValidationOptions {
|
|
1674
1747
|
clockSkew?: number;
|
|
1675
1748
|
requireTimestamps?: boolean;
|
|
@@ -1688,8 +1761,6 @@ interface SAMLConditions {
|
|
|
1688
1761
|
* @throws {APIError} If timestamps are invalid, expired, or not yet valid
|
|
1689
1762
|
*/
|
|
1690
1763
|
declare function validateSAMLTimestamp(conditions: SAMLConditions | undefined, options?: TimestampValidationOptions): void;
|
|
1691
|
-
//#endregion
|
|
1692
|
-
//#region src/oidc/types.d.ts
|
|
1693
1764
|
/**
|
|
1694
1765
|
* OIDC Discovery Types
|
|
1695
1766
|
*
|
|
@@ -1856,8 +1927,6 @@ interface DiscoverOIDCConfigParams {
|
|
|
1856
1927
|
*/
|
|
1857
1928
|
declare const REQUIRED_DISCOVERY_FIELDS: readonly ["issuer", "authorization_endpoint", "token_endpoint", "jwks_uri"];
|
|
1858
1929
|
type RequiredDiscoveryField = (typeof REQUIRED_DISCOVERY_FIELDS)[number];
|
|
1859
|
-
//#endregion
|
|
1860
|
-
//#region src/oidc/discovery.d.ts
|
|
1861
1930
|
/**
|
|
1862
1931
|
* Main entry point: Discover and hydrate OIDC configuration from an issuer.
|
|
1863
1932
|
*
|
|
@@ -1957,8 +2026,6 @@ declare function selectTokenEndpointAuthMethod(doc: OIDCDiscoveryDocument, exist
|
|
|
1957
2026
|
* @returns true if runtime discovery should be performed
|
|
1958
2027
|
*/
|
|
1959
2028
|
declare function needsRuntimeDiscovery(config: Partial<HydratedOIDCConfig> | undefined): boolean;
|
|
1960
|
-
//#endregion
|
|
1961
|
-
//#region src/index.d.ts
|
|
1962
2029
|
declare module "@better-auth/core" {
|
|
1963
2030
|
interface BetterAuthPluginRegistry<AuthOptions, Options> {
|
|
1964
2031
|
sso: {
|
|
@@ -2022,5 +2089,4 @@ declare function sso<O extends SSOOptions>(options?: O | undefined): {
|
|
|
2022
2089
|
};
|
|
2023
2090
|
options: NoInfer<O>;
|
|
2024
2091
|
};
|
|
2025
|
-
|
|
2026
|
-
export { SAMLIdentityProviderMetadata as A, SSOUserResolutionContext as B, deriveSAMLIdentityProviderEntityID as C, DEFAULT_MAX_SAML_RESPONSE_SIZE as D, DEFAULT_MAX_SAML_METADATA_SIZE as E, SSOProviderMutationGuardInput as F, DigestAlgorithm as G, AlgorithmValidationOptions as H, SSOProviderReference as I, KeyEncryptionAlgorithm as K, SSOProviderUserProfile as L, SSOOptions as M, SSOProvider as N, OIDCConfig as O, SSOProviderMutationGuardContext as P, SSOSAMLUserResolutionInput as R, SAMLServiceProviderPolicy as S, DEFAULT_CLOCK_SKEW_MS as T, DataEncryptionAlgorithm as U, SSOUserResolutionInput as V, DeprecatedAlgorithmBehavior as W, REQUIRED_DISCOVERY_FIELDS as _, fetchDiscoveryDocument as a, TimestampValidationOptions as b, normalizeUrl as c, validateDiscoveryUrl as d, DiscoverOIDCConfigParams as f, OIDCDiscoveryDocument as g, HydratedOIDCConfig as h, discoverOIDCConfig as i, SSOOIDCUserResolutionInput as j, SAMLConfig as k, selectTokenEndpointAuthMethod as l, DiscoveryErrorCode as m, sso as n, needsRuntimeDiscovery as o, DiscoveryError as p, SignatureAlgorithm as q, computeDiscoveryUrl as r, normalizeDiscoveryUrls as s, SSOPlugin as t, validateDiscoveryDocument as u, RequiredDiscoveryField as v, deriveSAMLServiceProviderPolicy as w, validateSAMLTimestamp as x, SAMLConditions as y, SSOUserResolution as z };
|
|
2092
|
+
export { SAMLIdentityProviderMetadata as A, SSOProviderUserProfile as B, deriveSAMLIdentityProviderEntityID as C, DEFAULT_MAX_SAML_RESPONSE_SIZE as D, DEFAULT_MAX_SAML_METADATA_SIZE as E, SSOProfileMappingInput as F, SSOUserResolutionInput as G, SSOSAMLUserResolutionInput as H, SSOProvider as I, DeprecatedAlgorithmBehavior as J, AlgorithmValidationOptions as K, SSOProviderMutationGuardContext as L, SSOOIDCProfileMappingInput as M, SSOOIDCUserResolutionInput as N, OIDCConfig as O, SSOOptions as P, SSOProviderMutationGuardInput as R, SAMLServiceProviderPolicy as S, DEFAULT_CLOCK_SKEW_MS as T, SSOUserResolution as U, SSOSAMLProfileMappingInput as V, SSOUserResolutionContext as W, KeyEncryptionAlgorithm as X, DigestAlgorithm as Y, SignatureAlgorithm as Z, REQUIRED_DISCOVERY_FIELDS as _, fetchDiscoveryDocument as a, TimestampValidationOptions as b, normalizeUrl as c, validateDiscoveryUrl as d, DiscoverOIDCConfigParams as f, OIDCDiscoveryDocument as g, HydratedOIDCConfig as h, discoverOIDCConfig as i, SSOMappedUser as j, SAMLConfig as k, selectTokenEndpointAuthMethod as l, DiscoveryErrorCode as m, sso as n, needsRuntimeDiscovery as o, DiscoveryError as p, DataEncryptionAlgorithm as q, computeDiscoveryUrl as r, normalizeDiscoveryUrls as s, SSOPlugin as t, validateDiscoveryDocument as u, RequiredDiscoveryField as v, deriveSAMLServiceProviderPolicy as w, validateSAMLTimestamp as x, SAMLConditions as y, SSOProviderReference as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as SAMLIdentityProviderMetadata, B as
|
|
2
|
-
export { type AlgorithmValidationOptions, DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, type DeprecatedAlgorithmBehavior, DigestAlgorithm, type DiscoverOIDCConfigParams, DiscoveryError, type DiscoveryErrorCode, type HydratedOIDCConfig, KeyEncryptionAlgorithm, type OIDCConfig, type OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, type RequiredDiscoveryField, type SAMLConditions, type SAMLConfig, type SAMLIdentityProviderMetadata, type SAMLServiceProviderPolicy, type SSOOIDCUserResolutionInput, type SSOOptions, SSOPlugin, type SSOProvider, type SSOProviderMutationGuardContext, type SSOProviderMutationGuardInput, type SSOProviderReference, type SSOProviderUserProfile, type SSOSAMLUserResolutionInput, type SSOUserResolution, type SSOUserResolutionContext, type SSOUserResolutionInput, SignatureAlgorithm, type TimestampValidationOptions, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
|
1
|
+
import { A as SAMLIdentityProviderMetadata, B as SSOProviderUserProfile, C as deriveSAMLIdentityProviderEntityID, D as DEFAULT_MAX_SAML_RESPONSE_SIZE, E as DEFAULT_MAX_SAML_METADATA_SIZE, F as SSOProfileMappingInput, G as SSOUserResolutionInput, H as SSOSAMLUserResolutionInput, I as SSOProvider, J as DeprecatedAlgorithmBehavior, K as AlgorithmValidationOptions, L as SSOProviderMutationGuardContext, M as SSOOIDCProfileMappingInput, N as SSOOIDCUserResolutionInput, O as OIDCConfig, P as SSOOptions, R as SSOProviderMutationGuardInput, S as SAMLServiceProviderPolicy, T as DEFAULT_CLOCK_SKEW_MS, U as SSOUserResolution, V as SSOSAMLProfileMappingInput, W as SSOUserResolutionContext, X as KeyEncryptionAlgorithm, Y as DigestAlgorithm, Z as SignatureAlgorithm, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SSOMappedUser, k as SAMLConfig, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, q as DataEncryptionAlgorithm, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as deriveSAMLServiceProviderPolicy, x as validateSAMLTimestamp, y as SAMLConditions, z as SSOProviderReference } from "./index-BkypgbRt.mjs";
|
|
2
|
+
export { type AlgorithmValidationOptions, DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, type DeprecatedAlgorithmBehavior, DigestAlgorithm, type DiscoverOIDCConfigParams, DiscoveryError, type DiscoveryErrorCode, type HydratedOIDCConfig, KeyEncryptionAlgorithm, type OIDCConfig, type OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, type RequiredDiscoveryField, type SAMLConditions, type SAMLConfig, type SAMLIdentityProviderMetadata, type SAMLServiceProviderPolicy, type SSOMappedUser, type SSOOIDCProfileMappingInput, type SSOOIDCUserResolutionInput, type SSOOptions, SSOPlugin, type SSOProfileMappingInput, type SSOProvider, type SSOProviderMutationGuardContext, type SSOProviderMutationGuardInput, type SSOProviderReference, type SSOProviderUserProfile, type SSOSAMLProfileMappingInput, type SSOSAMLUserResolutionInput, type SSOUserResolution, type SSOUserResolutionContext, type SSOUserResolutionInput, SignatureAlgorithm, type TimestampValidationOptions, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-D4csRLQZ.mjs";
|
|
2
2
|
import { APIError, addOAuthServerContext, createAuthEndpoint, createAuthMiddleware, generateGenericState, getSessionFromCtx, parseGenericState, sessionMiddleware } from "better-auth/api";
|
|
3
3
|
import { XMLParser, XMLValidator } from "fast-xml-parser";
|
|
4
4
|
import { X509Certificate } from "node:crypto";
|
|
@@ -18,12 +18,12 @@ import samlifyDefault, * as samlifyNamespace from "samlify";
|
|
|
18
18
|
import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
|
|
19
19
|
import { parseInputData, toZodSchema } from "better-auth/db";
|
|
20
20
|
import { HIDE_METADATA } from "@better-auth/core/api";
|
|
21
|
-
import { PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, authorizationCodeRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, getOAuth2Tokens } from "@better-auth/core/oauth2";
|
|
21
|
+
import { PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, authorizationCodeRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, getOAuth2Tokens, parseStringBoolean } from "@better-auth/core/oauth2";
|
|
22
22
|
import { isAPIError } from "@better-auth/core/utils/is-api-error";
|
|
23
23
|
import { appendQueryParams } from "@better-auth/core/utils/url";
|
|
24
24
|
import { deleteSessionCookie, setAccountCookie, setSessionCookie } from "better-auth/cookies";
|
|
25
25
|
import { additionalAuthorizationParamsSchema, generateState, handleOAuthUserInfo, parseState } from "better-auth/oauth2";
|
|
26
|
-
|
|
26
|
+
// src/constants.ts
|
|
27
27
|
/**
|
|
28
28
|
* SAML Constants
|
|
29
29
|
*
|
|
@@ -62,8 +62,25 @@ const DEFAULT_MAX_SAML_RESPONSE_SIZE = 262144;
|
|
|
62
62
|
* Protects against oversized metadata documents.
|
|
63
63
|
*/
|
|
64
64
|
const DEFAULT_MAX_SAML_METADATA_SIZE = 102400;
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* NameID format whose value is an email address. Other formats, such as
|
|
67
|
+
* persistent and transient, are opaque identifiers.
|
|
68
|
+
*
|
|
69
|
+
* @see https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf (§8.3.2)
|
|
70
|
+
*/
|
|
71
|
+
const SAML_EMAIL_NAME_ID_FORMAT = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
|
|
72
|
+
/**
|
|
73
|
+
* Attributes read for the email, in order, when `samlConfig.mapping.email` is
|
|
74
|
+
* not set and neither the `email` attribute nor an `emailAddress` NameID is
|
|
75
|
+
* present: the claim URI many IdPs send by default, and the `mail` attribute
|
|
76
|
+
* by name and by OID.
|
|
77
|
+
*/
|
|
78
|
+
const SAML_FALLBACK_EMAIL_ATTRIBUTES = [
|
|
79
|
+
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
|
|
80
|
+
"mail",
|
|
81
|
+
"urn:oid:0.9.2342.19200300.100.1.3"
|
|
82
|
+
];
|
|
83
|
+
// src/utils.ts
|
|
67
84
|
const unsafeSAMLRedirectPathPrefix = /^\/(?:\/|\\|%2f|%5c)/i;
|
|
68
85
|
const isSafeSAMLRedirectPath = (url) => url.startsWith("/") && !unsafeSAMLRedirectPathPrefix.test(url);
|
|
69
86
|
/**
|
|
@@ -95,16 +112,6 @@ const domainMatches = (searchDomain, domainList) => {
|
|
|
95
112
|
return domains.some((domain) => search === domain || search.endsWith(`.${domain}`));
|
|
96
113
|
};
|
|
97
114
|
/**
|
|
98
|
-
* Strictly parse a provider-supplied email-verification claim.
|
|
99
|
-
*
|
|
100
|
-
* OIDC userInfo, OIDC id-token, and SAML attribute values are frequently
|
|
101
|
-
* strings, so a loose `Boolean(value)` or truthy fallback treats the string
|
|
102
|
-
* `"false"` as verified. Only a boolean `true` or the exact string `"true"`
|
|
103
|
-
* count as verified; every other value, including `"false"`, `"0"`, `""`,
|
|
104
|
-
* numbers, arrays, and objects, is unverified.
|
|
105
|
-
*/
|
|
106
|
-
const parseProviderEmailVerified = (value) => value === true || value === "true";
|
|
107
|
-
/**
|
|
108
115
|
* Validates email domain against allowed domain(s).
|
|
109
116
|
* Supports comma-separated domains for multi-domain SSO.
|
|
110
117
|
*/
|
|
@@ -152,8 +159,7 @@ function maskClientId(clientId) {
|
|
|
152
159
|
if (clientId.length <= 4) return "****";
|
|
153
160
|
return `****${clientId.slice(-4)}`;
|
|
154
161
|
}
|
|
155
|
-
|
|
156
|
-
//#region src/linking/org-assignment.ts
|
|
162
|
+
// src/linking/org-assignment.ts
|
|
157
163
|
const VERIFIED_PROVIDER_PAGE_SIZE = 100;
|
|
158
164
|
function getEmailDomain(email) {
|
|
159
165
|
const parts = email.trim().toLowerCase().split("@");
|
|
@@ -296,8 +302,7 @@ async function assignOrganizationByDomain(ctx, options) {
|
|
|
296
302
|
...options
|
|
297
303
|
});
|
|
298
304
|
}
|
|
299
|
-
|
|
300
|
-
//#region src/oidc/types.ts
|
|
305
|
+
// src/oidc/types.ts
|
|
301
306
|
/**
|
|
302
307
|
* Custom error class for OIDC discovery failures.
|
|
303
308
|
* Can be caught and mapped to APIError at the edge.
|
|
@@ -322,8 +327,7 @@ const REQUIRED_DISCOVERY_FIELDS = [
|
|
|
322
327
|
"token_endpoint",
|
|
323
328
|
"jwks_uri"
|
|
324
329
|
];
|
|
325
|
-
|
|
326
|
-
//#region src/oidc/discovery.ts
|
|
330
|
+
// src/oidc/discovery.ts
|
|
327
331
|
/**
|
|
328
332
|
* OIDC Discovery Pipeline
|
|
329
333
|
*
|
|
@@ -789,8 +793,7 @@ async function ensureRuntimeDiscovery(config, issuer, isTrustedOrigin) {
|
|
|
789
793
|
await assertServerFetchedOIDCEndpointsAllowed(resolved, isTrustedOrigin);
|
|
790
794
|
return resolved;
|
|
791
795
|
}
|
|
792
|
-
|
|
793
|
-
//#region src/oidc/errors.ts
|
|
796
|
+
// src/oidc/errors.ts
|
|
794
797
|
/**
|
|
795
798
|
* OIDC Discovery Error Mapping
|
|
796
799
|
*
|
|
@@ -870,8 +873,7 @@ function mapDiscoveryErrorToAPIError(error) {
|
|
|
870
873
|
});
|
|
871
874
|
}
|
|
872
875
|
}
|
|
873
|
-
|
|
874
|
-
//#region src/provider-reference.ts
|
|
876
|
+
// src/provider-reference.ts
|
|
875
877
|
const SSO_PROVIDER_STATE_KEY = "ssoProviderReference";
|
|
876
878
|
function serializeCanonical(value) {
|
|
877
879
|
if (value === null) return "null";
|
|
@@ -966,8 +968,7 @@ async function isCurrentSSOProviderReference(provider, reference) {
|
|
|
966
968
|
if (source.type === "persisted" && (reference.source.type !== "persisted" || source.recordId !== reference.source.recordId)) return false;
|
|
967
969
|
return reference.authenticationConfigurationFingerprint === await computeProviderAuthenticationFingerprint(provider);
|
|
968
970
|
}
|
|
969
|
-
|
|
970
|
-
//#region src/saml/parser.ts
|
|
971
|
+
// src/saml/parser.ts
|
|
971
972
|
const xmlParser = new XMLParser({
|
|
972
973
|
ignoreAttributes: false,
|
|
973
974
|
attributeNamePrefix: "@_",
|
|
@@ -1000,8 +1001,7 @@ function countAllNodes(obj, nodeName) {
|
|
|
1000
1001
|
else if (typeof value === "object" && value !== null) count += countAllNodes(value, nodeName);
|
|
1001
1002
|
return count;
|
|
1002
1003
|
}
|
|
1003
|
-
|
|
1004
|
-
//#region src/saml/algorithms.ts
|
|
1004
|
+
// src/saml/algorithms.ts
|
|
1005
1005
|
const SignatureAlgorithm = {
|
|
1006
1006
|
RSA_SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
|
1007
1007
|
RSA_SHA256: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
|
@@ -1178,8 +1178,7 @@ function validateConfigAlgorithms(config, options = {}) {
|
|
|
1178
1178
|
}
|
|
1179
1179
|
}
|
|
1180
1180
|
const saml = typeof samlifyNamespace.SPMetadata === "function" && typeof samlifyNamespace.setSchemaValidator === "function" ? samlifyNamespace : samlifyDefault ?? samlifyNamespace;
|
|
1181
|
-
|
|
1182
|
-
//#region src/saml/assertions.ts
|
|
1181
|
+
// src/saml/assertions.ts
|
|
1183
1182
|
const SAML_ASSERTION_NAMESPACE = "urn:oasis:names:tc:SAML:2.0:assertion";
|
|
1184
1183
|
const SAML_PROTOCOL_NAMESPACE = "urn:oasis:names:tc:SAML:2.0:protocol";
|
|
1185
1184
|
const XML_SIGNATURE_NAMESPACE = "http://www.w3.org/2000/09/xmldsig#";
|
|
@@ -1255,8 +1254,7 @@ function verifySAMLAssertionSignature(samlContent, verificationOptions) {
|
|
|
1255
1254
|
throw assertionSignatureFailure();
|
|
1256
1255
|
}
|
|
1257
1256
|
}
|
|
1258
|
-
|
|
1259
|
-
//#region src/saml/error-codes.ts
|
|
1257
|
+
// src/saml/error-codes.ts
|
|
1260
1258
|
const SAML_ERROR_CODES = defineErrorCodes({
|
|
1261
1259
|
SINGLE_LOGOUT_NOT_ENABLED: "Single Logout is not enabled",
|
|
1262
1260
|
INVALID_LOGOUT_RESPONSE: "Invalid LogoutResponse",
|
|
@@ -1266,8 +1264,7 @@ const SAML_ERROR_CODES = defineErrorCodes({
|
|
|
1266
1264
|
SAML_PROVIDER_NOT_FOUND: "SAML provider not found",
|
|
1267
1265
|
CERT_SOURCE_MISSING: "samlConfig requires either a signing certificate (cert or idpMetadata.cert) or an idpMetadata.metadata XML document."
|
|
1268
1266
|
});
|
|
1269
|
-
|
|
1270
|
-
//#region src/saml/cert.ts
|
|
1267
|
+
// src/saml/cert.ts
|
|
1271
1268
|
/**
|
|
1272
1269
|
* IdP signing-certificate rules for SAML configs. Centralized so the runtime
|
|
1273
1270
|
* verification path (`createIdP`), the sanitizer (`getSSOProvider` and
|
|
@@ -1296,8 +1293,7 @@ function validateCertSources(config) {
|
|
|
1296
1293
|
const hasExplicitCert = config.idpMetadata?.cert !== void 0 || config.cert !== void 0;
|
|
1297
1294
|
if (!hasMetadataXml && !hasExplicitCert) throw APIError.from("BAD_REQUEST", SAML_ERROR_CODES.CERT_SOURCE_MISSING);
|
|
1298
1295
|
}
|
|
1299
|
-
|
|
1300
|
-
//#region src/saml/response-binding.ts
|
|
1296
|
+
// src/saml/response-binding.ts
|
|
1301
1297
|
const SAML_HTTP_POST_BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";
|
|
1302
1298
|
const SAML_BEARER_CONFIRMATION_METHOD = "urn:oasis:names:tc:SAML:2.0:cm:bearer";
|
|
1303
1299
|
const SAML_METADATA_NAMESPACE = "urn:oasis:names:tc:SAML:2.0:metadata";
|
|
@@ -1437,6 +1433,15 @@ function validateAudienceRestrictions(assertion, expectedAudiences) {
|
|
|
1437
1433
|
code: "SAML_AUDIENCE_MISMATCH"
|
|
1438
1434
|
});
|
|
1439
1435
|
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Reads the user's email from a SAML assertion: the mapped attribute (default
|
|
1438
|
+
* `email`), then a NameID whose Format is `emailAddress`, then, without a
|
|
1439
|
+
* mapped attribute, the fallback email attributes. Other NameIDs are opaque or
|
|
1440
|
+
* unspecified, so they never become the email.
|
|
1441
|
+
*/
|
|
1442
|
+
function readSAMLEmail(attr, options) {
|
|
1443
|
+
return attr(options.mappedAttribute || "email") || (options.nameIDFormat === "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" ? options.nameID : void 0) || (options.mappedAttribute ? void 0 : SAML_FALLBACK_EMAIL_ATTRIBUTES.map(attr).find(Boolean));
|
|
1444
|
+
}
|
|
1440
1445
|
function getBearerSubjectConfirmationData(assertion) {
|
|
1441
1446
|
return toNodeArray(firstNode(assertion.Subject)?.SubjectConfirmation).filter((confirmation) => confirmation["@_Method"] === SAML_BEARER_CONFIRMATION_METHOD).map((confirmation) => firstNode(confirmation.SubjectConfirmationData)).filter((data) => !!data);
|
|
1442
1447
|
}
|
|
@@ -1464,6 +1469,11 @@ function validateResponseDestination(response, expectedRecipients) {
|
|
|
1464
1469
|
code: "SAML_DESTINATION_MISMATCH"
|
|
1465
1470
|
});
|
|
1466
1471
|
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Validates that the assertion is bound to this Service Provider, then returns
|
|
1474
|
+
* the NameID format so user attribute extraction need not parse the response
|
|
1475
|
+
* again.
|
|
1476
|
+
*/
|
|
1467
1477
|
function validateSAMLResponseBinding(samlContent, options) {
|
|
1468
1478
|
const expectedAudiences = toStringSet(options.expectedAudiences);
|
|
1469
1479
|
const expectedRecipients = toStringSet(options.expectedRecipients);
|
|
@@ -1473,9 +1483,10 @@ function validateSAMLResponseBinding(samlContent, options) {
|
|
|
1473
1483
|
validateAudienceRestrictions(assertion, expectedAudiences);
|
|
1474
1484
|
validateBearerRecipient(assertion, expectedRecipients);
|
|
1475
1485
|
validateResponseDestination(response, expectedRecipients);
|
|
1486
|
+
const nameIDFormat = firstNode(firstNode(assertion.Subject)?.NameID)?.["@_Format"];
|
|
1487
|
+
return { nameIDFormat: typeof nameIDFormat === "string" && nameIDFormat ? nameIDFormat : void 0 };
|
|
1476
1488
|
}
|
|
1477
|
-
|
|
1478
|
-
//#region src/saml/response-validation.ts
|
|
1489
|
+
// src/saml/response-validation.ts
|
|
1479
1490
|
function errorRedirectUrl(base, error, description) {
|
|
1480
1491
|
try {
|
|
1481
1492
|
const url = new URL(base);
|
|
@@ -1572,9 +1583,66 @@ function validateAudience(c, ctx) {
|
|
|
1572
1583
|
throw c.redirect(errorRedirectUrl(ctx.redirectUrl, "invalid_saml_response", "Audience mismatch"));
|
|
1573
1584
|
}
|
|
1574
1585
|
}
|
|
1575
|
-
|
|
1576
|
-
//#region src/user-resolution.ts
|
|
1586
|
+
// src/user-resolution.ts
|
|
1577
1587
|
const SSO_AUTHENTICATION_FAILURE = Symbol("SSO_AUTHENTICATION_FAILURE");
|
|
1588
|
+
/**
|
|
1589
|
+
* Applies `mapProfileToUser` to the profile read from the provider. Only the
|
|
1590
|
+
* documented user fields are taken from the callback's result.
|
|
1591
|
+
*/
|
|
1592
|
+
async function mapSSOProfile(options, input, logger) {
|
|
1593
|
+
if (!options?.mapProfileToUser) return input.providerUser;
|
|
1594
|
+
let mappedUser;
|
|
1595
|
+
try {
|
|
1596
|
+
const result = await options.mapProfileToUser(structuredClone(input));
|
|
1597
|
+
if (isRecord$1(result)) {
|
|
1598
|
+
const { email, name, image } = result;
|
|
1599
|
+
mappedUser = {
|
|
1600
|
+
email,
|
|
1601
|
+
name,
|
|
1602
|
+
image
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
logFailure(logger, "SSO profile mapping failed", {
|
|
1607
|
+
providerId: input.providerId,
|
|
1608
|
+
protocol: input.protocol,
|
|
1609
|
+
error
|
|
1610
|
+
});
|
|
1611
|
+
throw profileMappingFailure();
|
|
1612
|
+
}
|
|
1613
|
+
if (!isSSOMappedUser(mappedUser) || !isAcceptedEmail(mappedUser.email, input.providerUser.email)) {
|
|
1614
|
+
logFailure(logger, "SSO profile mapper returned an invalid profile", {
|
|
1615
|
+
providerId: input.providerId,
|
|
1616
|
+
protocol: input.protocol
|
|
1617
|
+
});
|
|
1618
|
+
throw profileMappingFailure();
|
|
1619
|
+
}
|
|
1620
|
+
const { email, name, image } = mappedUser;
|
|
1621
|
+
const providerUser = input.providerUser;
|
|
1622
|
+
const mappedEmail = email === null ? void 0 : email ?? providerUser.email;
|
|
1623
|
+
return {
|
|
1624
|
+
email: mappedEmail,
|
|
1625
|
+
emailVerified: providerUser.emailVerified && mappedEmail?.toLowerCase() === providerUser.email?.toLowerCase(),
|
|
1626
|
+
name: name ?? providerUser.name,
|
|
1627
|
+
image: image ?? providerUser.image
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
function isOptionalString(value) {
|
|
1631
|
+
return value === void 0 || typeof value === "string";
|
|
1632
|
+
}
|
|
1633
|
+
function isSSOMappedUser(value) {
|
|
1634
|
+
return isRecord$1(value) && (value.email === null || isOptionalString(value.email)) && isOptionalString(value.name) && isOptionalString(value.image);
|
|
1635
|
+
}
|
|
1636
|
+
/** A changed email must be an address; the provider's own email passes as sent. */
|
|
1637
|
+
function isAcceptedEmail(email, providerEmail) {
|
|
1638
|
+
return typeof email !== "string" || email.toLowerCase() === providerEmail?.toLowerCase() || z.email().safeParse(email).success;
|
|
1639
|
+
}
|
|
1640
|
+
function profileMappingFailure() {
|
|
1641
|
+
return new APIError("INTERNAL_SERVER_ERROR", {
|
|
1642
|
+
code: "SSO_PROFILE_MAPPING_FAILED",
|
|
1643
|
+
message: "Unable to map the SSO profile"
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1578
1646
|
function isRecord$1(value) {
|
|
1579
1647
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1580
1648
|
}
|
|
@@ -1587,9 +1655,9 @@ function isSSOUserResolution(value) {
|
|
|
1587
1655
|
if (value.action === "link") return isNonEmptyString(value.userId) && (value.profile === "preserve" || value.profile === "update");
|
|
1588
1656
|
return value.action === "reject" && isNonEmptyString(value.code) && (value.message === void 0 || typeof value.message === "string");
|
|
1589
1657
|
}
|
|
1590
|
-
function logFailure(logger, message) {
|
|
1658
|
+
function logFailure(logger, message, details) {
|
|
1591
1659
|
try {
|
|
1592
|
-
logger.error(message);
|
|
1660
|
+
logger.error(message, ...details ? [details] : []);
|
|
1593
1661
|
} catch {}
|
|
1594
1662
|
}
|
|
1595
1663
|
function resolutionFailure() {
|
|
@@ -1659,8 +1727,7 @@ function getFailedSSOAuthenticationResult(error) {
|
|
|
1659
1727
|
if (!(error instanceof Error) || !(SSO_AUTHENTICATION_FAILURE in error) || error[SSO_AUTHENTICATION_FAILURE] !== true || !("result" in error)) return;
|
|
1660
1728
|
return error.result;
|
|
1661
1729
|
}
|
|
1662
|
-
|
|
1663
|
-
//#region src/routes/helpers.ts
|
|
1730
|
+
// src/routes/helpers.ts
|
|
1664
1731
|
/**
|
|
1665
1732
|
* Same as `normalizePem`, but applied across the resolved list of IdP signing
|
|
1666
1733
|
* certificates so multi-cert rotation configs survive the line-trim step.
|
|
@@ -1824,8 +1891,7 @@ function createSAMLPostForm(action, samlParam, samlValue, relayState) {
|
|
|
1824
1891
|
const html = `<!DOCTYPE html><html><body onload="document.forms[0].submit();"><form method="POST" action="${safeAction}"><input type="hidden" name="${safeSamlParam}" value="${safeSamlValue}" />${safeRelayState ? `<input type="hidden" name="RelayState" value="${safeRelayState}" />` : ""}<noscript><input type="submit" value="Continue" /></noscript></form></body></html>`;
|
|
1825
1892
|
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
1826
1893
|
}
|
|
1827
|
-
|
|
1828
|
-
//#region src/routes/schemas.ts
|
|
1894
|
+
// src/routes/schemas.ts
|
|
1829
1895
|
const absoluteUrlSchema = z.url();
|
|
1830
1896
|
const samlRedirectUrlSchema = z.string().refine((url) => isSafeSAMLRedirectPath(url) || absoluteUrlSchema.safeParse(url).success, { message: "Expected an absolute URL or a relative path starting with /" });
|
|
1831
1897
|
function getSSOProviderAdditionalFields$1(options) {
|
|
@@ -1968,8 +2034,7 @@ function getUpdateSSOProviderBodySchema(options) {
|
|
|
1968
2034
|
...getSSOProviderAdditionalFieldsSchema(options).partial().shape
|
|
1969
2035
|
});
|
|
1970
2036
|
}
|
|
1971
|
-
|
|
1972
|
-
//#region src/routes/providers.ts
|
|
2037
|
+
// src/routes/providers.ts
|
|
1973
2038
|
const ADMIN_ROLES = ["owner", "admin"];
|
|
1974
2039
|
const OIDC_IDENTITY_BOUNDARY_FIELDS = [
|
|
1975
2040
|
"authorizationEndpoint",
|
|
@@ -2520,8 +2585,7 @@ const deleteSSOProvider = (options) => {
|
|
|
2520
2585
|
return ctx.json({ success: true });
|
|
2521
2586
|
});
|
|
2522
2587
|
};
|
|
2523
|
-
|
|
2524
|
-
//#region src/routes/domain-verification.ts
|
|
2588
|
+
// src/routes/domain-verification.ts
|
|
2525
2589
|
const DNS_LABEL_MAX_LENGTH = 63;
|
|
2526
2590
|
const DEFAULT_TOKEN_PREFIX = "better-auth-token";
|
|
2527
2591
|
const domainVerificationBodySchema = z.object({ providerId: z.string() });
|
|
@@ -2650,8 +2714,7 @@ const verifyDomain = (options) => {
|
|
|
2650
2714
|
ctx.setStatus(204);
|
|
2651
2715
|
});
|
|
2652
2716
|
};
|
|
2653
|
-
|
|
2654
|
-
//#region src/saml-state.ts
|
|
2717
|
+
// src/saml-state.ts
|
|
2655
2718
|
async function generateRelayState(c, link, providerReference) {
|
|
2656
2719
|
const callbackURL = c.body.callbackURL;
|
|
2657
2720
|
if (!callbackURL) throw new APIError("BAD_REQUEST", { message: "callbackURL is required" });
|
|
@@ -2701,8 +2764,7 @@ async function parseRelayState(c) {
|
|
|
2701
2764
|
if (!parsedData.errorURL) parsedData.errorURL = errorURL;
|
|
2702
2765
|
return parsedData;
|
|
2703
2766
|
}
|
|
2704
|
-
|
|
2705
|
-
//#region src/saml/timestamp.ts
|
|
2767
|
+
// src/saml/timestamp.ts
|
|
2706
2768
|
/**
|
|
2707
2769
|
* Validates SAML assertion timestamp conditions (NotBefore/NotOnOrAfter).
|
|
2708
2770
|
* Prevents acceptance of expired or future-dated assertions.
|
|
@@ -2742,8 +2804,7 @@ function validateSAMLTimestamp(conditions, options = {}) {
|
|
|
2742
2804
|
});
|
|
2743
2805
|
}
|
|
2744
2806
|
}
|
|
2745
|
-
|
|
2746
|
-
//#region src/routes/saml-pipeline.ts
|
|
2807
|
+
// src/routes/saml-pipeline.ts
|
|
2747
2808
|
function isSameSSOProviderReference(left, right) {
|
|
2748
2809
|
if (left.providerId !== right.providerId || left.authenticationConfigurationFingerprint !== right.authenticationConfigurationFingerprint || left.source.type !== right.source.type) return false;
|
|
2749
2810
|
return left.source.type === "configured" ? true : right.source.type === "persisted" && left.source.recordId === right.source.recordId;
|
|
@@ -2870,6 +2931,12 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
2870
2931
|
const samlRedirectUrl = getSafeRedirectUrl(redirectCandidates, currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
2871
2932
|
const samlErrorRedirectUrl = getSafeRedirectUrl([relayState?.errorURL, samlRedirectUrl], currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
2872
2933
|
params.onErrorRedirectResolved?.(samlErrorRedirectUrl);
|
|
2934
|
+
const redirectOnCodedAPIError = (e) => {
|
|
2935
|
+
if (isAPIError(e) && e.body?.code) throw ctx.redirect(buildSAMLRedirectUrl(samlErrorRedirectUrl, {
|
|
2936
|
+
error: e.body.code,
|
|
2937
|
+
...e.body.message ? { error_description: e.body.message } : {}
|
|
2938
|
+
}));
|
|
2939
|
+
};
|
|
2873
2940
|
const stateProviderReference = parseSSOProviderReference(relayState?.serverContext?.[SSO_PROVIDER_STATE_KEY]);
|
|
2874
2941
|
validateSingleAssertion(SAMLResponse);
|
|
2875
2942
|
let parsedResponse;
|
|
@@ -2895,13 +2962,14 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
2895
2962
|
const expectedRecipients = getExpectedSAMLRecipients(parsedSamlConfig, ctx.context.baseURL, providerId, currentCallbackPath);
|
|
2896
2963
|
const serviceProviderPolicy = deriveSAMLServiceProviderPolicy(parsedSamlConfig);
|
|
2897
2964
|
let samlBindingContent;
|
|
2965
|
+
let boundAssertion;
|
|
2898
2966
|
try {
|
|
2899
2967
|
samlBindingContent = await getSAMLResponseBindingContent(sp, samlContent);
|
|
2900
2968
|
if (serviceProviderPolicy.wantAssertionsSigned) verifySAMLAssertionSignature(samlBindingContent, {
|
|
2901
2969
|
metadata: idp.entityMeta,
|
|
2902
2970
|
signatureAlgorithm: idp.entitySetting.requestSignatureAlgorithm
|
|
2903
2971
|
});
|
|
2904
|
-
validateSAMLResponseBinding(samlBindingContent, {
|
|
2972
|
+
boundAssertion = validateSAMLResponseBinding(samlBindingContent, {
|
|
2905
2973
|
expectedAudiences,
|
|
2906
2974
|
expectedRecipients
|
|
2907
2975
|
});
|
|
@@ -2987,6 +3055,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
2987
3055
|
for (const [name, value] of Object.entries(attributes)) if (typeof value === "string") providerAttributes[name] = value;
|
|
2988
3056
|
else if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) providerAttributes[name] = value;
|
|
2989
3057
|
const mapping = parsedSamlConfig.mapping ?? {};
|
|
3058
|
+
const { nameIDFormat } = boundAssertion;
|
|
2990
3059
|
const attr = (key) => {
|
|
2991
3060
|
const value = attributes[key];
|
|
2992
3061
|
return Array.isArray(value) ? value[0] : value;
|
|
@@ -2994,34 +3063,63 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
2994
3063
|
const userInfo = {
|
|
2995
3064
|
...Object.fromEntries(Object.entries(mapping.extraFields || {}).map(([key, value]) => [key, attributes[value]])),
|
|
2996
3065
|
id: extract.nameID,
|
|
2997
|
-
email: (attr
|
|
2998
|
-
|
|
2999
|
-
|
|
3066
|
+
email: readSAMLEmail(attr, {
|
|
3067
|
+
mappedAttribute: mapping.email,
|
|
3068
|
+
nameID: extract.nameID,
|
|
3069
|
+
nameIDFormat
|
|
3070
|
+
}),
|
|
3071
|
+
name: [attr(mapping.firstName || "givenName"), attr(mapping.lastName || "surname")].filter(Boolean).join(" ") || attr(mapping.name || "displayName") || void 0,
|
|
3072
|
+
emailVerified: options?.trustEmailVerified && mapping.emailVerified ? parseStringBoolean(attr(mapping.emailVerified)) ?? false : false
|
|
3000
3073
|
};
|
|
3001
|
-
|
|
3002
|
-
|
|
3074
|
+
const providerUserAttributes = Object.fromEntries(Object.entries(userInfo).filter(([key]) => key !== "id"));
|
|
3075
|
+
const missingUserInfo = (hasNameId, providerSentEmail = false) => {
|
|
3076
|
+
ctx.context.logger.error(providerSentEmail ? "mapProfileToUser refused the email the SAML provider sent" : hasNameId ? `SAML sign-in has no email. Send an email attribute, or a NameID with Format ${SAML_EMAIL_NAME_ID_FORMAT}, from the IdP; set samlConfig.mapping.email; or return an email from mapProfileToUser.` : "SAML response has no NameID", {
|
|
3003
3077
|
providerId,
|
|
3004
3078
|
attributeNames: Object.keys(attributes),
|
|
3005
|
-
|
|
3006
|
-
|
|
3079
|
+
nameIDFormat
|
|
3080
|
+
});
|
|
3081
|
+
return new APIError("BAD_REQUEST", {
|
|
3082
|
+
code: "MISSING_USER_INFO",
|
|
3083
|
+
message: "Unable to extract user ID or email from SAML response. Map an email attribute, or create a placeholder with `mapProfileToUser`."
|
|
3007
3084
|
});
|
|
3008
|
-
throw new APIError("BAD_REQUEST", { message: "Unable to extract user ID or email from SAML response" });
|
|
3009
|
-
}
|
|
3010
|
-
const providerUserAttributes = Object.fromEntries(Object.entries(userInfo).filter(([key]) => key !== "id"));
|
|
3011
|
-
const providerUser = {
|
|
3012
|
-
...providerUserAttributes,
|
|
3013
|
-
email: userInfo.email,
|
|
3014
|
-
name: userInfo.name || userInfo.email,
|
|
3015
|
-
image: typeof providerUserAttributes.image === "string" ? providerUserAttributes.image : void 0,
|
|
3016
|
-
emailVerified: userInfo.emailVerified
|
|
3017
3085
|
};
|
|
3086
|
+
if (!userInfo.id) throw missingUserInfo(false);
|
|
3018
3087
|
const accountKey = {
|
|
3019
3088
|
issuer,
|
|
3020
3089
|
accountId: userInfo.id
|
|
3021
3090
|
};
|
|
3022
|
-
const
|
|
3091
|
+
const mappedUser = await mapSSOProfile(options, {
|
|
3092
|
+
protocol: "saml",
|
|
3093
|
+
providerId,
|
|
3094
|
+
accountKey,
|
|
3095
|
+
providerUser: {
|
|
3096
|
+
email: userInfo.email,
|
|
3097
|
+
emailVerified: userInfo.emailVerified,
|
|
3098
|
+
name: userInfo.name,
|
|
3099
|
+
image: typeof providerUserAttributes.image === "string" ? providerUserAttributes.image : void 0
|
|
3100
|
+
},
|
|
3101
|
+
providerAttributes,
|
|
3102
|
+
nameID: userInfo.id,
|
|
3103
|
+
nameIDFormat
|
|
3104
|
+
}, ctx.context.logger).catch((e) => {
|
|
3105
|
+
redirectOnCodedAPIError(e);
|
|
3106
|
+
throw e;
|
|
3107
|
+
});
|
|
3108
|
+
const email = mappedUser.email?.toLowerCase();
|
|
3109
|
+
if (!email) throw missingUserInfo(true, Boolean(userInfo.email));
|
|
3110
|
+
const providerUser = {
|
|
3111
|
+
...providerUserAttributes,
|
|
3112
|
+
email,
|
|
3113
|
+
name: mappedUser.name || email,
|
|
3114
|
+
image: mappedUser.image,
|
|
3115
|
+
emailVerified: mappedUser.emailVerified
|
|
3116
|
+
};
|
|
3117
|
+
const mappedUserInfo = {
|
|
3118
|
+
...providerUser,
|
|
3119
|
+
id: accountKey.accountId
|
|
3120
|
+
};
|
|
3121
|
+
const isTrustedProvider = "domainVerified" in provider && !!provider.domainVerified && validateEmailDomain(email, provider.domain);
|
|
3023
3122
|
const callbackUrl = redirectCandidates.some(Boolean) ? samlRedirectUrl : ctx.context.baseURL;
|
|
3024
|
-
const errorUrl = samlErrorRedirectUrl;
|
|
3025
3123
|
let result;
|
|
3026
3124
|
try {
|
|
3027
3125
|
if (options?.resolveUser) {
|
|
@@ -3049,10 +3147,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
3049
3147
|
...resolution.message === void 0 ? {} : { message: resolution.message }
|
|
3050
3148
|
});
|
|
3051
3149
|
const authentication = await handleOAuthUserInfo(ctx, {
|
|
3052
|
-
userInfo:
|
|
3053
|
-
...providerUser,
|
|
3054
|
-
id: userInfo.id
|
|
3055
|
-
},
|
|
3150
|
+
userInfo: mappedUserInfo,
|
|
3056
3151
|
account: {
|
|
3057
3152
|
providerId,
|
|
3058
3153
|
accountId: accountKey.accountId,
|
|
@@ -3084,17 +3179,16 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
3084
3179
|
} catch (e) {
|
|
3085
3180
|
const failedAuthentication = getFailedSSOAuthenticationResult(e);
|
|
3086
3181
|
if (failedAuthentication) result = failedAuthentication;
|
|
3087
|
-
else
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
}
|
|
3091
|
-
else throw e;
|
|
3182
|
+
else {
|
|
3183
|
+
redirectOnCodedAPIError(e);
|
|
3184
|
+
throw e;
|
|
3185
|
+
}
|
|
3092
3186
|
}
|
|
3093
3187
|
if (result.error) throw ctx.redirect(buildSAMLRedirectUrl(callbackUrl, { error: result.error.split(" ").join("_") }));
|
|
3094
3188
|
const { session, user } = result.data;
|
|
3095
3189
|
if (options?.provisionUser && (result.isRegister || options.provisionUserOnEveryLogin)) await options.provisionUser({
|
|
3096
3190
|
user,
|
|
3097
|
-
userInfo,
|
|
3191
|
+
userInfo: mappedUserInfo,
|
|
3098
3192
|
provider
|
|
3099
3193
|
});
|
|
3100
3194
|
await assignOrganizationFromProvider(ctx, {
|
|
@@ -3102,9 +3196,9 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
3102
3196
|
profile: {
|
|
3103
3197
|
providerType: "saml",
|
|
3104
3198
|
providerId,
|
|
3105
|
-
accountId:
|
|
3106
|
-
email
|
|
3107
|
-
emailVerified:
|
|
3199
|
+
accountId: accountKey.accountId,
|
|
3200
|
+
email,
|
|
3201
|
+
emailVerified: providerUser.emailVerified,
|
|
3108
3202
|
rawAttributes: attributes
|
|
3109
3203
|
},
|
|
3110
3204
|
provider,
|
|
@@ -3137,8 +3231,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
3137
3231
|
}
|
|
3138
3232
|
return samlRedirectUrl;
|
|
3139
3233
|
}
|
|
3140
|
-
|
|
3141
|
-
//#region src/routes/sso.ts
|
|
3234
|
+
// src/routes/sso.ts
|
|
3142
3235
|
const BUILT_IN_ACCOUNT_PROVIDER_IDS = [
|
|
3143
3236
|
"credential",
|
|
3144
3237
|
"email-otp",
|
|
@@ -3936,7 +4029,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
3936
4029
|
...Object.fromEntries(Object.entries(mapping.extraFields || {}).map(([key, value]) => [key, rawUserInfo[value]])),
|
|
3937
4030
|
id: readStringClaim(rawUserInfo, "sub"),
|
|
3938
4031
|
email: readEmailClaim(rawUserInfo),
|
|
3939
|
-
emailVerified: options?.trustEmailVerified ?
|
|
4032
|
+
emailVerified: options?.trustEmailVerified ? parseStringBoolean(rawUserInfo[mapping.emailVerified || "email_verified"]) ?? false : false,
|
|
3940
4033
|
name: readStringClaim(rawUserInfo, mapping.name || "name"),
|
|
3941
4034
|
image: readStringClaim(rawUserInfo, mapping.image || "picture")
|
|
3942
4035
|
};
|
|
@@ -3947,24 +4040,47 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
3947
4040
|
...Object.fromEntries(Object.entries(mapping.extraFields || {}).map(([key, value]) => [key, idToken[value]])),
|
|
3948
4041
|
id: idToken.sub,
|
|
3949
4042
|
email: readEmailClaim(idToken),
|
|
3950
|
-
emailVerified: options?.trustEmailVerified ?
|
|
4043
|
+
emailVerified: options?.trustEmailVerified ? parseStringBoolean(idToken[mapping.emailVerified || "email_verified"]) ?? false : false,
|
|
3951
4044
|
name: readStringClaim(idToken, mapping.name || "name"),
|
|
3952
4045
|
image: readStringClaim(idToken, mapping.image || "picture")
|
|
3953
4046
|
};
|
|
3954
4047
|
} else return redirectOIDCError("invalid_provider", "user_info_endpoint_not_found");
|
|
3955
|
-
if (!userInfo.
|
|
3956
|
-
const
|
|
3957
|
-
|
|
4048
|
+
if (!userInfo.id) return redirectOIDCError("invalid_provider", "missing_user_info");
|
|
4049
|
+
const accountKey = {
|
|
4050
|
+
issuer: verifiedIdToken && readStringClaim(verifiedIdToken.payload, "iss") || provider.issuer,
|
|
4051
|
+
accountId: userInfo.id
|
|
4052
|
+
};
|
|
4053
|
+
const mappedUser = await mapSSOProfile(options, {
|
|
4054
|
+
protocol: "oidc",
|
|
4055
|
+
providerId,
|
|
4056
|
+
accountKey,
|
|
4057
|
+
providerUser: {
|
|
4058
|
+
email: userInfo.email,
|
|
4059
|
+
emailVerified: userInfo.emailVerified === true,
|
|
4060
|
+
name: userInfo.name,
|
|
4061
|
+
image: userInfo.image
|
|
4062
|
+
},
|
|
4063
|
+
providerClaims: rawProfile ?? {},
|
|
4064
|
+
verifiedIdTokenClaims: verifiedIdToken?.payload ?? {}
|
|
4065
|
+
}, ctx.context.logger).catch((e) => {
|
|
4066
|
+
if (isAPIError(e) && e.body?.code) redirectOIDCError(e.body.code, e.body.message);
|
|
4067
|
+
throw e;
|
|
4068
|
+
});
|
|
4069
|
+
if (!mappedUser.email) {
|
|
4070
|
+
ctx.context.logger.error("OIDC provider returned no email. Map an email claim, or create a placeholder with `mapProfileToUser`.", { providerId });
|
|
4071
|
+
return redirectOIDCError("invalid_provider", "missing_user_info");
|
|
4072
|
+
}
|
|
4073
|
+
const userInfoEmail = mappedUser.email;
|
|
3958
4074
|
const providerUser = {
|
|
3959
4075
|
...Object.fromEntries(Object.entries(userInfo).filter(([key]) => key !== "id")),
|
|
3960
4076
|
email: userInfoEmail,
|
|
3961
|
-
name:
|
|
3962
|
-
image:
|
|
3963
|
-
emailVerified:
|
|
4077
|
+
name: mappedUser.name ?? "",
|
|
4078
|
+
image: mappedUser.image,
|
|
4079
|
+
emailVerified: mappedUser.emailVerified
|
|
3964
4080
|
};
|
|
3965
|
-
const
|
|
3966
|
-
|
|
3967
|
-
|
|
4081
|
+
const mappedUserInfo = {
|
|
4082
|
+
...providerUser,
|
|
4083
|
+
id: accountKey.accountId
|
|
3968
4084
|
};
|
|
3969
4085
|
const isTrustedProvider = "domainVerified" in provider && provider.domainVerified === true && validateEmailDomain(userInfoEmail, provider.domain);
|
|
3970
4086
|
let linked;
|
|
@@ -3995,19 +4111,12 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
3995
4111
|
...resolution.message === void 0 ? {} : { message: resolution.message }
|
|
3996
4112
|
});
|
|
3997
4113
|
const authentication = await handleOAuthUserInfo(ctx, {
|
|
3998
|
-
userInfo:
|
|
3999
|
-
...providerUser,
|
|
4000
|
-
email: providerUser.email,
|
|
4001
|
-
name: providerUser.name,
|
|
4002
|
-
id: userInfoId,
|
|
4003
|
-
image: providerUser.image,
|
|
4004
|
-
emailVerified: providerUser.emailVerified
|
|
4005
|
-
},
|
|
4114
|
+
userInfo: mappedUserInfo,
|
|
4006
4115
|
account: {
|
|
4007
4116
|
idToken: tokenResponse.idToken,
|
|
4008
4117
|
accessToken: tokenResponse.accessToken,
|
|
4009
4118
|
refreshToken: tokenResponse.refreshToken,
|
|
4010
|
-
accountId:
|
|
4119
|
+
accountId: accountKey.accountId,
|
|
4011
4120
|
providerId: provider.providerId,
|
|
4012
4121
|
accessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,
|
|
4013
4122
|
refreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,
|
|
@@ -4046,7 +4155,7 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
4046
4155
|
const { session, user } = linked.data;
|
|
4047
4156
|
if (options?.provisionUser && (linked.isRegister || options.provisionUserOnEveryLogin)) await options.provisionUser({
|
|
4048
4157
|
user,
|
|
4049
|
-
userInfo,
|
|
4158
|
+
userInfo: mappedUserInfo,
|
|
4050
4159
|
token: tokenResponse,
|
|
4051
4160
|
provider
|
|
4052
4161
|
});
|
|
@@ -4055,10 +4164,10 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
4055
4164
|
profile: {
|
|
4056
4165
|
providerType: "oidc",
|
|
4057
4166
|
providerId: provider.providerId,
|
|
4058
|
-
accountId:
|
|
4167
|
+
accountId: accountKey.accountId,
|
|
4059
4168
|
email: userInfoEmail,
|
|
4060
|
-
emailVerified:
|
|
4061
|
-
rawAttributes:
|
|
4169
|
+
emailVerified: providerUser.emailVerified,
|
|
4170
|
+
rawAttributes: mappedUserInfo
|
|
4062
4171
|
},
|
|
4063
4172
|
provider,
|
|
4064
4173
|
token: tokenResponse,
|
|
@@ -4456,8 +4565,7 @@ const initiateSLO = (options) => {
|
|
|
4456
4565
|
throw ctx.redirect(logoutRequest.context);
|
|
4457
4566
|
});
|
|
4458
4567
|
};
|
|
4459
|
-
|
|
4460
|
-
//#region src/index.ts
|
|
4568
|
+
// src/index.ts
|
|
4461
4569
|
saml.setSchemaValidator({ async validate(xml) {
|
|
4462
4570
|
if (XMLValidator.validate(xml, { allowBooleanAttributes: true }) === true) return "SUCCESS_VALIDATE_XML";
|
|
4463
4571
|
throw "ERR_INVALID_XML";
|
|
@@ -4677,5 +4785,4 @@ function sso(options) {
|
|
|
4677
4785
|
options
|
|
4678
4786
|
};
|
|
4679
4787
|
}
|
|
4680
|
-
//#endregion
|
|
4681
4788
|
export { DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, DigestAlgorithm, DiscoveryError, KeyEncryptionAlgorithm, REQUIRED_DISCOVERY_FIELDS, SignatureAlgorithm, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/sso",
|
|
3
|
-
"version": "1.8.0-beta.
|
|
3
|
+
"version": "1.8.0-beta.9",
|
|
4
4
|
"description": "SSO plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,24 +64,24 @@
|
|
|
64
64
|
"zod": "^4.5.4"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
+
"@better-auth/core": "1.8.0-beta.9",
|
|
68
|
+
"@better-auth/kysely-adapter": "1.8.0-beta.9",
|
|
67
69
|
"@types/body-parser": "^1.19.6",
|
|
68
70
|
"@types/express": "^5.0.6",
|
|
71
|
+
"better-auth": "1.8.0-beta.9",
|
|
69
72
|
"better-call": "1.4.5",
|
|
70
73
|
"body-parser": "^2.3.0",
|
|
71
74
|
"express": "^5.2.1",
|
|
72
75
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
73
76
|
"oauth2-mock-server": "^9.0.0",
|
|
74
|
-
"tsdown": "0.23.1"
|
|
75
|
-
"@better-auth/core": "1.8.0-beta.8",
|
|
76
|
-
"@better-auth/kysely-adapter": "1.8.0-beta.8",
|
|
77
|
-
"better-auth": "1.8.0-beta.8"
|
|
77
|
+
"tsdown": "0.23.1"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
+
"@better-auth/core": "^1.8.0-beta.9",
|
|
80
81
|
"@better-auth/utils": "^0.4.2 || ^0.5.0",
|
|
81
82
|
"@better-fetch/fetch": "1.3.3",
|
|
82
|
-
"better-
|
|
83
|
-
"
|
|
84
|
-
"better-auth": "^1.8.0-beta.8"
|
|
83
|
+
"better-auth": "^1.8.0-beta.9",
|
|
84
|
+
"better-call": "1.4.5"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build": "tsdown",
|