@better-auth/sso 1.8.0-beta.1 → 1.8.0-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.
package/dist/client.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as SSOPlugin } from "./index-
|
|
1
|
+
import { t as SSOPlugin } from "./index-BvKzNnKP.mjs";
|
|
2
2
|
import { DBFieldAttribute } from "better-auth/db";
|
|
3
|
-
|
|
4
3
|
//#region src/client.d.ts
|
|
5
4
|
interface SSOClientOptions {
|
|
6
5
|
domainVerification?: {
|
|
@@ -14,7 +13,7 @@ interface SSOClientOptions {
|
|
|
14
13
|
};
|
|
15
14
|
} | undefined;
|
|
16
15
|
}
|
|
17
|
-
declare const ssoClient: <CO extends SSOClientOptions>(options?: CO | undefined) => {
|
|
16
|
+
export declare const ssoClient: <CO extends SSOClientOptions>(options?: CO | undefined) => {
|
|
18
17
|
id: "sso-client";
|
|
19
18
|
version: string;
|
|
20
19
|
$InferServerPlugin: SSOPlugin<{
|
|
@@ -31,4 +30,4 @@ declare const ssoClient: <CO extends SSOClientOptions>(options?: CO | undefined)
|
|
|
31
30
|
};
|
|
32
31
|
};
|
|
33
32
|
//#endregion
|
|
34
|
-
export {
|
|
33
|
+
export {};
|
package/dist/client.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { APIError } from "better-auth/api";
|
|
2
2
|
import * as z from "zod";
|
|
3
|
+
import "@better-fetch/fetch";
|
|
3
4
|
import samlifyDefault from "samlify";
|
|
4
5
|
import { DBFieldAttribute, FieldAttributeToObject, InferAdditionalFieldsFromPluginOptions, RemoveFieldsWithReturnedFalse } from "better-auth/db";
|
|
5
6
|
import { Awaitable, DBTransactionAdapter, OAuth2Tokens, User } from "better-auth";
|
|
6
|
-
import
|
|
7
|
+
import "@better-auth/core/db/adapter";
|
|
8
|
+
import "@better-auth/core";
|
|
7
9
|
//#region src/saml/algorithms.d.ts
|
|
8
10
|
declare const SignatureAlgorithm: {
|
|
9
11
|
readonly RSA_SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
|
|
@@ -479,7 +481,7 @@ interface SSOOptions {
|
|
|
479
481
|
domain?: string | undefined;
|
|
480
482
|
domainVerified?: string | undefined;
|
|
481
483
|
};
|
|
482
|
-
additionalFields?: { [key in string]: DBFieldAttribute };
|
|
484
|
+
additionalFields?: { [key in string]: DBFieldAttribute; };
|
|
483
485
|
};
|
|
484
486
|
} | undefined;
|
|
485
487
|
/**
|
|
@@ -683,7 +685,7 @@ interface SSOOptions {
|
|
|
683
685
|
}
|
|
684
686
|
//#endregion
|
|
685
687
|
//#region src/routes/domain-verification.d.ts
|
|
686
|
-
declare const requestDomainVerification: (options: SSOOptions) =>
|
|
688
|
+
declare const requestDomainVerification: (options: SSOOptions) => import("better-call").StrictEndpoint<"/sso/request-domain-verification", {
|
|
687
689
|
method: "POST";
|
|
688
690
|
body: z.ZodObject<{
|
|
689
691
|
providerId: z.ZodString;
|
|
@@ -705,7 +707,7 @@ declare const requestDomainVerification: (options: SSOOptions) => _$better_call0
|
|
|
705
707
|
};
|
|
706
708
|
};
|
|
707
709
|
};
|
|
708
|
-
use:
|
|
710
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
709
711
|
session: {
|
|
710
712
|
session: Record<string, any> & {
|
|
711
713
|
id: string;
|
|
@@ -731,7 +733,7 @@ declare const requestDomainVerification: (options: SSOOptions) => _$better_call0
|
|
|
731
733
|
}, {
|
|
732
734
|
domainVerificationToken: string;
|
|
733
735
|
}>;
|
|
734
|
-
declare const verifyDomain: (options: SSOOptions) =>
|
|
736
|
+
declare const verifyDomain: (options: SSOOptions) => import("better-call").StrictEndpoint<"/sso/verify-domain", {
|
|
735
737
|
method: "POST";
|
|
736
738
|
body: z.ZodObject<{
|
|
737
739
|
providerId: z.ZodString;
|
|
@@ -756,7 +758,7 @@ declare const verifyDomain: (options: SSOOptions) => _$better_call0.StrictEndpoi
|
|
|
756
758
|
};
|
|
757
759
|
};
|
|
758
760
|
};
|
|
759
|
-
use:
|
|
761
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
760
762
|
session: {
|
|
761
763
|
session: Record<string, any> & {
|
|
762
764
|
id: string;
|
|
@@ -794,9 +796,9 @@ type ParsedCert = ReturnType<typeof parseCertificate>;
|
|
|
794
796
|
type SanitizedCert = ParsedCert | {
|
|
795
797
|
error: string;
|
|
796
798
|
};
|
|
797
|
-
declare const listSSOProviders: (options?: SSOOptions) =>
|
|
799
|
+
declare const listSSOProviders: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/providers", {
|
|
798
800
|
method: "GET";
|
|
799
|
-
use:
|
|
801
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
800
802
|
session: {
|
|
801
803
|
session: Record<string, any> & {
|
|
802
804
|
id: string;
|
|
@@ -865,9 +867,9 @@ declare const listSSOProviders: (options?: SSOOptions) => _$better_call0.StrictE
|
|
|
865
867
|
spMetadataUrl: string;
|
|
866
868
|
}[];
|
|
867
869
|
}>;
|
|
868
|
-
declare const getSSOProvider: (options?: SSOOptions) =>
|
|
870
|
+
declare const getSSOProvider: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/get-provider", {
|
|
869
871
|
method: "GET";
|
|
870
|
-
use:
|
|
872
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
871
873
|
session: {
|
|
872
874
|
session: Record<string, any> & {
|
|
873
875
|
id: string;
|
|
@@ -943,9 +945,9 @@ declare const getSSOProvider: (options?: SSOOptions) => _$better_call0.StrictEnd
|
|
|
943
945
|
} | undefined;
|
|
944
946
|
spMetadataUrl: string;
|
|
945
947
|
}>;
|
|
946
|
-
declare const updateSSOProvider: (options: SSOOptions) =>
|
|
948
|
+
declare const updateSSOProvider: (options: SSOOptions) => import("better-call").StrictEndpoint<"/sso/update-provider", {
|
|
947
949
|
method: "POST";
|
|
948
|
-
use:
|
|
950
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
949
951
|
session: {
|
|
950
952
|
session: Record<string, any> & {
|
|
951
953
|
id: string;
|
|
@@ -999,7 +1001,6 @@ declare const updateSSOProvider: (options: SSOOptions) => _$better_call0.StrictE
|
|
|
999
1001
|
}, z.core.$strict>>>;
|
|
1000
1002
|
}, z.core.$strip>>;
|
|
1001
1003
|
samlConfig: z.ZodOptional<z.ZodObject<{
|
|
1002
|
-
audience: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
1003
1004
|
mapping: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
1004
1005
|
email: z.ZodOptional<z.ZodString>;
|
|
1005
1006
|
emailVerified: z.ZodOptional<z.ZodString>;
|
|
@@ -1021,6 +1022,7 @@ declare const updateSSOProvider: (options: SSOOptions) => _$better_call0.StrictE
|
|
|
1021
1022
|
}, z.core.$strip>>>;
|
|
1022
1023
|
cert: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
|
|
1023
1024
|
entryPoint: z.ZodOptional<z.ZodString>;
|
|
1025
|
+
audience: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
1024
1026
|
callbackUrl: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
1025
1027
|
wantAssertionsSigned: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
1026
1028
|
authnRequestsSigned: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -1099,9 +1101,9 @@ declare const updateSSOProvider: (options: SSOOptions) => _$better_call0.StrictE
|
|
|
1099
1101
|
} | undefined;
|
|
1100
1102
|
spMetadataUrl: string;
|
|
1101
1103
|
}>;
|
|
1102
|
-
declare const deleteSSOProvider: (options?: SSOOptions) =>
|
|
1104
|
+
declare const deleteSSOProvider: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/delete-provider", {
|
|
1103
1105
|
method: "POST";
|
|
1104
|
-
use:
|
|
1106
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
1105
1107
|
session: {
|
|
1106
1108
|
session: Record<string, any> & {
|
|
1107
1109
|
id: string;
|
|
@@ -1150,7 +1152,7 @@ declare const deleteSSOProvider: (options?: SSOOptions) => _$better_call0.Strict
|
|
|
1150
1152
|
}>;
|
|
1151
1153
|
//#endregion
|
|
1152
1154
|
//#region src/routes/sso.d.ts
|
|
1153
|
-
declare const spMetadata: (options?: SSOOptions) =>
|
|
1155
|
+
declare const spMetadata: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/saml2/sp/metadata", {
|
|
1154
1156
|
method: "GET";
|
|
1155
1157
|
query: z.ZodObject<{
|
|
1156
1158
|
providerId: z.ZodString;
|
|
@@ -1168,12 +1170,12 @@ declare const spMetadata: (options?: SSOOptions) => _$better_call0.StrictEndpoin
|
|
|
1168
1170
|
};
|
|
1169
1171
|
};
|
|
1170
1172
|
}, Response>;
|
|
1171
|
-
declare const registerSSOProvider: <O extends SSOOptions>(options: O) =>
|
|
1173
|
+
declare const registerSSOProvider: <O extends SSOOptions>(options: O) => import("better-call").StrictEndpoint<"/sso/register", {
|
|
1172
1174
|
method: "POST";
|
|
1173
1175
|
body: z.ZodObject<{
|
|
1174
1176
|
[x: string]: z.ZodOptional<z.ZodAny>;
|
|
1175
1177
|
}, z.core.$strip>;
|
|
1176
|
-
use:
|
|
1178
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
1177
1179
|
session: {
|
|
1178
1180
|
session: Record<string, any> & {
|
|
1179
1181
|
id: string;
|
|
@@ -1369,7 +1371,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => _$bette
|
|
|
1369
1371
|
oidcConfig: OIDCConfig | null;
|
|
1370
1372
|
samlConfig: SAMLConfig | null;
|
|
1371
1373
|
} & Omit<InferSSOProvider<O>, "samlConfig" | "oidcConfig">>;
|
|
1372
|
-
declare const signInSSO: (options?: SSOOptions) =>
|
|
1374
|
+
declare const signInSSO: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sign-in/sso", {
|
|
1373
1375
|
method: "POST";
|
|
1374
1376
|
body: z.ZodObject<{
|
|
1375
1377
|
email: z.ZodOptional<z.ZodString>;
|
|
@@ -1491,7 +1493,7 @@ declare const signInSSO: (options?: SSOOptions) => _$better_call0.StrictEndpoint
|
|
|
1491
1493
|
url: string;
|
|
1492
1494
|
redirect: boolean;
|
|
1493
1495
|
}>;
|
|
1494
|
-
declare const callbackSSO: (options?: SSOOptions) =>
|
|
1496
|
+
declare const callbackSSO: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/callback/:providerId", {
|
|
1495
1497
|
method: "GET";
|
|
1496
1498
|
query: z.ZodObject<{
|
|
1497
1499
|
code: z.ZodOptional<z.ZodString>;
|
|
@@ -1519,7 +1521,7 @@ declare const callbackSSO: (options?: SSOOptions) => _$better_call0.StrictEndpoi
|
|
|
1519
1521
|
* Used when `options.redirectURI` is set — the `providerId` is read from
|
|
1520
1522
|
* the OAuth state instead of the URL path.
|
|
1521
1523
|
*/
|
|
1522
|
-
declare const callbackSSOShared: (options?: SSOOptions) =>
|
|
1524
|
+
declare const callbackSSOShared: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/callback", {
|
|
1523
1525
|
metadata: {
|
|
1524
1526
|
openapi: {
|
|
1525
1527
|
operationId: string;
|
|
@@ -1542,7 +1544,7 @@ declare const callbackSSOShared: (options?: SSOOptions) => _$better_call0.Strict
|
|
|
1542
1544
|
}, z.core.$strip>;
|
|
1543
1545
|
allowedMediaTypes: readonly ["application/x-www-form-urlencoded", "application/json"];
|
|
1544
1546
|
}, never>;
|
|
1545
|
-
declare const acsEndpoint: (options?: SSOOptions) =>
|
|
1547
|
+
declare const acsEndpoint: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
|
|
1546
1548
|
method: ("GET" | "POST")[];
|
|
1547
1549
|
body: z.ZodOptional<z.ZodObject<{
|
|
1548
1550
|
SAMLResponse: z.ZodString;
|
|
@@ -1572,7 +1574,7 @@ declare const acsEndpoint: (options?: SSOOptions) => _$better_call0.StrictEndpoi
|
|
|
1572
1574
|
scope: "server";
|
|
1573
1575
|
};
|
|
1574
1576
|
}, never>;
|
|
1575
|
-
declare const sloEndpoint: (options?: SSOOptions) =>
|
|
1577
|
+
declare const sloEndpoint: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/saml2/sp/slo/:providerId", {
|
|
1576
1578
|
method: ("GET" | "POST")[];
|
|
1577
1579
|
body: z.ZodOptional<z.ZodObject<{
|
|
1578
1580
|
SAMLRequest: z.ZodOptional<z.ZodString>;
|
|
@@ -1593,12 +1595,12 @@ declare const sloEndpoint: (options?: SSOOptions) => _$better_call0.StrictEndpoi
|
|
|
1593
1595
|
scope: "server";
|
|
1594
1596
|
};
|
|
1595
1597
|
}, void | Response>;
|
|
1596
|
-
declare const initiateSLO: (options?: SSOOptions) =>
|
|
1598
|
+
declare const initiateSLO: (options?: SSOOptions) => import("better-call").StrictEndpoint<"/sso/saml2/logout/:providerId", {
|
|
1597
1599
|
method: "POST";
|
|
1598
1600
|
body: z.ZodObject<{
|
|
1599
1601
|
callbackURL: z.ZodOptional<z.ZodString>;
|
|
1600
1602
|
}, z.core.$strip>;
|
|
1601
|
-
use:
|
|
1603
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
1602
1604
|
session: {
|
|
1603
1605
|
session: Record<string, any> & {
|
|
1604
1606
|
id: string;
|
|
@@ -1762,7 +1764,29 @@ interface OIDCDiscoveryDocument {
|
|
|
1762
1764
|
/**
|
|
1763
1765
|
* Error codes for OIDC discovery operations.
|
|
1764
1766
|
*/
|
|
1765
|
-
type DiscoveryErrorCode =
|
|
1767
|
+
type DiscoveryErrorCode =
|
|
1768
|
+
/** Request to discovery endpoint timed out */
|
|
1769
|
+
"discovery_timeout" |
|
|
1770
|
+
/** Discovery endpoint returned 404 or similar */
|
|
1771
|
+
"discovery_not_found" |
|
|
1772
|
+
/** Discovery endpoint returned invalid JSON */
|
|
1773
|
+
"discovery_invalid_json" |
|
|
1774
|
+
/** OIDC endpoint URL (discovery or per-endpoint: authorization, token, userinfo, jwks) is invalid, malformed, or uses a non-`http(s)` scheme */
|
|
1775
|
+
"discovery_invalid_url" |
|
|
1776
|
+
/** OIDC endpoint URL is not trusted by the trusted origins configuration */
|
|
1777
|
+
"discovery_untrusted_origin" |
|
|
1778
|
+
/** OIDC endpoint URL (discovery or per-endpoint) points to a host that is not publicly routable (loopback, RFC 1918, link-local, cloud metadata FQDN, etc.) */
|
|
1779
|
+
"discovery_private_host" |
|
|
1780
|
+
/** Server-side OIDC endpoint fetch received an HTTP redirect response */
|
|
1781
|
+
"oidc_endpoint_redirect" |
|
|
1782
|
+
/** Discovery document issuer doesn't match configured issuer */
|
|
1783
|
+
"issuer_mismatch" |
|
|
1784
|
+
/** Discovery document is missing required fields */
|
|
1785
|
+
"discovery_incomplete" |
|
|
1786
|
+
/** IdP only advertises token auth methods that Better Auth doesn't currently support */
|
|
1787
|
+
"unsupported_token_auth_method" |
|
|
1788
|
+
/** Catch-all for unexpected errors */
|
|
1789
|
+
"discovery_unexpected_error";
|
|
1766
1790
|
/**
|
|
1767
1791
|
* Custom error class for OIDC discovery failures.
|
|
1768
1792
|
* Can be caught and mapped to APIError at the edge.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as SAMLIdentityProviderMetadata, B as SSOUserResolutionContext, C as deriveSAMLIdentityProviderEntityID, D as DEFAULT_MAX_SAML_RESPONSE_SIZE, E as DEFAULT_MAX_SAML_METADATA_SIZE, F as SSOProviderMutationGuardInput, G as DigestAlgorithm, H as AlgorithmValidationOptions, I as SSOProviderReference, K as KeyEncryptionAlgorithm, L as SSOProviderUserProfile, M as SSOOptions, N as SSOProvider, O as OIDCConfig, P as SSOProviderMutationGuardContext, R as SSOSAMLUserResolutionInput, S as SAMLServiceProviderPolicy, T as DEFAULT_CLOCK_SKEW_MS, U as DataEncryptionAlgorithm, V as SSOUserResolutionInput, W as DeprecatedAlgorithmBehavior, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SSOOIDCUserResolutionInput, k as SAMLConfig, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, q as SignatureAlgorithm, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as deriveSAMLServiceProviderPolicy, x as validateSAMLTimestamp, y as SAMLConditions, z as SSOUserResolution } from "./index-
|
|
2
|
-
export { AlgorithmValidationOptions, DEFAULT_CLOCK_SKEW_MS, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE, DataEncryptionAlgorithm, DeprecatedAlgorithmBehavior, DigestAlgorithm, DiscoverOIDCConfigParams, DiscoveryError, DiscoveryErrorCode, HydratedOIDCConfig, KeyEncryptionAlgorithm, OIDCConfig, OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, RequiredDiscoveryField, SAMLConditions, SAMLConfig, SAMLIdentityProviderMetadata, SAMLServiceProviderPolicy, SSOOIDCUserResolutionInput, SSOOptions, SSOPlugin, SSOProvider, SSOProviderMutationGuardContext, SSOProviderMutationGuardInput, SSOProviderReference, SSOProviderUserProfile, SSOSAMLUserResolutionInput, SSOUserResolution, SSOUserResolutionContext, SSOUserResolutionInput, SignatureAlgorithm, TimestampValidationOptions, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
|
1
|
+
import { A as SAMLIdentityProviderMetadata, B as SSOUserResolutionContext, C as deriveSAMLIdentityProviderEntityID, D as DEFAULT_MAX_SAML_RESPONSE_SIZE, E as DEFAULT_MAX_SAML_METADATA_SIZE, F as SSOProviderMutationGuardInput, G as DigestAlgorithm, H as AlgorithmValidationOptions, I as SSOProviderReference, K as KeyEncryptionAlgorithm, L as SSOProviderUserProfile, M as SSOOptions, N as SSOProvider, O as OIDCConfig, P as SSOProviderMutationGuardContext, R as SSOSAMLUserResolutionInput, S as SAMLServiceProviderPolicy, T as DEFAULT_CLOCK_SKEW_MS, U as DataEncryptionAlgorithm, V as SSOUserResolutionInput, W as DeprecatedAlgorithmBehavior, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SSOOIDCUserResolutionInput, k as SAMLConfig, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, q as SignatureAlgorithm, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as deriveSAMLServiceProviderPolicy, x as validateSAMLTimestamp, y as SAMLConditions, z as SSOUserResolution } from "./index-BvKzNnKP.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 SSOOIDCUserResolutionInput, type SSOOptions, SSOPlugin, type SSOProvider, type SSOProviderMutationGuardContext, type SSOProviderMutationGuardInput, type SSOProviderReference, type SSOProviderUserProfile, type SSOSAMLUserResolutionInput, type SSOUserResolution, type SSOUserResolutionContext, type SSOUserResolutionInput, SignatureAlgorithm, type TimestampValidationOptions, computeDiscoveryUrl, deriveSAMLIdentityProviderEntityID, deriveSAMLServiceProviderPolicy, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-HRaJjPrD.mjs";
|
|
2
2
|
import { APIError, addOAuthServerContext, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
|
|
3
3
|
import { XMLParser, XMLValidator } from "fast-xml-parser";
|
|
4
4
|
import { X509Certificate } from "node:crypto";
|
|
@@ -7,14 +7,14 @@ import { generateRandomString } from "better-auth/crypto";
|
|
|
7
7
|
import * as z from "zod";
|
|
8
8
|
import { getCurrentAdapter, getCurrentDBAdapterAsyncLocalStorage, runWithTransaction } from "@better-auth/core/context";
|
|
9
9
|
import { filterOutputFields } from "@better-auth/core/utils/db";
|
|
10
|
+
import { verifyIdToken } from "@better-auth/core/oauth2/internal";
|
|
10
11
|
import { classifyHost, isPublicRoutableHost } from "@better-auth/core/utils/host";
|
|
11
12
|
import { betterFetch } from "@better-fetch/fetch";
|
|
12
|
-
import { createRemoteJWKSet, customFetch
|
|
13
|
+
import { createRemoteJWKSet, customFetch } from "jose";
|
|
13
14
|
import { base64, base64Url } from "@better-auth/utils/base64";
|
|
14
15
|
import { createHash } from "@better-auth/utils/hash";
|
|
15
16
|
import { DOMParser } from "@xmldom/xmldom";
|
|
16
|
-
import * as samlifyNamespace from "samlify";
|
|
17
|
-
import samlifyDefault from "samlify";
|
|
17
|
+
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 { isAPIError } from "@better-auth/core/utils/is-api-error";
|
|
@@ -40,7 +40,7 @@ const LOGOUT_REQUEST_KEY_PREFIX = "saml-logout-request:";
|
|
|
40
40
|
* Default TTL for used assertion records (15 minutes).
|
|
41
41
|
* This should match the maximum expected NotOnOrAfter window plus clock skew.
|
|
42
42
|
*/
|
|
43
|
-
const DEFAULT_ASSERTION_TTL_MS =
|
|
43
|
+
const DEFAULT_ASSERTION_TTL_MS = 9e5;
|
|
44
44
|
/**
|
|
45
45
|
* Default clock skew tolerance (5 minutes).
|
|
46
46
|
* Allows for minor time differences between IdP and SP servers.
|
|
@@ -50,17 +50,17 @@ const DEFAULT_ASSERTION_TTL_MS = 900 * 1e3;
|
|
|
50
50
|
* - Clock synchronization differences (NTP drift)
|
|
51
51
|
* - Distributed systems across timezones
|
|
52
52
|
*/
|
|
53
|
-
const DEFAULT_CLOCK_SKEW_MS =
|
|
53
|
+
const DEFAULT_CLOCK_SKEW_MS = 3e5;
|
|
54
54
|
/**
|
|
55
55
|
* Default maximum size for SAML responses (256 KB).
|
|
56
56
|
* Protects against memory exhaustion from oversized SAML payloads.
|
|
57
57
|
*/
|
|
58
|
-
const DEFAULT_MAX_SAML_RESPONSE_SIZE =
|
|
58
|
+
const DEFAULT_MAX_SAML_RESPONSE_SIZE = 262144;
|
|
59
59
|
/**
|
|
60
60
|
* Default maximum size for IdP metadata (100 KB).
|
|
61
61
|
* Protects against oversized metadata documents.
|
|
62
62
|
*/
|
|
63
|
-
const DEFAULT_MAX_SAML_METADATA_SIZE =
|
|
63
|
+
const DEFAULT_MAX_SAML_METADATA_SIZE = 102400;
|
|
64
64
|
//#endregion
|
|
65
65
|
//#region src/utils.ts
|
|
66
66
|
const unsafeSAMLRedirectPathPrefix = /^\/(?:\/|\\|%2f|%5c)/i;
|
|
@@ -113,7 +113,8 @@ const validateEmailDomain = (email, domain) => {
|
|
|
113
113
|
return domainMatches(emailDomain, domain);
|
|
114
114
|
};
|
|
115
115
|
function parseCertificate(certPem) {
|
|
116
|
-
const
|
|
116
|
+
const normalized = certPem.includes("-----BEGIN") ? certPem : `-----BEGIN CERTIFICATE-----\n${certPem}\n-----END CERTIFICATE-----`;
|
|
117
|
+
const cert = new X509Certificate(normalized);
|
|
117
118
|
return {
|
|
118
119
|
fingerprintSha256: cert.fingerprint256,
|
|
119
120
|
notBefore: cert.validFrom,
|
|
@@ -577,11 +578,13 @@ function assertOIDCAuthorizedParty(payload, clientId) {
|
|
|
577
578
|
* the SSO OIDC flow.
|
|
578
579
|
*/
|
|
579
580
|
async function validateOIDCIdToken(token, jwksEndpoint, options, isTrustedOrigin) {
|
|
580
|
-
const
|
|
581
|
+
const jwks = createRemoteJWKSet(new URL(jwksEndpoint), { [customFetch]: (url, init) => fetchOIDCEndpointResponse("jwksEndpoint", url, init, isTrustedOrigin) });
|
|
582
|
+
const verified = await verifyIdToken(token, {
|
|
583
|
+
jwks,
|
|
581
584
|
audience: options.audience,
|
|
582
585
|
issuer: options.issuer
|
|
583
586
|
});
|
|
584
|
-
|
|
587
|
+
assertOIDCAuthorizedParty(verified.payload, options.audience);
|
|
585
588
|
return verified;
|
|
586
589
|
}
|
|
587
590
|
/**
|
|
@@ -1098,10 +1101,7 @@ function handleDeprecatedAlgorithm(message, behavior, errorCode) {
|
|
|
1098
1101
|
message,
|
|
1099
1102
|
code: errorCode
|
|
1100
1103
|
});
|
|
1101
|
-
case "warn":
|
|
1102
|
-
console.warn(`[SAML Security Warning] ${message}`);
|
|
1103
|
-
break;
|
|
1104
|
-
case "allow": break;
|
|
1104
|
+
case "warn": console.warn(`[SAML Security Warning] ${message}`);
|
|
1105
1105
|
}
|
|
1106
1106
|
}
|
|
1107
1107
|
function validateSignatureAlgorithm(algorithm, options = {}) {
|
|
@@ -2276,9 +2276,10 @@ async function checkProviderAccess(ctx, providerId) {
|
|
|
2276
2276
|
});
|
|
2277
2277
|
if (!provider) throw new APIError("NOT_FOUND", { message: "Provider not found" });
|
|
2278
2278
|
let hasAccess = false;
|
|
2279
|
-
if (provider.organizationId)
|
|
2280
|
-
|
|
2281
|
-
|
|
2279
|
+
if (provider.organizationId) {
|
|
2280
|
+
if (ctx.context.hasPlugin("organization")) hasAccess = await isOrgAdmin(ctx, userId, provider.organizationId);
|
|
2281
|
+
else hasAccess = provider.userId === userId;
|
|
2282
|
+
} else hasAccess = provider.userId === userId;
|
|
2282
2283
|
if (!hasAccess) throw new APIError("FORBIDDEN", { message: "You don't have access to this provider" });
|
|
2283
2284
|
return provider;
|
|
2284
2285
|
}
|
|
@@ -2573,7 +2574,7 @@ const requestDomainVerification = (options) => {
|
|
|
2573
2574
|
await ctx.context.internalAdapter.createVerificationValue({
|
|
2574
2575
|
identifier,
|
|
2575
2576
|
value: domainVerificationToken,
|
|
2576
|
-
expiresAt: new Date(Date.now() +
|
|
2577
|
+
expiresAt: new Date(Date.now() + 6048e5)
|
|
2577
2578
|
});
|
|
2578
2579
|
ctx.setStatus(201);
|
|
2579
2580
|
return ctx.json({ domainVerificationToken });
|
|
@@ -2661,7 +2662,7 @@ async function generateRelayState(c, link, providerReference) {
|
|
|
2661
2662
|
/**
|
|
2662
2663
|
* This is the actual expiry time of the state
|
|
2663
2664
|
*/
|
|
2664
|
-
expiresAt: Date.now() +
|
|
2665
|
+
expiresAt: Date.now() + 6e5,
|
|
2665
2666
|
requestSignUp: c.body.requestSignUp,
|
|
2666
2667
|
serverContext: providerReference ? { [SSO_PROVIDER_STATE_KEY]: providerReference } : void 0
|
|
2667
2668
|
};
|
|
@@ -3191,9 +3192,10 @@ const spMetadata = (options) => {
|
|
|
3191
3192
|
});
|
|
3192
3193
|
};
|
|
3193
3194
|
const registerSSOProvider = (options) => {
|
|
3195
|
+
const registerBodySchema = getRegisterSSOProviderBodySchema(options);
|
|
3194
3196
|
return createAuthEndpoint("/sso/register", {
|
|
3195
3197
|
method: "POST",
|
|
3196
|
-
body:
|
|
3198
|
+
body: registerBodySchema,
|
|
3197
3199
|
use: [sessionMiddleware],
|
|
3198
3200
|
metadata: {
|
|
3199
3201
|
$Infer: { body: {} },
|
|
@@ -3388,13 +3390,13 @@ const registerSSOProvider = (options) => {
|
|
|
3388
3390
|
if (!member) throw new APIError("BAD_REQUEST", { message: "You are not a member of the organization" });
|
|
3389
3391
|
if (ctx.context.hasPlugin("organization") && !hasOrgAdminRole(member)) throw new APIError("FORBIDDEN", { message: "You must be an organization owner or admin to register SSO providers" });
|
|
3390
3392
|
}
|
|
3391
|
-
if (new Set([
|
|
3393
|
+
if ((/* @__PURE__ */ new Set([
|
|
3392
3394
|
...BUILT_IN_ACCOUNT_PROVIDER_IDS,
|
|
3393
3395
|
...Object.keys(ctx.context.options.socialProviders ?? {}),
|
|
3394
3396
|
...ctx.context.socialProviders.map((p) => p.id),
|
|
3395
3397
|
...ctx.context.trustedProviders,
|
|
3396
3398
|
...options?.defaultSSO?.map((p) => p.providerId) ?? []
|
|
3397
|
-
]).has(body.providerId)) {
|
|
3399
|
+
])).has(body.providerId)) {
|
|
3398
3400
|
ctx.context.logger.warn(`SSO provider registration rejected for reserved providerId: ${body.providerId}`);
|
|
3399
3401
|
throw new APIError("UNPROCESSABLE_ENTITY", { message: "This providerId is reserved and cannot be used for an SSO provider" });
|
|
3400
3402
|
}
|
|
@@ -3536,7 +3538,7 @@ const registerSSOProvider = (options) => {
|
|
|
3536
3538
|
await ctx.context.internalAdapter.createVerificationValue({
|
|
3537
3539
|
identifier: getVerificationIdentifier(options, provider.providerId),
|
|
3538
3540
|
value: domainVerificationToken,
|
|
3539
|
-
expiresAt: new Date(Date.now() +
|
|
3541
|
+
expiresAt: new Date(Date.now() + 6048e5)
|
|
3540
3542
|
});
|
|
3541
3543
|
}
|
|
3542
3544
|
const result = {
|
|
@@ -3816,7 +3818,9 @@ async function handleOIDCCallback(ctx, options, providerId, stateData, parsedPro
|
|
|
3816
3818
|
const { code, error, error_description } = ctx.query;
|
|
3817
3819
|
if (!stateData) stateData = await parseState(ctx);
|
|
3818
3820
|
if (!stateData) {
|
|
3819
|
-
const
|
|
3821
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
|
|
3822
|
+
const params = new URLSearchParams({ error: "invalid_state" });
|
|
3823
|
+
const redirectURL = appendQueryParams(errorURL, params);
|
|
3820
3824
|
throw ctx.redirect(redirectURL);
|
|
3821
3825
|
}
|
|
3822
3826
|
const providerReference = parsedProviderReference ?? parseSSOProviderReference(stateData.serverContext?.["ssoProviderReference"]);
|
|
@@ -4197,15 +4201,19 @@ const callbackSSOShared = (options) => {
|
|
|
4197
4201
|
}, async (ctx) => {
|
|
4198
4202
|
const stateData = await parseState(ctx);
|
|
4199
4203
|
if (!stateData) {
|
|
4200
|
-
const
|
|
4204
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
|
|
4205
|
+
const params = new URLSearchParams({ error: "invalid_state" });
|
|
4206
|
+
const redirectURL = appendQueryParams(errorURL, params);
|
|
4201
4207
|
throw ctx.redirect(redirectURL);
|
|
4202
4208
|
}
|
|
4203
4209
|
const providerReference = parseSSOProviderReference(stateData.serverContext?.[SSO_PROVIDER_STATE_KEY]);
|
|
4204
4210
|
if (!providerReference) {
|
|
4205
|
-
const
|
|
4211
|
+
const errorURL = stateData.errorURL || stateData.callbackURL;
|
|
4212
|
+
const params = new URLSearchParams({
|
|
4206
4213
|
error: "invalid_state",
|
|
4207
4214
|
error_description: "missing_sso_provider_reference"
|
|
4208
|
-
})
|
|
4215
|
+
});
|
|
4216
|
+
const redirectURL = appendQueryParams(errorURL, params);
|
|
4209
4217
|
throw ctx.redirect(redirectURL);
|
|
4210
4218
|
}
|
|
4211
4219
|
return handleOIDCCallback(ctx, options, providerReference.providerId, stateData, providerReference);
|
|
@@ -4241,7 +4249,9 @@ const acsEndpoint = (options) => {
|
|
|
4241
4249
|
let resolvedErrorRedirectUrl;
|
|
4242
4250
|
if (ctx.method === "GET" && !ctx.body?.SAMLResponse) {
|
|
4243
4251
|
if (!(await getSessionFromCtx(ctx))?.session) {
|
|
4244
|
-
const
|
|
4252
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`;
|
|
4253
|
+
const params = new URLSearchParams({ error: "invalid_request" });
|
|
4254
|
+
const redirectURL = appendQueryParams(errorURL, params);
|
|
4245
4255
|
throw ctx.redirect(redirectURL);
|
|
4246
4256
|
}
|
|
4247
4257
|
const relayState = ctx.query?.RelayState;
|
|
@@ -4310,10 +4320,11 @@ const sloEndpoint = (options) => {
|
|
|
4310
4320
|
const appOrigin = new URL(ctx.context.baseURL).origin;
|
|
4311
4321
|
const safeErrorURL = getSafeRedirectUrl([relayState], `${appOrigin}/sso/saml2/sp/slo/${providerId}`, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
4312
4322
|
if (!samlRequest && !samlResponse) {
|
|
4313
|
-
const
|
|
4323
|
+
const params = new URLSearchParams({
|
|
4314
4324
|
error: "invalid_request",
|
|
4315
4325
|
error_description: "missing_logout_data"
|
|
4316
|
-
})
|
|
4326
|
+
});
|
|
4327
|
+
const redirectURL = appendQueryParams(safeErrorURL, params);
|
|
4317
4328
|
throw ctx.redirect(redirectURL);
|
|
4318
4329
|
}
|
|
4319
4330
|
const provider = await findSAMLProvider(providerId, options, ctx.context.adapter);
|
|
@@ -4376,14 +4387,16 @@ async function handleLogoutRequest(ctx, sp, idp, relayState, providerId) {
|
|
|
4376
4387
|
const stored = await ctx.context.internalAdapter.findVerificationValue(key);
|
|
4377
4388
|
if (stored) {
|
|
4378
4389
|
const data = safeJsonParse(stored.value);
|
|
4379
|
-
if (data)
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4390
|
+
if (data) {
|
|
4391
|
+
if (!sessionIndex || !data.sessionIndex || sessionIndex === data.sessionIndex) {
|
|
4392
|
+
await ctx.context.internalAdapter.deleteSession(data.sessionToken).catch((e) => ctx.context.logger.warn("Failed to delete session during SLO", { error: e }));
|
|
4393
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(`${SAML_SESSION_BY_ID_PREFIX}${data.sessionId}`).catch((e) => ctx.context.logger.warn("Failed to delete SAML session lookup during SLO", e));
|
|
4394
|
+
} else ctx.context.logger.warn("SessionIndex mismatch in LogoutRequest - skipping session deletion", {
|
|
4395
|
+
providerId,
|
|
4396
|
+
requestedSessionIndex: sessionIndex,
|
|
4397
|
+
storedSessionIndex: data.sessionIndex
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4387
4400
|
await ctx.context.internalAdapter.deleteVerificationByIdentifier(key).catch((e) => ctx.context.logger.warn("Failed to delete SAML session key during SLO", e));
|
|
4388
4401
|
}
|
|
4389
4402
|
const currentSession = await getSessionFromCtx(ctx);
|
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.3",
|
|
4
4
|
"description": "SSO plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,22 +66,22 @@
|
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/body-parser": "^1.19.6",
|
|
68
68
|
"@types/express": "^5.0.6",
|
|
69
|
-
"better-call": "1.4.
|
|
69
|
+
"better-call": "1.4.4",
|
|
70
70
|
"body-parser": "^2.3.0",
|
|
71
71
|
"express": "^5.2.1",
|
|
72
72
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
73
73
|
"oauth2-mock-server": "^9.0.0",
|
|
74
|
-
"tsdown": "0.
|
|
75
|
-
"@better-auth/core": "1.8.0-beta.
|
|
76
|
-
"@better-auth/kysely-adapter": "1.8.0-beta.
|
|
77
|
-
"better-auth": "1.8.0-beta.
|
|
74
|
+
"tsdown": "0.23.1",
|
|
75
|
+
"@better-auth/core": "1.8.0-beta.3",
|
|
76
|
+
"@better-auth/kysely-adapter": "1.8.0-beta.3",
|
|
77
|
+
"better-auth": "1.8.0-beta.3"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"@better-auth/utils": "^0.4.2 || ^0.5.0",
|
|
81
|
-
"@better-fetch/fetch": "1.3.
|
|
82
|
-
"better-call": "1.4.
|
|
83
|
-
"better-auth": "^1.8.0-beta.
|
|
84
|
-
"
|
|
81
|
+
"@better-fetch/fetch": "1.3.3",
|
|
82
|
+
"better-call": "1.4.4",
|
|
83
|
+
"@better-auth/core": "^1.8.0-beta.3",
|
|
84
|
+
"better-auth": "^1.8.0-beta.3"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build": "tsdown",
|