@better-auth/sso 1.4.0-beta.1 → 1.4.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 +23 -15
- package/dist/client.cjs +8 -6
- package/dist/client.d.cts +6 -8
- package/dist/client.d.ts +6 -8
- package/dist/client.js +12 -0
- package/dist/index-Cl11-WdU.d.cts +965 -0
- package/dist/index-Dtx0Mkqc.d.ts +965 -0
- package/dist/index.cjs +2 -1161
- package/dist/index.d.cts +2 -812
- package/dist/index.d.ts +2 -812
- package/dist/index.js +3 -0
- package/dist/src-BYOa9Nr6.cjs +1256 -0
- package/dist/src-Z1RpfPZt.js +1218 -0
- package/package.json +13 -12
- package/src/index.ts +896 -150
- package/src/oidc.test.ts +156 -172
- package/src/saml.test.ts +212 -8
- package/tsconfig.json +4 -15
- package/tsdown.config.ts +8 -0
- package/CHANGELOG.md +0 -20
- package/build.config.ts +0 -12
- package/dist/client.d.mts +0 -11
- package/dist/client.mjs +0 -8
- package/dist/index.d.mts +0 -812
- package/dist/index.mjs +0 -1145
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 idpEncryptionKey = `
|
|
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 idpEncyptionKey = `
|
|
|
274
274
|
ISbutnQPUN5fsaIsgKDIV3T7n6519t6brobcW5bdigmf5ebFeZJ16/lYy6V77UM5
|
|
275
275
|
-----END RSA PRIVATE KEY-----
|
|
276
276
|
`;
|
|
277
|
-
const
|
|
277
|
+
const spEncryptionKey = `
|
|
278
278
|
-----BEGIN RSA PRIVATE KEY-----
|
|
279
279
|
Proc-Type: 4,ENCRYPTED
|
|
280
280
|
DEK-Info: DES-EDE3-CBC,860FDB9F3BE14699
|
|
@@ -493,6 +493,98 @@ 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
|
+
|
|
496
588
|
describe("SAML SSO", async () => {
|
|
497
589
|
const data = {
|
|
498
590
|
user: [],
|
|
@@ -606,7 +698,7 @@ describe("SAML SSO", async () => {
|
|
|
606
698
|
privateKey: idpPrivateKey,
|
|
607
699
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
608
700
|
isAssertionEncrypted: true,
|
|
609
|
-
encPrivateKey:
|
|
701
|
+
encPrivateKey: idpEncryptionKey,
|
|
610
702
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
611
703
|
},
|
|
612
704
|
spMetadata: {
|
|
@@ -615,7 +707,7 @@ describe("SAML SSO", async () => {
|
|
|
615
707
|
privateKey: spPrivateKey,
|
|
616
708
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
617
709
|
isAssertionEncrypted: true,
|
|
618
|
-
encPrivateKey:
|
|
710
|
+
encPrivateKey: spEncryptionKey,
|
|
619
711
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
620
712
|
},
|
|
621
713
|
identifierFormat:
|
|
@@ -662,7 +754,7 @@ describe("SAML SSO", async () => {
|
|
|
662
754
|
privateKey: idpPrivateKey,
|
|
663
755
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
664
756
|
isAssertionEncrypted: true,
|
|
665
|
-
encPrivateKey:
|
|
757
|
+
encPrivateKey: idpEncryptionKey,
|
|
666
758
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
667
759
|
},
|
|
668
760
|
spMetadata: {
|
|
@@ -671,7 +763,7 @@ describe("SAML SSO", async () => {
|
|
|
671
763
|
privateKey: spPrivateKey,
|
|
672
764
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
673
765
|
isAssertionEncrypted: true,
|
|
674
|
-
encPrivateKey:
|
|
766
|
+
encPrivateKey: spEncryptionKey,
|
|
675
767
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
676
768
|
},
|
|
677
769
|
identifierFormat:
|
|
@@ -690,6 +782,69 @@ describe("SAML SSO", async () => {
|
|
|
690
782
|
expect(spMetadataRes.status).toBe(200);
|
|
691
783
|
expect(spMetadataResResValue).toBe(spMetadata);
|
|
692
784
|
});
|
|
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
|
+
});
|
|
693
848
|
it("should initiate SAML login and handle response", async () => {
|
|
694
849
|
const headers = await getAuthHeaders();
|
|
695
850
|
const res = await authClient.signIn.email(testUser, {
|
|
@@ -713,7 +868,7 @@ describe("SAML SSO", async () => {
|
|
|
713
868
|
privateKey: idpPrivateKey,
|
|
714
869
|
privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
|
|
715
870
|
isAssertionEncrypted: true,
|
|
716
|
-
encPrivateKey:
|
|
871
|
+
encPrivateKey: idpEncryptionKey,
|
|
717
872
|
encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
|
|
718
873
|
},
|
|
719
874
|
spMetadata: {
|
|
@@ -722,7 +877,7 @@ describe("SAML SSO", async () => {
|
|
|
722
877
|
privateKey: spPrivateKey,
|
|
723
878
|
privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
|
|
724
879
|
isAssertionEncrypted: true,
|
|
725
|
-
encPrivateKey:
|
|
880
|
+
encPrivateKey: spEncryptionKey,
|
|
726
881
|
encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
|
|
727
882
|
},
|
|
728
883
|
identifierFormat:
|
|
@@ -914,4 +1069,53 @@ describe("SAML SSO", async () => {
|
|
|
914
1069
|
},
|
|
915
1070
|
});
|
|
916
1071
|
});
|
|
1072
|
+
|
|
1073
|
+
it("should not allow creating a provider with duplicate providerId", async () => {
|
|
1074
|
+
const headers = await getAuthHeaders();
|
|
1075
|
+
await authClient.signIn.email(testUser, {
|
|
1076
|
+
throw: true,
|
|
1077
|
+
onSuccess: setCookieToHeader(headers),
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
await auth.api.registerSSOProvider({
|
|
1081
|
+
body: {
|
|
1082
|
+
providerId: "duplicate-provider",
|
|
1083
|
+
issuer: "http://localhost:8081",
|
|
1084
|
+
domain: "http://localhost:8081",
|
|
1085
|
+
samlConfig: {
|
|
1086
|
+
entryPoint: mockIdP.metadataUrl,
|
|
1087
|
+
cert: certificate,
|
|
1088
|
+
callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
|
|
1089
|
+
spMetadata: {
|
|
1090
|
+
metadata: spMetadata,
|
|
1091
|
+
},
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
headers,
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
await expect(
|
|
1098
|
+
auth.api.registerSSOProvider({
|
|
1099
|
+
body: {
|
|
1100
|
+
providerId: "duplicate-provider",
|
|
1101
|
+
issuer: "http://localhost:8082",
|
|
1102
|
+
domain: "http://localhost:8082",
|
|
1103
|
+
samlConfig: {
|
|
1104
|
+
entryPoint: mockIdP.metadataUrl,
|
|
1105
|
+
cert: certificate,
|
|
1106
|
+
callbackUrl: "http://localhost:8082/api/sso/saml2/callback",
|
|
1107
|
+
spMetadata: {
|
|
1108
|
+
metadata: spMetadata,
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
},
|
|
1112
|
+
headers,
|
|
1113
|
+
}),
|
|
1114
|
+
).rejects.toMatchObject({
|
|
1115
|
+
status: "UNPROCESSABLE_ENTITY",
|
|
1116
|
+
body: {
|
|
1117
|
+
message: "SSO provider with this providerId already exists",
|
|
1118
|
+
},
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
917
1121
|
});
|
package/tsconfig.json
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
2
3
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"lib": ["esnext", "dom", "dom.iterable"]
|
|
17
7
|
},
|
|
18
|
-
"exclude": ["node_modules", "dist"],
|
|
19
8
|
"include": ["src"]
|
|
20
9
|
}
|
package/tsdown.config.ts
ADDED
package/CHANGELOG.md
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# @better-auth/sso
|
|
2
|
-
|
|
3
|
-
## 1.3.4
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- 2bd2fa9: Added support for listing organization members with pagination, sorting, and filtering, and improved client inference for additional organization fields. Also fixed date handling in rate limits and tokens, improved Notion OAuth user extraction, and ensured session is always set in context.
|
|
8
|
-
|
|
9
|
-
Organization
|
|
10
|
-
|
|
11
|
-
- Added listMembers API with pagination, sorting, and filtering.
|
|
12
|
-
- Added membersLimit param to getFullOrganization.
|
|
13
|
-
- Improved client inference for additional fields in organization schemas.
|
|
14
|
-
- Bug Fixes
|
|
15
|
-
- Fixed date handling by casting DB values to Date objects before using date methods.
|
|
16
|
-
- Fixed Notion OAuth to extract user info correctly.
|
|
17
|
-
- Ensured session is set in context when reading from cookie cach
|
|
18
|
-
|
|
19
|
-
- Updated dependencies [2bd2fa9]
|
|
20
|
-
- better-auth@1.3.4
|
package/build.config.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineBuildConfig } from "unbuild";
|
|
2
|
-
|
|
3
|
-
export default defineBuildConfig({
|
|
4
|
-
declaration: true,
|
|
5
|
-
rollup: {
|
|
6
|
-
emitCJS: true,
|
|
7
|
-
},
|
|
8
|
-
outDir: "dist",
|
|
9
|
-
clean: false,
|
|
10
|
-
failOnWarn: false,
|
|
11
|
-
externals: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"],
|
|
12
|
-
});
|
package/dist/client.d.mts
DELETED