@better-auth/sso 1.7.0-beta.0 → 1.7.0-beta.1
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
package/dist/client.mjs
CHANGED
|
@@ -81,16 +81,29 @@ interface OIDCConfig {
|
|
|
81
81
|
mapping?: OIDCMapping | undefined;
|
|
82
82
|
}
|
|
83
83
|
interface SAMLConfig {
|
|
84
|
+
/**
|
|
85
|
+
* SP Entity ID. Used as the `entityID` in SP metadata when
|
|
86
|
+
* `spMetadata.entityID` is not set. Also used as the expected
|
|
87
|
+
* audience for SAML assertion validation when `audience` is not set.
|
|
88
|
+
*/
|
|
84
89
|
issuer: string;
|
|
90
|
+
/**
|
|
91
|
+
* IdP SSO URL. Used as the redirect destination when
|
|
92
|
+
* `idpMetadata.metadata` is not provided. Ignored when
|
|
93
|
+
* IdP metadata XML is set (the SSO URL is extracted from the XML).
|
|
94
|
+
*/
|
|
85
95
|
entryPoint: string;
|
|
96
|
+
/**
|
|
97
|
+
* IdP signing certificate. Used to verify SAML response signatures
|
|
98
|
+
* when `idpMetadata.metadata` is not provided. Ignored when IdP
|
|
99
|
+
* metadata XML is set (the certificate is extracted from the XML).
|
|
100
|
+
* When both this and `idpMetadata.cert` are set, `idpMetadata.cert` takes precedence.
|
|
101
|
+
*/
|
|
86
102
|
cert: string;
|
|
87
|
-
callbackUrl: string;
|
|
88
103
|
audience?: string | undefined;
|
|
89
104
|
idpMetadata?: {
|
|
90
105
|
metadata?: string;
|
|
91
106
|
entityID?: string;
|
|
92
|
-
entityURL?: string;
|
|
93
|
-
redirectURL?: string;
|
|
94
107
|
cert?: string;
|
|
95
108
|
privateKey?: string;
|
|
96
109
|
privateKeyPass?: string;
|
|
@@ -106,7 +119,12 @@ interface SAMLConfig {
|
|
|
106
119
|
Location: string;
|
|
107
120
|
}>;
|
|
108
121
|
} | undefined;
|
|
109
|
-
|
|
122
|
+
/**
|
|
123
|
+
* SP metadata configuration. All fields are optional; when omitted,
|
|
124
|
+
* SP metadata is auto-generated from `issuer`, `wantAssertionsSigned`,
|
|
125
|
+
* `authnRequestsSigned`, and `identifierFormat`.
|
|
126
|
+
*/
|
|
127
|
+
spMetadata?: {
|
|
110
128
|
metadata?: string | undefined;
|
|
111
129
|
entityID?: string | undefined;
|
|
112
130
|
binding?: string | undefined;
|
|
@@ -116,14 +134,17 @@ interface SAMLConfig {
|
|
|
116
134
|
encPrivateKey?: string | undefined;
|
|
117
135
|
encPrivateKeyPass?: string | undefined;
|
|
118
136
|
};
|
|
137
|
+
/**
|
|
138
|
+
* Request signed assertions from the IdP. When true, the SP metadata
|
|
139
|
+
* advertises `WantAssertionsSigned="true"` and samlify will reject
|
|
140
|
+
* unsigned assertions.
|
|
141
|
+
*/
|
|
119
142
|
wantAssertionsSigned?: boolean | undefined;
|
|
120
143
|
authnRequestsSigned?: boolean | undefined;
|
|
121
144
|
signatureAlgorithm?: string | undefined;
|
|
122
145
|
digestAlgorithm?: string | undefined;
|
|
123
146
|
identifierFormat?: string | undefined;
|
|
124
147
|
privateKey?: string | undefined;
|
|
125
|
-
decryptionPvk?: string | undefined;
|
|
126
|
-
additionalParams?: Record<string, any> | undefined;
|
|
127
148
|
mapping?: SAMLMapping | undefined;
|
|
128
149
|
}
|
|
129
150
|
type BaseSSOProvider = {
|
|
@@ -614,7 +635,6 @@ declare const listSSOProviders: () => better_call0.StrictEndpoint<"/sso/provider
|
|
|
614
635
|
} | undefined;
|
|
615
636
|
samlConfig: {
|
|
616
637
|
entryPoint: string;
|
|
617
|
-
callbackUrl: string;
|
|
618
638
|
audience: string | undefined;
|
|
619
639
|
wantAssertionsSigned: boolean | undefined;
|
|
620
640
|
authnRequestsSigned: boolean | undefined;
|
|
@@ -699,7 +719,6 @@ declare const getSSOProvider: () => better_call0.StrictEndpoint<"/sso/get-provid
|
|
|
699
719
|
} | undefined;
|
|
700
720
|
samlConfig: {
|
|
701
721
|
entryPoint: string;
|
|
702
|
-
callbackUrl: string;
|
|
703
722
|
audience: string | undefined;
|
|
704
723
|
wantAssertionsSigned: boolean | undefined;
|
|
705
724
|
authnRequestsSigned: boolean | undefined;
|
|
@@ -775,7 +794,6 @@ declare const updateSSOProvider: (options: SSOOptions) => better_call0.StrictEnd
|
|
|
775
794
|
samlConfig: z.ZodOptional<z.ZodObject<{
|
|
776
795
|
entryPoint: z.ZodOptional<z.ZodString>;
|
|
777
796
|
cert: z.ZodOptional<z.ZodString>;
|
|
778
|
-
callbackUrl: z.ZodOptional<z.ZodString>;
|
|
779
797
|
audience: z.ZodOptional<z.ZodString>;
|
|
780
798
|
idpMetadata: z.ZodOptional<z.ZodObject<{
|
|
781
799
|
metadata: z.ZodOptional<z.ZodString>;
|
|
@@ -807,8 +825,6 @@ declare const updateSSOProvider: (options: SSOOptions) => better_call0.StrictEnd
|
|
|
807
825
|
digestAlgorithm: z.ZodOptional<z.ZodString>;
|
|
808
826
|
identifierFormat: z.ZodOptional<z.ZodString>;
|
|
809
827
|
privateKey: z.ZodOptional<z.ZodString>;
|
|
810
|
-
decryptionPvk: z.ZodOptional<z.ZodString>;
|
|
811
|
-
additionalParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
812
828
|
mapping: z.ZodOptional<z.ZodObject<{
|
|
813
829
|
id: z.ZodOptional<z.ZodString>;
|
|
814
830
|
email: z.ZodOptional<z.ZodString>;
|
|
@@ -859,7 +875,6 @@ declare const updateSSOProvider: (options: SSOOptions) => better_call0.StrictEnd
|
|
|
859
875
|
} | undefined;
|
|
860
876
|
samlConfig: {
|
|
861
877
|
entryPoint: string;
|
|
862
|
-
callbackUrl: string;
|
|
863
878
|
audience: string | undefined;
|
|
864
879
|
wantAssertionsSigned: boolean | undefined;
|
|
865
880
|
authnRequestsSigned: boolean | undefined;
|
|
@@ -932,10 +947,6 @@ declare const spMetadata: (options?: SSOOptions) => better_call0.StrictEndpoint<
|
|
|
932
947
|
method: "GET";
|
|
933
948
|
query: z.ZodObject<{
|
|
934
949
|
providerId: z.ZodString;
|
|
935
|
-
format: z.ZodDefault<z.ZodEnum<{
|
|
936
|
-
json: "json";
|
|
937
|
-
xml: "xml";
|
|
938
|
-
}>>;
|
|
939
950
|
}, z.core.$strip>;
|
|
940
951
|
metadata: {
|
|
941
952
|
openapi: {
|
|
@@ -986,7 +997,6 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
|
|
|
986
997
|
samlConfig: z.ZodOptional<z.ZodObject<{
|
|
987
998
|
entryPoint: z.ZodString;
|
|
988
999
|
cert: z.ZodString;
|
|
989
|
-
callbackUrl: z.ZodString;
|
|
990
1000
|
audience: z.ZodOptional<z.ZodString>;
|
|
991
1001
|
idpMetadata: z.ZodOptional<z.ZodObject<{
|
|
992
1002
|
metadata: z.ZodOptional<z.ZodString>;
|
|
@@ -1002,7 +1012,7 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
|
|
|
1002
1012
|
Location: z.ZodString;
|
|
1003
1013
|
}, z.core.$strip>>>;
|
|
1004
1014
|
}, z.core.$strip>>;
|
|
1005
|
-
spMetadata: z.ZodObject<{
|
|
1015
|
+
spMetadata: z.ZodOptional<z.ZodObject<{
|
|
1006
1016
|
metadata: z.ZodOptional<z.ZodString>;
|
|
1007
1017
|
entityID: z.ZodOptional<z.ZodString>;
|
|
1008
1018
|
binding: z.ZodOptional<z.ZodString>;
|
|
@@ -1011,15 +1021,13 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
|
|
|
1011
1021
|
isAssertionEncrypted: z.ZodOptional<z.ZodBoolean>;
|
|
1012
1022
|
encPrivateKey: z.ZodOptional<z.ZodString>;
|
|
1013
1023
|
encPrivateKeyPass: z.ZodOptional<z.ZodString>;
|
|
1014
|
-
}, z.core.$strip
|
|
1024
|
+
}, z.core.$strip>>;
|
|
1015
1025
|
wantAssertionsSigned: z.ZodOptional<z.ZodBoolean>;
|
|
1016
1026
|
authnRequestsSigned: z.ZodOptional<z.ZodBoolean>;
|
|
1017
1027
|
signatureAlgorithm: z.ZodOptional<z.ZodString>;
|
|
1018
1028
|
digestAlgorithm: z.ZodOptional<z.ZodString>;
|
|
1019
1029
|
identifierFormat: z.ZodOptional<z.ZodString>;
|
|
1020
1030
|
privateKey: z.ZodOptional<z.ZodString>;
|
|
1021
|
-
decryptionPvk: z.ZodOptional<z.ZodString>;
|
|
1022
|
-
additionalParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
1023
1031
|
mapping: z.ZodOptional<z.ZodObject<{
|
|
1024
1032
|
id: z.ZodString;
|
|
1025
1033
|
email: z.ZodString;
|
|
@@ -1376,7 +1384,7 @@ declare const callbackSSOShared: (options?: SSOOptions) => better_call0.StrictEn
|
|
|
1376
1384
|
}, z.core.$strip>;
|
|
1377
1385
|
allowedMediaTypes: readonly ["application/x-www-form-urlencoded", "application/json"];
|
|
1378
1386
|
}, void>;
|
|
1379
|
-
declare const
|
|
1387
|
+
declare const acsEndpoint: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
|
|
1380
1388
|
method: ("POST" | "GET")[];
|
|
1381
1389
|
body: z.ZodOptional<z.ZodObject<{
|
|
1382
1390
|
SAMLResponse: z.ZodString;
|
|
@@ -1398,28 +1406,7 @@ declare const callbackSSOSAML: (options?: SSOOptions) => better_call0.StrictEndp
|
|
|
1398
1406
|
"400": {
|
|
1399
1407
|
description: string;
|
|
1400
1408
|
};
|
|
1401
|
-
"
|
|
1402
|
-
description: string;
|
|
1403
|
-
};
|
|
1404
|
-
};
|
|
1405
|
-
};
|
|
1406
|
-
scope: "server";
|
|
1407
|
-
};
|
|
1408
|
-
}, never>;
|
|
1409
|
-
declare const acsEndpoint: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sso/saml2/sp/acs/:providerId", {
|
|
1410
|
-
method: "POST";
|
|
1411
|
-
body: z.ZodObject<{
|
|
1412
|
-
SAMLResponse: z.ZodString;
|
|
1413
|
-
RelayState: z.ZodOptional<z.ZodString>;
|
|
1414
|
-
}, z.core.$strip>;
|
|
1415
|
-
metadata: {
|
|
1416
|
-
allowedMediaTypes: string[];
|
|
1417
|
-
openapi: {
|
|
1418
|
-
operationId: string;
|
|
1419
|
-
summary: string;
|
|
1420
|
-
description: string;
|
|
1421
|
-
responses: {
|
|
1422
|
-
"302": {
|
|
1409
|
+
"404": {
|
|
1423
1410
|
description: string;
|
|
1424
1411
|
};
|
|
1425
1412
|
};
|
|
@@ -1788,7 +1775,6 @@ type SSOEndpoints<O extends SSOOptions> = {
|
|
|
1788
1775
|
signInSSO: ReturnType<typeof signInSSO>;
|
|
1789
1776
|
callbackSSO: ReturnType<typeof callbackSSO>;
|
|
1790
1777
|
callbackSSOShared: ReturnType<typeof callbackSSOShared>;
|
|
1791
|
-
callbackSSOSAML: ReturnType<typeof callbackSSOSAML>;
|
|
1792
1778
|
acsEndpoint: ReturnType<typeof acsEndpoint>;
|
|
1793
1779
|
sloEndpoint: ReturnType<typeof sloEndpoint>;
|
|
1794
1780
|
initiateSLO: ReturnType<typeof initiateSLO>;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as DataEncryptionAlgorithm, C as DEFAULT_MAX_SAML_METADATA_SIZE, D as SSOOptions, E as SAMLConfig, M as DigestAlgorithm, N as KeyEncryptionAlgorithm, O as SSOProvider, P as SignatureAlgorithm, S as DEFAULT_CLOCK_SKEW_MS, T as OIDCConfig, _ 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 DeprecatedAlgorithmBehavior, k as AlgorithmValidationOptions, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as DEFAULT_MAX_SAML_RESPONSE_SIZE, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-
|
|
1
|
+
import { A as DataEncryptionAlgorithm, C as DEFAULT_MAX_SAML_METADATA_SIZE, D as SSOOptions, E as SAMLConfig, M as DigestAlgorithm, N as KeyEncryptionAlgorithm, O as SSOProvider, P as SignatureAlgorithm, S as DEFAULT_CLOCK_SKEW_MS, T as OIDCConfig, _ 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 DeprecatedAlgorithmBehavior, k as AlgorithmValidationOptions, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as DEFAULT_MAX_SAML_RESPONSE_SIZE, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-CagV4mMx.mjs";
|
|
2
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, SSOOptions, SSOPlugin, SSOProvider, SignatureAlgorithm, TimestampValidationOptions, computeDiscoveryUrl, 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-C22JHwcK.mjs";
|
|
2
2
|
import { APIError, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
|
|
3
3
|
import { XMLParser, XMLValidator } from "fast-xml-parser";
|
|
4
4
|
import * as saml from "samlify";
|
|
@@ -693,7 +693,10 @@ async function validateInResponseTo(c, ctx) {
|
|
|
693
693
|
* could be accepted.
|
|
694
694
|
*/
|
|
695
695
|
function validateAudience(c, ctx) {
|
|
696
|
-
if (!ctx.expectedAudience)
|
|
696
|
+
if (!ctx.expectedAudience) {
|
|
697
|
+
c.context.logger.warn("Could not determine SP entity ID for audience validation; skipping", { providerId: ctx.providerId });
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
697
700
|
const audience = ctx.extract.audience;
|
|
698
701
|
if (!audience) {
|
|
699
702
|
c.context.logger.error("SAML assertion missing AudienceRestriction but audience is configured — rejecting", { providerId: ctx.providerId });
|
|
@@ -751,7 +754,6 @@ const oidcConfigSchema = z.object({
|
|
|
751
754
|
const samlConfigSchema = z.object({
|
|
752
755
|
entryPoint: z.string().url().optional(),
|
|
753
756
|
cert: z.string().optional(),
|
|
754
|
-
callbackUrl: z.string().url().optional(),
|
|
755
757
|
audience: z.string().optional(),
|
|
756
758
|
idpMetadata: z.object({
|
|
757
759
|
metadata: z.string().optional(),
|
|
@@ -783,8 +785,6 @@ const samlConfigSchema = z.object({
|
|
|
783
785
|
digestAlgorithm: z.string().optional(),
|
|
784
786
|
identifierFormat: z.string().optional(),
|
|
785
787
|
privateKey: z.string().optional(),
|
|
786
|
-
decryptionPvk: z.string().optional(),
|
|
787
|
-
additionalParams: z.record(z.string(), z.any()).optional(),
|
|
788
788
|
mapping: samlMappingSchema
|
|
789
789
|
});
|
|
790
790
|
const updateSSOProviderBodySchema = z.object({
|
|
@@ -861,7 +861,6 @@ function sanitizeProvider(provider, baseURL) {
|
|
|
861
861
|
} : void 0,
|
|
862
862
|
samlConfig: samlConfig ? {
|
|
863
863
|
entryPoint: samlConfig.entryPoint,
|
|
864
|
-
callbackUrl: samlConfig.callbackUrl,
|
|
865
864
|
audience: samlConfig.audience,
|
|
866
865
|
wantAssertionsSigned: samlConfig.wantAssertionsSigned,
|
|
867
866
|
authnRequestsSigned: samlConfig.authnRequestsSigned,
|
|
@@ -964,7 +963,6 @@ function mergeSAMLConfig(current, updates, issuer) {
|
|
|
964
963
|
issuer,
|
|
965
964
|
entryPoint: updates.entryPoint ?? current.entryPoint,
|
|
966
965
|
cert: updates.cert ?? current.cert,
|
|
967
|
-
callbackUrl: updates.callbackUrl ?? current.callbackUrl,
|
|
968
966
|
spMetadata: updates.spMetadata ?? current.spMetadata,
|
|
969
967
|
idpMetadata: updates.idpMetadata ?? current.idpMetadata,
|
|
970
968
|
mapping: updates.mapping ?? current.mapping,
|
|
@@ -1510,6 +1508,16 @@ async function parseRelayState(c) {
|
|
|
1510
1508
|
}
|
|
1511
1509
|
//#endregion
|
|
1512
1510
|
//#region src/routes/helpers.ts
|
|
1511
|
+
/**
|
|
1512
|
+
* Normalizes a PEM string by trimming leading/trailing whitespace from each
|
|
1513
|
+
* line. Native `crypto.createPrivateKey` (used by samlify 2.12+) rejects PEM
|
|
1514
|
+
* blocks with leading whitespace, which is common when keys are stored in
|
|
1515
|
+
* indented config files, environment variables, or JSON.
|
|
1516
|
+
*/
|
|
1517
|
+
function normalizePem(pem) {
|
|
1518
|
+
if (!pem) return pem;
|
|
1519
|
+
return pem.split("\n").map((line) => line.trim()).join("\n");
|
|
1520
|
+
}
|
|
1513
1521
|
async function findSAMLProvider(providerId, options, adapter) {
|
|
1514
1522
|
if (options?.defaultSSO?.length) {
|
|
1515
1523
|
const match = options.defaultSSO.find((p) => p.providerId === providerId);
|
|
@@ -1536,30 +1544,35 @@ async function findSAMLProvider(providerId, options, adapter) {
|
|
|
1536
1544
|
function createSP(config, baseURL, providerId, opts) {
|
|
1537
1545
|
const spData = config.spMetadata;
|
|
1538
1546
|
const sloLocation = `${baseURL}/sso/saml2/sp/slo/${providerId}`;
|
|
1539
|
-
const acsUrl =
|
|
1540
|
-
|
|
1547
|
+
const acsUrl = `${baseURL}/sso/saml2/sp/acs/${providerId}`;
|
|
1548
|
+
let metadata = spData?.metadata;
|
|
1549
|
+
if (!metadata) metadata = saml.SPMetadata({
|
|
1541
1550
|
entityID: spData?.entityID || config.issuer,
|
|
1542
|
-
assertionConsumerService:
|
|
1551
|
+
assertionConsumerService: [{
|
|
1543
1552
|
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
1544
1553
|
Location: acsUrl
|
|
1545
1554
|
}],
|
|
1546
|
-
singleLogoutService: [{
|
|
1555
|
+
singleLogoutService: opts?.sloOptions ? [{
|
|
1547
1556
|
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
1548
1557
|
Location: sloLocation
|
|
1549
1558
|
}, {
|
|
1550
1559
|
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
|
1551
1560
|
Location: sloLocation
|
|
1552
|
-
}],
|
|
1561
|
+
}] : void 0,
|
|
1553
1562
|
wantMessageSigned: config.wantAssertionsSigned || false,
|
|
1563
|
+
authnRequestsSigned: config.authnRequestsSigned || false,
|
|
1564
|
+
nameIDFormat: config.identifierFormat ? [config.identifierFormat] : void 0
|
|
1565
|
+
}).getMetadata() || "";
|
|
1566
|
+
return saml.ServiceProvider({
|
|
1567
|
+
metadata,
|
|
1568
|
+
allowCreate: true,
|
|
1554
1569
|
wantLogoutRequestSigned: opts?.sloOptions?.wantLogoutRequestSigned ?? false,
|
|
1555
1570
|
wantLogoutResponseSigned: opts?.sloOptions?.wantLogoutResponseSigned ?? false,
|
|
1556
|
-
|
|
1557
|
-
privateKey: spData?.privateKey || config.privateKey,
|
|
1571
|
+
privateKey: normalizePem(spData?.privateKey || config.privateKey),
|
|
1558
1572
|
privateKeyPass: spData?.privateKeyPass,
|
|
1559
1573
|
isAssertionEncrypted: spData?.isAssertionEncrypted || false,
|
|
1560
|
-
encPrivateKey: spData?.encPrivateKey,
|
|
1574
|
+
encPrivateKey: normalizePem(spData?.encPrivateKey),
|
|
1561
1575
|
encPrivateKeyPass: spData?.encPrivateKeyPass,
|
|
1562
|
-
nameIDFormat: config.identifierFormat ? [config.identifierFormat] : void 0,
|
|
1563
1576
|
relayState: opts?.relayState
|
|
1564
1577
|
});
|
|
1565
1578
|
}
|
|
@@ -1567,10 +1580,10 @@ function createIdP(config) {
|
|
|
1567
1580
|
const idpData = config.idpMetadata;
|
|
1568
1581
|
if (idpData?.metadata) return saml.IdentityProvider({
|
|
1569
1582
|
metadata: idpData.metadata,
|
|
1570
|
-
privateKey: idpData.privateKey,
|
|
1583
|
+
privateKey: normalizePem(idpData.privateKey),
|
|
1571
1584
|
privateKeyPass: idpData.privateKeyPass,
|
|
1572
1585
|
isAssertionEncrypted: idpData.isAssertionEncrypted,
|
|
1573
|
-
encPrivateKey: idpData.encPrivateKey,
|
|
1586
|
+
encPrivateKey: normalizePem(idpData.encPrivateKey),
|
|
1574
1587
|
encPrivateKeyPass: idpData.encPrivateKeyPass
|
|
1575
1588
|
});
|
|
1576
1589
|
return saml.IdentityProvider({
|
|
@@ -1580,10 +1593,10 @@ function createIdP(config) {
|
|
|
1580
1593
|
Location: config.entryPoint
|
|
1581
1594
|
}],
|
|
1582
1595
|
singleLogoutService: idpData?.singleLogoutService,
|
|
1583
|
-
signingCert: idpData?.cert || config.cert,
|
|
1596
|
+
signingCert: normalizePem(idpData?.cert || config.cert),
|
|
1584
1597
|
wantAuthnRequestsSigned: config.authnRequestsSigned || false,
|
|
1585
1598
|
isAssertionEncrypted: idpData?.isAssertionEncrypted || false,
|
|
1586
|
-
encPrivateKey: idpData?.encPrivateKey,
|
|
1599
|
+
encPrivateKey: normalizePem(idpData?.encPrivateKey),
|
|
1587
1600
|
encPrivateKeyPass: idpData?.encPrivateKeyPass
|
|
1588
1601
|
});
|
|
1589
1602
|
}
|
|
@@ -1694,8 +1707,8 @@ function extractAssertionId(samlContent) {
|
|
|
1694
1707
|
/**
|
|
1695
1708
|
* Unified SAML response processing pipeline.
|
|
1696
1709
|
*
|
|
1697
|
-
*
|
|
1698
|
-
*
|
|
1710
|
+
* The `/sso/saml2/sp/acs/:providerId` endpoint delegates to this function.
|
|
1711
|
+
* It handles the full lifecycle: provider lookup,
|
|
1699
1712
|
* SP/IdP construction, response validation, session creation, and redirect
|
|
1700
1713
|
* URL computation.
|
|
1701
1714
|
*/
|
|
@@ -1718,7 +1731,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
1718
1731
|
if (!parsedSamlConfig) throw new APIError("BAD_REQUEST", { message: "Invalid SAML configuration" });
|
|
1719
1732
|
const sp = createSP(parsedSamlConfig, ctx.context.baseURL, providerId);
|
|
1720
1733
|
const idp = createIdP(parsedSamlConfig);
|
|
1721
|
-
const samlRedirectUrl = getSafeRedirectUrl(relayState?.callbackURL
|
|
1734
|
+
const samlRedirectUrl = getSafeRedirectUrl(relayState?.callbackURL, params.currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
1722
1735
|
validateSingleAssertion(SAMLResponse);
|
|
1723
1736
|
let parsedResponse;
|
|
1724
1737
|
try {
|
|
@@ -1755,7 +1768,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
1755
1768
|
});
|
|
1756
1769
|
validateAudience(ctx, {
|
|
1757
1770
|
extract,
|
|
1758
|
-
expectedAudience: parsedSamlConfig.audience,
|
|
1771
|
+
expectedAudience: parsedSamlConfig.audience || sp.entityMeta.getEntityID(),
|
|
1759
1772
|
providerId,
|
|
1760
1773
|
redirectUrl: samlRedirectUrl
|
|
1761
1774
|
});
|
|
@@ -1815,7 +1828,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
1815
1828
|
throw new APIError("BAD_REQUEST", { message: "Unable to extract user ID or email from SAML response" });
|
|
1816
1829
|
}
|
|
1817
1830
|
const isTrustedProvider = ctx.context.trustedProviders.includes(providerId) || "domainVerified" in provider && !!provider.domainVerified && validateEmailDomain(userInfo.email, provider.domain);
|
|
1818
|
-
const
|
|
1831
|
+
const postAuthRedirect = relayState?.callbackURL || ctx.context.baseURL;
|
|
1819
1832
|
const result = await handleOAuthUserInfo(ctx, {
|
|
1820
1833
|
userInfo: {
|
|
1821
1834
|
email: userInfo.email,
|
|
@@ -1829,7 +1842,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
1829
1842
|
accessToken: "",
|
|
1830
1843
|
refreshToken: ""
|
|
1831
1844
|
},
|
|
1832
|
-
callbackURL:
|
|
1845
|
+
callbackURL: postAuthRedirect,
|
|
1833
1846
|
disableSignUp: options?.disableImplicitSignUp,
|
|
1834
1847
|
isTrustedProvider
|
|
1835
1848
|
});
|
|
@@ -1876,7 +1889,7 @@ async function processSAMLResponse(ctx, params, options) {
|
|
|
1876
1889
|
expiresAt: session.expiresAt
|
|
1877
1890
|
}).catch((e) => ctx.context.logger.warn("Failed to create SAML session lookup record", e));
|
|
1878
1891
|
}
|
|
1879
|
-
return getSafeRedirectUrl(relayState?.callbackURL
|
|
1892
|
+
return getSafeRedirectUrl(relayState?.callbackURL, currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
1880
1893
|
}
|
|
1881
1894
|
//#endregion
|
|
1882
1895
|
//#region src/routes/sso.ts
|
|
@@ -1893,10 +1906,7 @@ function getOIDCRedirectURI(baseURL, providerId, options) {
|
|
|
1893
1906
|
}
|
|
1894
1907
|
return `${baseURL}/sso/callback/${providerId}`;
|
|
1895
1908
|
}
|
|
1896
|
-
const spMetadataQuerySchema = z.object({
|
|
1897
|
-
providerId: z.string(),
|
|
1898
|
-
format: z.enum(["xml", "json"]).default("xml")
|
|
1899
|
-
});
|
|
1909
|
+
const spMetadataQuerySchema = z.object({ providerId: z.string() });
|
|
1900
1910
|
const spMetadata = (options) => {
|
|
1901
1911
|
return createAuthEndpoint("/sso/saml2/sp/metadata", {
|
|
1902
1912
|
method: "GET",
|
|
@@ -1918,25 +1928,10 @@ const spMetadata = (options) => {
|
|
|
1918
1928
|
if (!provider) throw new APIError("NOT_FOUND", { message: "No provider found for the given providerId" });
|
|
1919
1929
|
const parsedSamlConfig = safeJsonParse(provider.samlConfig);
|
|
1920
1930
|
if (!parsedSamlConfig) throw new APIError("BAD_REQUEST", { message: "Invalid SAML configuration" });
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
}, {
|
|
1926
|
-
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
|
1927
|
-
Location: sloLocation
|
|
1928
|
-
}] : void 0;
|
|
1929
|
-
const sp = parsedSamlConfig.spMetadata.metadata ? saml.ServiceProvider({ metadata: parsedSamlConfig.spMetadata.metadata }) : saml.SPMetadata({
|
|
1930
|
-
entityID: parsedSamlConfig.spMetadata?.entityID || parsedSamlConfig.issuer,
|
|
1931
|
-
assertionConsumerService: [{
|
|
1932
|
-
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
1933
|
-
Location: parsedSamlConfig.callbackUrl || `${ctx.context.baseURL}/sso/saml2/sp/acs/${ctx.query.providerId}`
|
|
1934
|
-
}],
|
|
1935
|
-
singleLogoutService,
|
|
1936
|
-
wantMessageSigned: parsedSamlConfig.wantAssertionsSigned || false,
|
|
1937
|
-
authnRequestsSigned: parsedSamlConfig.authnRequestsSigned || false,
|
|
1938
|
-
nameIDFormat: parsedSamlConfig.identifierFormat ? [parsedSamlConfig.identifierFormat] : void 0
|
|
1939
|
-
});
|
|
1931
|
+
const sp = createSP(parsedSamlConfig, ctx.context.baseURL, ctx.query.providerId, options?.saml?.enableSingleLogout ? { sloOptions: {
|
|
1932
|
+
wantLogoutRequestSigned: options?.saml?.wantLogoutRequestSigned,
|
|
1933
|
+
wantLogoutResponseSigned: options?.saml?.wantLogoutResponseSigned
|
|
1934
|
+
} } : void 0);
|
|
1940
1935
|
return new Response(sp.getMetadata(), { headers: { "Content-Type": "application/xml" } });
|
|
1941
1936
|
});
|
|
1942
1937
|
};
|
|
@@ -1974,7 +1969,6 @@ const ssoProviderBodySchema = z.object({
|
|
|
1974
1969
|
samlConfig: z.object({
|
|
1975
1970
|
entryPoint: z.string({}).meta({ description: "The entry point of the provider" }),
|
|
1976
1971
|
cert: z.string({}).meta({ description: "The certificate of the provider" }),
|
|
1977
|
-
callbackUrl: z.string({}).meta({ description: "The callback URL of the provider" }),
|
|
1978
1972
|
audience: z.string().optional(),
|
|
1979
1973
|
idpMetadata: z.object({
|
|
1980
1974
|
metadata: z.string().optional(),
|
|
@@ -1999,15 +1993,13 @@ const ssoProviderBodySchema = z.object({
|
|
|
1999
1993
|
isAssertionEncrypted: z.boolean().optional(),
|
|
2000
1994
|
encPrivateKey: z.string().optional(),
|
|
2001
1995
|
encPrivateKeyPass: z.string().optional()
|
|
2002
|
-
}),
|
|
1996
|
+
}).optional(),
|
|
2003
1997
|
wantAssertionsSigned: z.boolean().optional(),
|
|
2004
1998
|
authnRequestsSigned: z.boolean().optional(),
|
|
2005
1999
|
signatureAlgorithm: z.string().optional(),
|
|
2006
2000
|
digestAlgorithm: z.string().optional(),
|
|
2007
2001
|
identifierFormat: z.string().optional(),
|
|
2008
2002
|
privateKey: z.string().optional(),
|
|
2009
|
-
decryptionPvk: z.string().optional(),
|
|
2010
|
-
additionalParams: z.record(z.string(), z.any()).optional(),
|
|
2011
2003
|
mapping: z.object({
|
|
2012
2004
|
id: z.string({}).meta({ description: "Field mapping for user ID (defaults to 'nameID')" }),
|
|
2013
2005
|
email: z.string({}).meta({ description: "Field mapping for email (defaults to 'email')" }),
|
|
@@ -2321,7 +2313,6 @@ const registerSSOProvider = (options) => {
|
|
|
2321
2313
|
issuer: body.issuer,
|
|
2322
2314
|
entryPoint: body.samlConfig.entryPoint,
|
|
2323
2315
|
cert: body.samlConfig.cert,
|
|
2324
|
-
callbackUrl: body.samlConfig.callbackUrl,
|
|
2325
2316
|
audience: body.samlConfig.audience,
|
|
2326
2317
|
idpMetadata: body.samlConfig.idpMetadata,
|
|
2327
2318
|
spMetadata: body.samlConfig.spMetadata,
|
|
@@ -2331,8 +2322,6 @@ const registerSSOProvider = (options) => {
|
|
|
2331
2322
|
digestAlgorithm: body.samlConfig.digestAlgorithm,
|
|
2332
2323
|
identifierFormat: body.samlConfig.identifierFormat,
|
|
2333
2324
|
privateKey: body.samlConfig.privateKey,
|
|
2334
|
-
decryptionPvk: body.samlConfig.decryptionPvk,
|
|
2335
|
-
additionalParams: body.samlConfig.additionalParams,
|
|
2336
2325
|
mapping: body.samlConfig.mapping
|
|
2337
2326
|
}) : null,
|
|
2338
2327
|
organizationId: body.organizationId,
|
|
@@ -2539,46 +2528,8 @@ const signInSSO = (options) => {
|
|
|
2539
2528
|
if (!parsedSamlConfig) throw new APIError("BAD_REQUEST", { message: "Invalid SAML configuration" });
|
|
2540
2529
|
if (parsedSamlConfig.authnRequestsSigned && !parsedSamlConfig.spMetadata?.privateKey && !parsedSamlConfig.privateKey) throw new APIError("BAD_REQUEST", { message: "authnRequestsSigned is enabled but no privateKey provided in spMetadata or samlConfig" });
|
|
2541
2530
|
const { state: relayState } = await generateRelayState(ctx, void 0, false);
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
entityID: parsedSamlConfig.spMetadata?.entityID || parsedSamlConfig.issuer,
|
|
2545
|
-
assertionConsumerService: [{
|
|
2546
|
-
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
2547
|
-
Location: parsedSamlConfig.callbackUrl || `${ctx.context.baseURL}/sso/saml2/sp/acs/${provider.providerId}`
|
|
2548
|
-
}],
|
|
2549
|
-
wantMessageSigned: parsedSamlConfig.wantAssertionsSigned || false,
|
|
2550
|
-
authnRequestsSigned: parsedSamlConfig.authnRequestsSigned || false,
|
|
2551
|
-
nameIDFormat: parsedSamlConfig.identifierFormat ? [parsedSamlConfig.identifierFormat] : void 0
|
|
2552
|
-
}).getMetadata() || "";
|
|
2553
|
-
const sp = saml.ServiceProvider({
|
|
2554
|
-
metadata,
|
|
2555
|
-
allowCreate: true,
|
|
2556
|
-
privateKey: parsedSamlConfig.spMetadata?.privateKey || parsedSamlConfig.privateKey,
|
|
2557
|
-
privateKeyPass: parsedSamlConfig.spMetadata?.privateKeyPass,
|
|
2558
|
-
relayState
|
|
2559
|
-
});
|
|
2560
|
-
const idpData = parsedSamlConfig.idpMetadata;
|
|
2561
|
-
let idp;
|
|
2562
|
-
if (!idpData?.metadata) idp = saml.IdentityProvider({
|
|
2563
|
-
entityID: idpData?.entityID || parsedSamlConfig.issuer,
|
|
2564
|
-
singleSignOnService: idpData?.singleSignOnService || [{
|
|
2565
|
-
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
|
2566
|
-
Location: parsedSamlConfig.entryPoint
|
|
2567
|
-
}],
|
|
2568
|
-
signingCert: idpData?.cert || parsedSamlConfig.cert,
|
|
2569
|
-
wantAuthnRequestsSigned: parsedSamlConfig.authnRequestsSigned || false,
|
|
2570
|
-
isAssertionEncrypted: idpData?.isAssertionEncrypted || false,
|
|
2571
|
-
encPrivateKey: idpData?.encPrivateKey,
|
|
2572
|
-
encPrivateKeyPass: idpData?.encPrivateKeyPass
|
|
2573
|
-
});
|
|
2574
|
-
else idp = saml.IdentityProvider({
|
|
2575
|
-
metadata: idpData.metadata,
|
|
2576
|
-
privateKey: idpData.privateKey,
|
|
2577
|
-
privateKeyPass: idpData.privateKeyPass,
|
|
2578
|
-
isAssertionEncrypted: idpData.isAssertionEncrypted,
|
|
2579
|
-
encPrivateKey: idpData.encPrivateKey,
|
|
2580
|
-
encPrivateKeyPass: idpData.encPrivateKeyPass
|
|
2581
|
-
});
|
|
2531
|
+
const sp = createSP(parsedSamlConfig, ctx.context.baseURL, provider.providerId, { relayState });
|
|
2532
|
+
const idp = createIdP(parsedSamlConfig);
|
|
2582
2533
|
const loginRequest = sp.createLoginRequest(idp, "redirect");
|
|
2583
2534
|
if (!loginRequest) throw new APIError("BAD_REQUEST", { message: "Invalid SAML request" });
|
|
2584
2535
|
if (loginRequest.id && options?.saml?.enableInResponseToValidation !== false) {
|
|
@@ -2853,72 +2804,42 @@ const callbackSSOShared = (options) => {
|
|
|
2853
2804
|
return handleOIDCCallback(ctx, options, providerId, stateData);
|
|
2854
2805
|
});
|
|
2855
2806
|
};
|
|
2856
|
-
const
|
|
2807
|
+
const acsEndpointBodySchema = z.object({
|
|
2857
2808
|
SAMLResponse: z.string(),
|
|
2858
2809
|
RelayState: z.string().optional()
|
|
2859
2810
|
});
|
|
2860
|
-
const
|
|
2861
|
-
return createAuthEndpoint("/sso/saml2/
|
|
2811
|
+
const acsEndpoint = (options) => {
|
|
2812
|
+
return createAuthEndpoint("/sso/saml2/sp/acs/:providerId", {
|
|
2862
2813
|
method: ["GET", "POST"],
|
|
2863
|
-
body:
|
|
2814
|
+
body: acsEndpointBodySchema.optional(),
|
|
2864
2815
|
query: z.object({ RelayState: z.string().optional() }).optional(),
|
|
2865
2816
|
metadata: {
|
|
2866
2817
|
...HIDE_METADATA,
|
|
2867
2818
|
allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
|
|
2868
2819
|
openapi: {
|
|
2869
|
-
operationId: "
|
|
2870
|
-
summary: "
|
|
2871
|
-
description: "
|
|
2820
|
+
operationId: "handleSAMLAssertionConsumerService",
|
|
2821
|
+
summary: "SAML Assertion Consumer Service",
|
|
2822
|
+
description: "Handles SAML responses from IdP after successful authentication. Supports GET for post-auth redirects and POST for SAML response processing.",
|
|
2872
2823
|
responses: {
|
|
2873
|
-
"302": { description: "Redirects
|
|
2874
|
-
"400": { description: "
|
|
2875
|
-
"
|
|
2824
|
+
"302": { description: "Redirects after authentication (success or error with query params)" },
|
|
2825
|
+
"400": { description: "Missing SAMLResponse in POST body" },
|
|
2826
|
+
"404": { description: "SAML provider not found" }
|
|
2876
2827
|
}
|
|
2877
2828
|
}
|
|
2878
2829
|
}
|
|
2879
2830
|
}, async (ctx) => {
|
|
2880
2831
|
const { providerId } = ctx.params;
|
|
2832
|
+
const currentCallbackPath = `${ctx.context.baseURL}/sso/saml2/sp/acs/${providerId}`;
|
|
2881
2833
|
const appOrigin = new URL(ctx.context.baseURL).origin;
|
|
2882
|
-
const errorURL = ctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`;
|
|
2883
|
-
const currentCallbackPath = `${ctx.context.baseURL}/sso/saml2/callback/${providerId}`;
|
|
2884
2834
|
if (ctx.method === "GET" && !ctx.body?.SAMLResponse) {
|
|
2885
|
-
if (!(await getSessionFromCtx(ctx))?.session)
|
|
2835
|
+
if (!(await getSessionFromCtx(ctx))?.session) {
|
|
2836
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`;
|
|
2837
|
+
throw ctx.redirect(`${errorURL}?error=invalid_request`);
|
|
2838
|
+
}
|
|
2886
2839
|
const relayState = ctx.query?.RelayState;
|
|
2887
|
-
|
|
2888
|
-
throw ctx.redirect(safeRedirectUrl);
|
|
2840
|
+
throw ctx.redirect(getSafeRedirectUrl(relayState, currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings)));
|
|
2889
2841
|
}
|
|
2890
2842
|
if (!ctx.body?.SAMLResponse) throw new APIError("BAD_REQUEST", { message: "SAMLResponse is required for POST requests" });
|
|
2891
|
-
const safeRedirectUrl = await processSAMLResponse(ctx, {
|
|
2892
|
-
SAMLResponse: ctx.body.SAMLResponse,
|
|
2893
|
-
RelayState: ctx.body.RelayState,
|
|
2894
|
-
providerId,
|
|
2895
|
-
currentCallbackPath
|
|
2896
|
-
}, options);
|
|
2897
|
-
throw ctx.redirect(safeRedirectUrl);
|
|
2898
|
-
});
|
|
2899
|
-
};
|
|
2900
|
-
const acsEndpointBodySchema = z.object({
|
|
2901
|
-
SAMLResponse: z.string(),
|
|
2902
|
-
RelayState: z.string().optional()
|
|
2903
|
-
});
|
|
2904
|
-
const acsEndpoint = (options) => {
|
|
2905
|
-
return createAuthEndpoint("/sso/saml2/sp/acs/:providerId", {
|
|
2906
|
-
method: "POST",
|
|
2907
|
-
body: acsEndpointBodySchema,
|
|
2908
|
-
metadata: {
|
|
2909
|
-
...HIDE_METADATA,
|
|
2910
|
-
allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
|
|
2911
|
-
openapi: {
|
|
2912
|
-
operationId: "handleSAMLAssertionConsumerService",
|
|
2913
|
-
summary: "SAML Assertion Consumer Service",
|
|
2914
|
-
description: "Handles SAML responses from IdP after successful authentication",
|
|
2915
|
-
responses: { "302": { description: "Redirects to the callback URL after successful authentication" } }
|
|
2916
|
-
}
|
|
2917
|
-
}
|
|
2918
|
-
}, async (ctx) => {
|
|
2919
|
-
const { providerId } = ctx.params;
|
|
2920
|
-
const currentCallbackPath = `${ctx.context.baseURL}/sso/saml2/sp/acs/${providerId}`;
|
|
2921
|
-
const appOrigin = new URL(ctx.context.baseURL).origin;
|
|
2922
2843
|
try {
|
|
2923
2844
|
const safeRedirectUrl = await processSAMLResponse(ctx, {
|
|
2924
2845
|
SAMLResponse: ctx.body.SAMLResponse,
|
|
@@ -2930,9 +2851,8 @@ const acsEndpoint = (options) => {
|
|
|
2930
2851
|
} catch (error) {
|
|
2931
2852
|
if (error instanceof Response || error && typeof error === "object" && "status" in error && error.status === 302) throw error;
|
|
2932
2853
|
if (error instanceof APIError && error.statusCode === 400) {
|
|
2933
|
-
const
|
|
2934
|
-
const
|
|
2935
|
-
const redirectUrl = getSafeRedirectUrl(ctx.body.RelayState || void 0, currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
2854
|
+
const errorCode = (error.body?.code || "saml_error").toLowerCase();
|
|
2855
|
+
const redirectUrl = getSafeRedirectUrl(ctx.body?.RelayState || void 0, currentCallbackPath, appOrigin, (url, settings) => ctx.context.isTrustedOrigin(url, settings));
|
|
2936
2856
|
throw ctx.redirect(`${redirectUrl}${redirectUrl.includes("?") ? "&" : "?"}error=${encodeURIComponent(errorCode)}&error_description=${encodeURIComponent(error.message)}`);
|
|
2937
2857
|
}
|
|
2938
2858
|
throw error;
|
|
@@ -3107,11 +3027,7 @@ saml.setSchemaValidator({ async validate(xml) {
|
|
|
3107
3027
|
* These endpoints receive POST requests from external Identity Providers,
|
|
3108
3028
|
* which won't have a matching Origin header.
|
|
3109
3029
|
*/
|
|
3110
|
-
const SAML_SKIP_ORIGIN_CHECK_PATHS = [
|
|
3111
|
-
"/sso/saml2/callback",
|
|
3112
|
-
"/sso/saml2/sp/acs",
|
|
3113
|
-
"/sso/saml2/sp/slo"
|
|
3114
|
-
];
|
|
3030
|
+
const SAML_SKIP_ORIGIN_CHECK_PATHS = ["/sso/saml2/sp/acs", "/sso/saml2/sp/slo"];
|
|
3115
3031
|
function sso(options) {
|
|
3116
3032
|
const optionsWithStore = options;
|
|
3117
3033
|
let endpoints = {
|
|
@@ -3120,7 +3036,6 @@ function sso(options) {
|
|
|
3120
3036
|
signInSSO: signInSSO(optionsWithStore),
|
|
3121
3037
|
callbackSSO: callbackSSO(optionsWithStore),
|
|
3122
3038
|
callbackSSOShared: callbackSSOShared(optionsWithStore),
|
|
3123
|
-
callbackSSOSAML: callbackSSOSAML(optionsWithStore),
|
|
3124
3039
|
acsEndpoint: acsEndpoint(optionsWithStore),
|
|
3125
3040
|
sloEndpoint: sloEndpoint(optionsWithStore),
|
|
3126
3041
|
initiateSLO: initiateSLO(optionsWithStore),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/sso",
|
|
3
|
-
"version": "1.7.0-beta.
|
|
3
|
+
"version": "1.7.0-beta.1",
|
|
4
4
|
"description": "SSO plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"fast-xml-parser": "^5.5.7",
|
|
60
60
|
"jose": "^6.1.3",
|
|
61
|
-
"samlify": "~2.
|
|
61
|
+
"samlify": "~2.12.0",
|
|
62
62
|
"tldts": "^6.1.0",
|
|
63
63
|
"zod": "^4.3.6"
|
|
64
64
|
},
|
|
@@ -70,15 +70,15 @@
|
|
|
70
70
|
"express": "^5.2.1",
|
|
71
71
|
"oauth2-mock-server": "^8.2.2",
|
|
72
72
|
"tsdown": "0.21.1",
|
|
73
|
-
"@better-auth/core": "1.7.0-beta.
|
|
74
|
-
"better-auth": "1.7.0-beta.
|
|
73
|
+
"@better-auth/core": "1.7.0-beta.1",
|
|
74
|
+
"better-auth": "1.7.0-beta.1"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@better-auth/utils": "0.4.0",
|
|
78
78
|
"@better-fetch/fetch": "1.1.21",
|
|
79
79
|
"better-call": "1.3.5",
|
|
80
|
-
"@better-auth/core": "^1.7.0-beta.
|
|
81
|
-
"better-auth": "^1.7.0-beta.
|
|
80
|
+
"@better-auth/core": "^1.7.0-beta.1",
|
|
81
|
+
"better-auth": "^1.7.0-beta.1"
|
|
82
82
|
},
|
|
83
83
|
"scripts": {
|
|
84
84
|
"build": "tsdown",
|