@better-auth/sso 1.3.0-beta.1 → 1.3.0-beta.11
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/.turbo/turbo-build.log +4 -4
- package/dist/client.d.cts +1 -1
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/index.cjs +111 -84
- package/dist/index.d.cts +57 -319
- package/dist/index.d.mts +57 -319
- package/dist/index.d.ts +57 -319
- package/dist/index.mjs +68 -42
- package/package.json +3 -3
- package/src/index.ts +115 -42
- package/src/oidc.test.ts +101 -0
- package/src/saml.test.ts +292 -107
package/src/saml.test.ts
CHANGED
|
@@ -10,16 +10,23 @@ import {
|
|
|
10
10
|
import { betterAuth } from "better-auth";
|
|
11
11
|
import { memoryAdapter } from "better-auth/adapters/memory";
|
|
12
12
|
import { createAuthClient } from "better-auth/client";
|
|
13
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
13
14
|
import { setCookieToHeader } from "better-auth/cookies";
|
|
14
15
|
import { bearer } from "better-auth/plugins";
|
|
15
|
-
import { IdentityProvider, ServiceProvider } from "samlify";
|
|
16
16
|
import { sso } from ".";
|
|
17
17
|
import { ssoClient } from "./client";
|
|
18
18
|
import { createServer } from "http";
|
|
19
19
|
import * as saml from "samlify";
|
|
20
|
+
import type {
|
|
21
|
+
Application as ExpressApp,
|
|
22
|
+
Request as ExpressRequest,
|
|
23
|
+
Response as ExpressResponse,
|
|
24
|
+
} from "express";
|
|
25
|
+
// @ts-ignore
|
|
20
26
|
import express from "express";
|
|
21
27
|
import bodyParser from "body-parser";
|
|
22
28
|
import { randomUUID } from "crypto";
|
|
29
|
+
import { getTestInstanceMemory } from "better-auth/test";
|
|
23
30
|
|
|
24
31
|
const spMetadata = `
|
|
25
32
|
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:3001/api/sso/saml2/sp/metadata">
|
|
@@ -346,122 +353,146 @@ const createTemplateCallback =
|
|
|
346
353
|
context: saml.SamlLib.replaceTagsByValue(template, tagValues),
|
|
347
354
|
};
|
|
348
355
|
};
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
});
|
|
394
|
-
this.app.get("/api/sso/saml2/idp/post", async (req, res) => {
|
|
356
|
+
|
|
357
|
+
const createMockSAMLIdP = (port: number) => {
|
|
358
|
+
const app: ExpressApp = express();
|
|
359
|
+
let server: ReturnType<typeof createServer> | undefined;
|
|
360
|
+
|
|
361
|
+
app.use(bodyParser.urlencoded({ extended: true }));
|
|
362
|
+
app.use(bodyParser.json());
|
|
363
|
+
|
|
364
|
+
const idp = saml.IdentityProvider({
|
|
365
|
+
metadata: idpMetadata,
|
|
366
|
+
privateKey: idPk,
|
|
367
|
+
isAssertionEncrypted: false,
|
|
368
|
+
privateKeyPass: "jXmKf9By6ruLnUdRo90G",
|
|
369
|
+
loginResponseTemplate: {
|
|
370
|
+
context:
|
|
371
|
+
'<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AttributeStatement}</saml:Assertion></samlp:Response>',
|
|
372
|
+
attributes: [
|
|
373
|
+
{
|
|
374
|
+
name: "firstName",
|
|
375
|
+
valueTag: "firstName",
|
|
376
|
+
nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
|
|
377
|
+
valueXsiType: "xs:string",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "lastName",
|
|
381
|
+
valueTag: "lastName",
|
|
382
|
+
nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
|
|
383
|
+
valueXsiType: "xs:string",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: "email",
|
|
387
|
+
valueTag: "email",
|
|
388
|
+
nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
|
|
389
|
+
valueXsiType: "xs:string",
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
const sp = saml.ServiceProvider({
|
|
395
|
+
metadata: spMetadata,
|
|
396
|
+
});
|
|
397
|
+
app.get(
|
|
398
|
+
"/api/sso/saml2/idp/post",
|
|
399
|
+
async (req: ExpressRequest, res: ExpressResponse) => {
|
|
395
400
|
const user = { emailAddress: "test@email.com", famName: "hello world" };
|
|
396
|
-
const { context, entityEndpoint } = await
|
|
397
|
-
|
|
401
|
+
const { context, entityEndpoint } = await idp.createLoginResponse(
|
|
402
|
+
sp,
|
|
398
403
|
{} as any,
|
|
399
404
|
saml.Constants.wording.binding.post,
|
|
400
405
|
user,
|
|
401
|
-
createTemplateCallback(
|
|
406
|
+
createTemplateCallback(idp, sp, user.emailAddress),
|
|
402
407
|
);
|
|
403
408
|
res.status(200).send({ samlResponse: context, entityEndpoint });
|
|
404
|
-
}
|
|
405
|
-
|
|
409
|
+
},
|
|
410
|
+
);
|
|
411
|
+
app.get(
|
|
412
|
+
"/api/sso/saml2/idp/redirect",
|
|
413
|
+
async (req: ExpressRequest, res: ExpressResponse) => {
|
|
406
414
|
const user = { emailAddress: "test@email.com", famName: "hello world" };
|
|
407
|
-
const { context, entityEndpoint } = await
|
|
408
|
-
|
|
415
|
+
const { context, entityEndpoint } = await idp.createLoginResponse(
|
|
416
|
+
sp,
|
|
409
417
|
{} as any,
|
|
410
418
|
saml.Constants.wording.binding.post,
|
|
411
419
|
user,
|
|
412
|
-
createTemplateCallback(
|
|
420
|
+
createTemplateCallback(idp, sp, user.emailAddress),
|
|
413
421
|
);
|
|
414
422
|
res.status(200).send({ samlResponse: context, entityEndpoint });
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
423
|
+
},
|
|
424
|
+
);
|
|
425
|
+
app.post("/api/sso/saml2/sp/acs", async (req: any, res: any) => {
|
|
426
|
+
try {
|
|
427
|
+
const parseResult = await sp.parseLoginResponse(
|
|
428
|
+
idp,
|
|
429
|
+
saml.Constants.wording.binding.post,
|
|
430
|
+
req,
|
|
431
|
+
);
|
|
432
|
+
const { extract } = parseResult;
|
|
433
|
+
const { attributes } = extract;
|
|
434
|
+
const relayState = req.body.RelayState;
|
|
435
|
+
if (relayState) {
|
|
436
|
+
return res.status(200).send({ relayState, attributes });
|
|
437
|
+
} else {
|
|
438
|
+
return res
|
|
439
|
+
.status(200)
|
|
440
|
+
.send({ extract, message: "RelayState is missing." });
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error("Error handling SAML ACS endpoint:", error);
|
|
444
|
+
res.status(500).send({ error: "Failed to process SAML response." });
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
app.post(
|
|
448
|
+
"/api/sso/saml2/callback/:providerId",
|
|
449
|
+
async (req: ExpressRequest, res: ExpressResponse) => {
|
|
450
|
+
const { SAMLResponse, RelayState } = req.body;
|
|
418
451
|
try {
|
|
419
|
-
const parseResult = await
|
|
420
|
-
|
|
452
|
+
const parseResult = await sp.parseLoginResponse(
|
|
453
|
+
idp,
|
|
421
454
|
saml.Constants.wording.binding.post,
|
|
422
|
-
|
|
455
|
+
{ body: { SAMLResponse } },
|
|
423
456
|
);
|
|
424
|
-
|
|
425
|
-
const { attributes } = extract;
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
return res.status(200).send({ relayState, attributes });
|
|
429
|
-
} else {
|
|
430
|
-
return res
|
|
431
|
-
.status(200)
|
|
432
|
-
.send({ extract, message: "RelayState is missing." });
|
|
433
|
-
}
|
|
457
|
+
|
|
458
|
+
const { attributes, nameID } = parseResult.extract;
|
|
459
|
+
|
|
460
|
+
res.redirect(302, RelayState || "http://localhost:3000/dashboard");
|
|
434
461
|
} catch (error) {
|
|
435
|
-
console.error("Error
|
|
436
|
-
res.status(500).send({ error: "Failed to process SAML response
|
|
462
|
+
console.error("Error processing SAML callback:", error);
|
|
463
|
+
res.status(500).send({ error: "Failed to process SAML response" });
|
|
437
464
|
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
app.get(
|
|
468
|
+
"/api/sso/saml2/idp/metadata",
|
|
469
|
+
(req: ExpressRequest, res: ExpressResponse) => {
|
|
470
|
+
res.type("application/xml");
|
|
471
|
+
res.send(idpMetadata);
|
|
472
|
+
},
|
|
473
|
+
);
|
|
474
|
+
const start = () =>
|
|
475
|
+
new Promise<void>((resolve) => {
|
|
476
|
+
app.use(bodyParser.urlencoded({ extended: true }));
|
|
477
|
+
server = app.listen(port, () => {
|
|
478
|
+
console.log(`Mock SAML IdP running on port ${port}`);
|
|
446
479
|
resolve();
|
|
447
480
|
});
|
|
448
481
|
});
|
|
449
|
-
}
|
|
450
482
|
|
|
451
|
-
stop()
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
483
|
+
const stop = () =>
|
|
484
|
+
new Promise<void>((resolve, reject) => {
|
|
485
|
+
app.use(bodyParser.urlencoded({ extended: true }));
|
|
486
|
+
server?.close((err) => {
|
|
455
487
|
if (err) reject(err);
|
|
456
488
|
else resolve();
|
|
457
489
|
});
|
|
458
490
|
});
|
|
459
|
-
}
|
|
460
491
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
}
|
|
492
|
+
const metadataUrl = `http://localhost:${port}/idp/metadata`;
|
|
493
|
+
|
|
494
|
+
return { start, stop, metadataUrl };
|
|
495
|
+
};
|
|
465
496
|
|
|
466
497
|
describe("SAML SSO", async () => {
|
|
467
498
|
const data = {
|
|
@@ -473,7 +504,7 @@ describe("SAML SSO", async () => {
|
|
|
473
504
|
};
|
|
474
505
|
|
|
475
506
|
const memory = memoryAdapter(data);
|
|
476
|
-
const mockIdP =
|
|
507
|
+
const mockIdP = createMockSAMLIdP(8081); // Different port from your main app
|
|
477
508
|
|
|
478
509
|
const ssoOptions = {
|
|
479
510
|
provisionUser: vi
|
|
@@ -580,7 +611,7 @@ describe("SAML SSO", async () => {
|
|
|
580
611
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
581
612
|
},
|
|
582
613
|
spMetadata: {
|
|
583
|
-
metadata:
|
|
614
|
+
metadata: spMetadata,
|
|
584
615
|
binding: "post",
|
|
585
616
|
privateKey: spPrivateKey,
|
|
586
617
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
@@ -672,9 +703,9 @@ describe("SAML SSO", async () => {
|
|
|
672
703
|
issuer: "http://localhost:8081",
|
|
673
704
|
domain: "http://localhost:8081",
|
|
674
705
|
samlConfig: {
|
|
675
|
-
entryPoint:
|
|
706
|
+
entryPoint: "http://localhost:8081/api/sso/saml2/idp/post",
|
|
676
707
|
cert: certificate,
|
|
677
|
-
callbackUrl: "http://localhost:8081/
|
|
708
|
+
callbackUrl: "http://localhost:8081/dashboard",
|
|
678
709
|
wantAssertionsSigned: false,
|
|
679
710
|
signatureAlgorithm: "sha256",
|
|
680
711
|
digestAlgorithm: "sha256",
|
|
@@ -687,9 +718,8 @@ describe("SAML SSO", async () => {
|
|
|
687
718
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
688
719
|
},
|
|
689
720
|
spMetadata: {
|
|
690
|
-
metadata:
|
|
721
|
+
metadata: spMetadata,
|
|
691
722
|
binding: "post",
|
|
692
|
-
// we can do a mapping of property here
|
|
693
723
|
privateKey: spPrivateKey,
|
|
694
724
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
695
725
|
isAssertionEncrypted: true,
|
|
@@ -709,25 +739,180 @@ describe("SAML SSO", async () => {
|
|
|
709
739
|
callbackURL: "http://localhost:3000/dashboard",
|
|
710
740
|
},
|
|
711
741
|
});
|
|
742
|
+
|
|
712
743
|
expect(signInResponse).toEqual({
|
|
713
744
|
url: expect.stringContaining("http://localhost:8081"),
|
|
714
745
|
redirect: true,
|
|
715
746
|
});
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
747
|
+
let samlResponse: any;
|
|
748
|
+
await betterFetch(signInResponse?.url as string, {
|
|
749
|
+
onSuccess: async (context) => {
|
|
750
|
+
samlResponse = await context.data;
|
|
751
|
+
},
|
|
752
|
+
});
|
|
753
|
+
let redirectLocation = "";
|
|
754
|
+
await betterFetch(
|
|
755
|
+
"http://localhost:8081/api/sso/saml2/callback/saml-provider-1",
|
|
756
|
+
{
|
|
757
|
+
method: "POST",
|
|
758
|
+
redirect: "manual",
|
|
759
|
+
headers: {
|
|
760
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
761
|
+
},
|
|
762
|
+
body: new URLSearchParams({
|
|
763
|
+
SAMLResponse: samlResponse.samlResponse,
|
|
764
|
+
}),
|
|
765
|
+
onError: (context) => {
|
|
766
|
+
expect(context.response.status).toBe(302);
|
|
767
|
+
redirectLocation = context.response.headers.get("location") || "";
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
);
|
|
771
|
+
expect(redirectLocation).toBe("http://localhost:3000/dashboard");
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it("should not allow creating a provider if limit is set to 0", async () => {
|
|
775
|
+
const { auth, signInWithTestUser } = await getTestInstanceMemory({
|
|
776
|
+
plugins: [sso({ providersLimit: 0 })],
|
|
777
|
+
});
|
|
778
|
+
const { headers } = await signInWithTestUser();
|
|
779
|
+
await expect(
|
|
780
|
+
auth.api.registerSSOProvider({
|
|
781
|
+
body: {
|
|
782
|
+
providerId: "saml-provider-1",
|
|
783
|
+
issuer: "http://localhost:8081",
|
|
784
|
+
domain: "http://localhost:8081",
|
|
785
|
+
samlConfig: {
|
|
786
|
+
entryPoint: mockIdP.metadataUrl,
|
|
787
|
+
cert: certificate,
|
|
788
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
789
|
+
wantAssertionsSigned: false,
|
|
790
|
+
signatureAlgorithm: "sha256",
|
|
791
|
+
digestAlgorithm: "sha256",
|
|
792
|
+
spMetadata: {
|
|
793
|
+
metadata: spMetadata,
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
},
|
|
797
|
+
headers,
|
|
798
|
+
}),
|
|
799
|
+
).rejects.toMatchObject({
|
|
800
|
+
status: "FORBIDDEN",
|
|
801
|
+
body: { message: "SSO provider registration is disabled" },
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it("should not allow creating a provider if limit is reached", async () => {
|
|
806
|
+
const { auth, signInWithTestUser } = await getTestInstanceMemory({
|
|
807
|
+
plugins: [sso({ providersLimit: 1 })],
|
|
808
|
+
});
|
|
809
|
+
const { headers } = await signInWithTestUser();
|
|
810
|
+
|
|
811
|
+
await auth.api.registerSSOProvider({
|
|
719
812
|
body: {
|
|
720
|
-
|
|
721
|
-
|
|
813
|
+
providerId: "saml-provider-1",
|
|
814
|
+
issuer: "http://localhost:8081",
|
|
815
|
+
domain: "http://localhost:8081",
|
|
816
|
+
samlConfig: {
|
|
817
|
+
entryPoint: mockIdP.metadataUrl,
|
|
818
|
+
cert: certificate,
|
|
819
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
820
|
+
wantAssertionsSigned: false,
|
|
821
|
+
signatureAlgorithm: "sha256",
|
|
822
|
+
digestAlgorithm: "sha256",
|
|
823
|
+
spMetadata: {
|
|
824
|
+
metadata: spMetadata,
|
|
825
|
+
},
|
|
826
|
+
},
|
|
722
827
|
},
|
|
723
|
-
|
|
724
|
-
|
|
828
|
+
headers,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
await expect(
|
|
832
|
+
auth.api.registerSSOProvider({
|
|
833
|
+
body: {
|
|
834
|
+
providerId: "saml-provider-2",
|
|
835
|
+
issuer: "http://localhost:8081",
|
|
836
|
+
domain: "http://localhost:8081",
|
|
837
|
+
samlConfig: {
|
|
838
|
+
entryPoint: mockIdP.metadataUrl,
|
|
839
|
+
cert: certificate,
|
|
840
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
841
|
+
wantAssertionsSigned: false,
|
|
842
|
+
signatureAlgorithm: "sha256",
|
|
843
|
+
digestAlgorithm: "sha256",
|
|
844
|
+
spMetadata: {
|
|
845
|
+
metadata: spMetadata,
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
headers,
|
|
850
|
+
}),
|
|
851
|
+
).rejects.toMatchObject({
|
|
852
|
+
status: "FORBIDDEN",
|
|
853
|
+
body: {
|
|
854
|
+
message: "You have reached the maximum number of SSO providers",
|
|
725
855
|
},
|
|
726
856
|
});
|
|
857
|
+
});
|
|
727
858
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
859
|
+
it("should not allow creating a provider if limit from function is reached", async () => {
|
|
860
|
+
const { auth, signInWithTestUser } = await getTestInstanceMemory({
|
|
861
|
+
plugins: [
|
|
862
|
+
sso({
|
|
863
|
+
providersLimit: async (user) => {
|
|
864
|
+
return user.email === "pro@example.com" ? 2 : 1;
|
|
865
|
+
},
|
|
866
|
+
}),
|
|
867
|
+
],
|
|
868
|
+
});
|
|
869
|
+
const { headers } = await signInWithTestUser();
|
|
870
|
+
|
|
871
|
+
await auth.api.registerSSOProvider({
|
|
872
|
+
body: {
|
|
873
|
+
providerId: "saml-provider-1",
|
|
874
|
+
issuer: "http://localhost:8081",
|
|
875
|
+
domain: "http://localhost:8081",
|
|
876
|
+
samlConfig: {
|
|
877
|
+
entryPoint: mockIdP.metadataUrl,
|
|
878
|
+
cert: certificate,
|
|
879
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
880
|
+
wantAssertionsSigned: false,
|
|
881
|
+
signatureAlgorithm: "sha256",
|
|
882
|
+
digestAlgorithm: "sha256",
|
|
883
|
+
spMetadata: {
|
|
884
|
+
metadata: spMetadata,
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
headers,
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
await expect(
|
|
892
|
+
auth.api.registerSSOProvider({
|
|
893
|
+
body: {
|
|
894
|
+
providerId: "saml-provider-2",
|
|
895
|
+
issuer: "http://localhost:8081",
|
|
896
|
+
domain: "http://localhost:8081",
|
|
897
|
+
samlConfig: {
|
|
898
|
+
entryPoint: mockIdP.metadataUrl,
|
|
899
|
+
cert: certificate,
|
|
900
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
901
|
+
wantAssertionsSigned: false,
|
|
902
|
+
signatureAlgorithm: "sha256",
|
|
903
|
+
digestAlgorithm: "sha256",
|
|
904
|
+
spMetadata: {
|
|
905
|
+
metadata: spMetadata,
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
headers,
|
|
910
|
+
}),
|
|
911
|
+
).rejects.toMatchObject({
|
|
912
|
+
status: "FORBIDDEN",
|
|
913
|
+
body: {
|
|
914
|
+
message: "You have reached the maximum number of SSO providers",
|
|
915
|
+
},
|
|
731
916
|
});
|
|
732
917
|
});
|
|
733
918
|
});
|