@better-auth/sso 1.3.17 → 1.4.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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +20 -0
- package/dist/index.cjs +165 -551
- package/dist/index.d.cts +39 -186
- package/dist/index.d.mts +39 -186
- package/dist/index.d.ts +39 -186
- package/dist/index.mjs +165 -551
- package/package.json +5 -5
- package/src/index.ts +225 -811
- package/src/oidc.test.ts +21 -84
- package/src/saml.test.ts +8 -163
- package/tsconfig.json +15 -9
package/src/oidc.test.ts
CHANGED
|
@@ -84,13 +84,13 @@ describe("SSO", async () => {
|
|
|
84
84
|
tokenEndpoint: `${server.issuer.url}/token`,
|
|
85
85
|
jwksEndpoint: `${server.issuer.url}/jwks`,
|
|
86
86
|
discoveryEndpoint: `${server.issuer.url}/.well-known/openid-configuration`,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
},
|
|
88
|
+
mapping: {
|
|
89
|
+
id: "sub",
|
|
90
|
+
email: "email",
|
|
91
|
+
emailVerified: "email_verified",
|
|
92
|
+
name: "name",
|
|
93
|
+
image: "picture",
|
|
94
94
|
},
|
|
95
95
|
providerId: "test",
|
|
96
96
|
},
|
|
@@ -196,67 +196,6 @@ describe("SSO", async () => {
|
|
|
196
196
|
});
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
-
describe("SSO with defaultSSO array", async () => {
|
|
200
|
-
const { auth, signInWithTestUser, customFetchImpl } =
|
|
201
|
-
await getTestInstanceMemory({
|
|
202
|
-
plugins: [
|
|
203
|
-
sso({
|
|
204
|
-
defaultSSO: [
|
|
205
|
-
{
|
|
206
|
-
domain: "localhost.com",
|
|
207
|
-
providerId: "default-test",
|
|
208
|
-
oidcConfig: {
|
|
209
|
-
issuer: "http://localhost:8080",
|
|
210
|
-
clientId: "test",
|
|
211
|
-
clientSecret: "test",
|
|
212
|
-
authorizationEndpoint: "http://localhost:8080/authorize",
|
|
213
|
-
tokenEndpoint: "http://localhost:8080/token",
|
|
214
|
-
jwksEndpoint: "http://localhost:8080/jwks",
|
|
215
|
-
discoveryEndpoint:
|
|
216
|
-
"http://localhost:8080/.well-known/openid-configuration",
|
|
217
|
-
pkce: true,
|
|
218
|
-
mapping: {
|
|
219
|
-
id: "sub",
|
|
220
|
-
email: "email",
|
|
221
|
-
emailVerified: "email_verified",
|
|
222
|
-
name: "name",
|
|
223
|
-
image: "picture",
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
}),
|
|
229
|
-
organization(),
|
|
230
|
-
],
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("should use default SSO provider from array when no provider found in database using providerId", async () => {
|
|
234
|
-
const res = await auth.api.signInSSO({
|
|
235
|
-
body: {
|
|
236
|
-
providerId: "default-test",
|
|
237
|
-
callbackURL: "/dashboard",
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
expect(res.url).toContain("http://localhost:8080/authorize");
|
|
241
|
-
expect(res.url).toContain(
|
|
242
|
-
"redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Fdefault-test",
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it("should use default SSO provider from array when no provider found in database using domain fallback", async () => {
|
|
247
|
-
const res = await auth.api.signInSSO({
|
|
248
|
-
body: {
|
|
249
|
-
email: "test@localhost.com",
|
|
250
|
-
callbackURL: "/dashboard",
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
expect(res.url).toContain("http://localhost:8080/authorize");
|
|
254
|
-
expect(res.url).toContain(
|
|
255
|
-
"redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Fdefault-test",
|
|
256
|
-
);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
199
|
describe("SSO disable implicit sign in", async () => {
|
|
261
200
|
const { auth, signInWithTestUser, customFetchImpl } =
|
|
262
201
|
await getTestInstanceMemory({
|
|
@@ -333,14 +272,13 @@ describe("SSO disable implicit sign in", async () => {
|
|
|
333
272
|
authorizationEndpoint: `${server.issuer.url}/authorize`,
|
|
334
273
|
tokenEndpoint: `${server.issuer.url}/token`,
|
|
335
274
|
jwksEndpoint: `${server.issuer.url}/jwks`,
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
},
|
|
275
|
+
},
|
|
276
|
+
mapping: {
|
|
277
|
+
id: "sub",
|
|
278
|
+
email: "email",
|
|
279
|
+
emailVerified: "email_verified",
|
|
280
|
+
name: "name",
|
|
281
|
+
image: "picture",
|
|
344
282
|
},
|
|
345
283
|
providerId: "test",
|
|
346
284
|
},
|
|
@@ -588,14 +526,13 @@ describe("provisioning", async (ctx) => {
|
|
|
588
526
|
authorizationEndpoint: `${server.issuer.url}/authorize`,
|
|
589
527
|
tokenEndpoint: `${server.issuer.url}/token`,
|
|
590
528
|
jwksEndpoint: `${server.issuer.url}/jwks`,
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
},
|
|
529
|
+
},
|
|
530
|
+
mapping: {
|
|
531
|
+
id: "sub",
|
|
532
|
+
email: "email",
|
|
533
|
+
emailVerified: "email_verified",
|
|
534
|
+
name: "name",
|
|
535
|
+
image: "picture",
|
|
599
536
|
},
|
|
600
537
|
providerId: "test2",
|
|
601
538
|
organizationId: organization?.id,
|
package/src/saml.test.ts
CHANGED
|
@@ -242,7 +242,7 @@ const certificate = `
|
|
|
242
242
|
yyoWAJDUHiAmvFA=
|
|
243
243
|
-----END CERTIFICATE-----
|
|
244
244
|
`;
|
|
245
|
-
const
|
|
245
|
+
const idpEncyptionKey = `
|
|
246
246
|
-----BEGIN RSA PRIVATE KEY-----
|
|
247
247
|
Proc-Type: 4,ENCRYPTED
|
|
248
248
|
DEK-Info: DES-EDE3-CBC,860FDB9F3BE14699
|
|
@@ -274,7 +274,7 @@ const idpEncryptionKey = `
|
|
|
274
274
|
ISbutnQPUN5fsaIsgKDIV3T7n6519t6brobcW5bdigmf5ebFeZJ16/lYy6V77UM5
|
|
275
275
|
-----END RSA PRIVATE KEY-----
|
|
276
276
|
`;
|
|
277
|
-
const
|
|
277
|
+
const spEncyptionKey = `
|
|
278
278
|
-----BEGIN RSA PRIVATE KEY-----
|
|
279
279
|
Proc-Type: 4,ENCRYPTED
|
|
280
280
|
DEK-Info: DES-EDE3-CBC,860FDB9F3BE14699
|
|
@@ -493,98 +493,6 @@ const createMockSAMLIdP = (port: number) => {
|
|
|
493
493
|
return { start, stop, metadataUrl };
|
|
494
494
|
};
|
|
495
495
|
|
|
496
|
-
describe("SAML SSO with defaultSSO array", async () => {
|
|
497
|
-
const data = {
|
|
498
|
-
user: [],
|
|
499
|
-
session: [],
|
|
500
|
-
verification: [],
|
|
501
|
-
account: [],
|
|
502
|
-
ssoProvider: [],
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
const memory = memoryAdapter(data);
|
|
506
|
-
const mockIdP = createMockSAMLIdP(8081); // Different port from your main app
|
|
507
|
-
|
|
508
|
-
const ssoOptions = {
|
|
509
|
-
defaultSSO: [
|
|
510
|
-
{
|
|
511
|
-
domain: "localhost:8081",
|
|
512
|
-
providerId: "default-saml",
|
|
513
|
-
samlConfig: {
|
|
514
|
-
issuer: "http://localhost:8081",
|
|
515
|
-
entryPoint: "http://localhost:8081/api/sso/saml2/idp/post",
|
|
516
|
-
cert: certificate,
|
|
517
|
-
callbackUrl: "http://localhost:8081/dashboard",
|
|
518
|
-
wantAssertionsSigned: false,
|
|
519
|
-
signatureAlgorithm: "sha256",
|
|
520
|
-
digestAlgorithm: "sha256",
|
|
521
|
-
idpMetadata: {
|
|
522
|
-
metadata: idpMetadata,
|
|
523
|
-
},
|
|
524
|
-
spMetadata: {
|
|
525
|
-
metadata: spMetadata,
|
|
526
|
-
},
|
|
527
|
-
identifierFormat:
|
|
528
|
-
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
],
|
|
532
|
-
provisionUser: vi
|
|
533
|
-
.fn()
|
|
534
|
-
.mockImplementation(async ({ user, userInfo, token, provider }) => {
|
|
535
|
-
return {
|
|
536
|
-
id: "provisioned-user-id",
|
|
537
|
-
email: userInfo.email,
|
|
538
|
-
name: userInfo.name,
|
|
539
|
-
attributes: userInfo.attributes,
|
|
540
|
-
};
|
|
541
|
-
}),
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
const auth = betterAuth({
|
|
545
|
-
database: memory,
|
|
546
|
-
baseURL: "http://localhost:3000",
|
|
547
|
-
emailAndPassword: {
|
|
548
|
-
enabled: true,
|
|
549
|
-
},
|
|
550
|
-
plugins: [sso(ssoOptions)],
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
const ctx = await auth.$context;
|
|
554
|
-
|
|
555
|
-
const authClient = createAuthClient({
|
|
556
|
-
baseURL: "http://localhost:3000",
|
|
557
|
-
plugins: [bearer(), ssoClient()],
|
|
558
|
-
fetchOptions: {
|
|
559
|
-
customFetchImpl: async (url, init) => {
|
|
560
|
-
return auth.handler(new Request(url, init));
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
beforeAll(async () => {
|
|
566
|
-
await mockIdP.start();
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
afterAll(async () => {
|
|
570
|
-
await mockIdP.stop();
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
it("should use default SAML SSO provider from array when no provider found in database", async () => {
|
|
574
|
-
const signInResponse = await auth.api.signInSSO({
|
|
575
|
-
body: {
|
|
576
|
-
providerId: "default-saml",
|
|
577
|
-
callbackURL: "http://localhost:3000/dashboard",
|
|
578
|
-
},
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
expect(signInResponse).toEqual({
|
|
582
|
-
url: expect.stringContaining("http://localhost:8081"),
|
|
583
|
-
redirect: true,
|
|
584
|
-
});
|
|
585
|
-
});
|
|
586
|
-
});
|
|
587
|
-
|
|
588
496
|
describe("SAML SSO", async () => {
|
|
589
497
|
const data = {
|
|
590
498
|
user: [],
|
|
@@ -698,7 +606,7 @@ describe("SAML SSO", async () => {
|
|
|
698
606
|
privateKey: idpPrivateKey,
|
|
699
607
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
700
608
|
isAssertionEncrypted: true,
|
|
701
|
-
encPrivateKey:
|
|
609
|
+
encPrivateKey: idpEncyptionKey,
|
|
702
610
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
703
611
|
},
|
|
704
612
|
spMetadata: {
|
|
@@ -707,7 +615,7 @@ describe("SAML SSO", async () => {
|
|
|
707
615
|
privateKey: spPrivateKey,
|
|
708
616
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
709
617
|
isAssertionEncrypted: true,
|
|
710
|
-
encPrivateKey:
|
|
618
|
+
encPrivateKey: spEncyptionKey,
|
|
711
619
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
712
620
|
},
|
|
713
621
|
identifierFormat:
|
|
@@ -754,7 +662,7 @@ describe("SAML SSO", async () => {
|
|
|
754
662
|
privateKey: idpPrivateKey,
|
|
755
663
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
756
664
|
isAssertionEncrypted: true,
|
|
757
|
-
encPrivateKey:
|
|
665
|
+
encPrivateKey: idpEncyptionKey,
|
|
758
666
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
759
667
|
},
|
|
760
668
|
spMetadata: {
|
|
@@ -763,7 +671,7 @@ describe("SAML SSO", async () => {
|
|
|
763
671
|
privateKey: spPrivateKey,
|
|
764
672
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
765
673
|
isAssertionEncrypted: true,
|
|
766
|
-
encPrivateKey:
|
|
674
|
+
encPrivateKey: spEncyptionKey,
|
|
767
675
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
768
676
|
},
|
|
769
677
|
identifierFormat:
|
|
@@ -782,69 +690,6 @@ describe("SAML SSO", async () => {
|
|
|
782
690
|
expect(spMetadataRes.status).toBe(200);
|
|
783
691
|
expect(spMetadataResResValue).toBe(spMetadata);
|
|
784
692
|
});
|
|
785
|
-
it("Should fetch sp metadata", async () => {
|
|
786
|
-
const headers = await getAuthHeaders();
|
|
787
|
-
await authClient.signIn.email(testUser, {
|
|
788
|
-
throw: true,
|
|
789
|
-
onSuccess: setCookieToHeader(headers),
|
|
790
|
-
});
|
|
791
|
-
const issuer = "http://localhost:8081";
|
|
792
|
-
const provider = await auth.api.registerSSOProvider({
|
|
793
|
-
body: {
|
|
794
|
-
providerId: "saml-provider-1",
|
|
795
|
-
issuer: issuer,
|
|
796
|
-
domain: issuer,
|
|
797
|
-
samlConfig: {
|
|
798
|
-
entryPoint: mockIdP.metadataUrl,
|
|
799
|
-
cert: certificate,
|
|
800
|
-
callbackUrl: `${issuer}/api/sso/saml2/sp/acs`,
|
|
801
|
-
wantAssertionsSigned: false,
|
|
802
|
-
signatureAlgorithm: "sha256",
|
|
803
|
-
digestAlgorithm: "sha256",
|
|
804
|
-
idpMetadata: {
|
|
805
|
-
metadata: idpMetadata,
|
|
806
|
-
privateKey: idpPrivateKey,
|
|
807
|
-
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
808
|
-
isAssertionEncrypted: true,
|
|
809
|
-
encPrivateKey: idpEncryptionKey,
|
|
810
|
-
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
811
|
-
},
|
|
812
|
-
spMetadata: {
|
|
813
|
-
binding: "post",
|
|
814
|
-
privateKey: spPrivateKey,
|
|
815
|
-
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
816
|
-
isAssertionEncrypted: true,
|
|
817
|
-
encPrivateKey: spEncryptionKey,
|
|
818
|
-
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
819
|
-
},
|
|
820
|
-
identifierFormat:
|
|
821
|
-
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
|
822
|
-
},
|
|
823
|
-
},
|
|
824
|
-
headers,
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
const spMetadataRes = await auth.api.spMetadata({
|
|
828
|
-
query: {
|
|
829
|
-
providerId: provider.providerId,
|
|
830
|
-
},
|
|
831
|
-
});
|
|
832
|
-
const spMetadataResResValue = await spMetadataRes.text();
|
|
833
|
-
expect(spMetadataRes.status).toBe(200);
|
|
834
|
-
expect(spMetadataResResValue).toBeDefined();
|
|
835
|
-
expect(spMetadataResResValue).toContain(
|
|
836
|
-
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
|
837
|
-
);
|
|
838
|
-
expect(spMetadataResResValue).toContain(
|
|
839
|
-
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
840
|
-
);
|
|
841
|
-
expect(spMetadataResResValue).toContain(
|
|
842
|
-
`<EntityDescriptor entityID="${issuer}"`,
|
|
843
|
-
);
|
|
844
|
-
expect(spMetadataResResValue).toContain(
|
|
845
|
-
`Location="${issuer}/api/sso/saml2/sp/acs"`,
|
|
846
|
-
);
|
|
847
|
-
});
|
|
848
693
|
it("should initiate SAML login and handle response", async () => {
|
|
849
694
|
const headers = await getAuthHeaders();
|
|
850
695
|
const res = await authClient.signIn.email(testUser, {
|
|
@@ -868,7 +713,7 @@ describe("SAML SSO", async () => {
|
|
|
868
713
|
privateKey: idpPrivateKey,
|
|
869
714
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
870
715
|
isAssertionEncrypted: true,
|
|
871
|
-
encPrivateKey:
|
|
716
|
+
encPrivateKey: idpEncyptionKey,
|
|
872
717
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
873
718
|
},
|
|
874
719
|
spMetadata: {
|
|
@@ -877,7 +722,7 @@ describe("SAML SSO", async () => {
|
|
|
877
722
|
privateKey: spPrivateKey,
|
|
878
723
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
879
724
|
isAssertionEncrypted: true,
|
|
880
|
-
encPrivateKey:
|
|
725
|
+
encPrivateKey: spEncyptionKey,
|
|
881
726
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
882
727
|
},
|
|
883
728
|
identifierFormat:
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
2
|
"compilerOptions": {
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"esModuleInterop": true,
|
|
4
|
+
"skipLibCheck": true,
|
|
5
|
+
"target": "es2022",
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"moduleResolution": "Bundler",
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noImplicitOverride": true,
|
|
16
|
+
"noFallthroughCasesInSwitch": true
|
|
7
17
|
},
|
|
8
|
-
"
|
|
9
|
-
{
|
|
10
|
-
"path": "../better-auth/tsconfig.json"
|
|
11
|
-
}
|
|
12
|
-
],
|
|
18
|
+
"exclude": ["node_modules", "dist"],
|
|
13
19
|
"include": ["src"]
|
|
14
20
|
}
|