@better-auth/sso 1.4.0-beta.20 → 1.4.0-beta.21

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@better-auth/sso",
3
3
  "author": "Bereket Engida",
4
- "version": "1.4.0-beta.20",
4
+ "version": "1.4.0-beta.21",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
7
7
  "homepage": "https://www.better-auth.com/docs/plugins/sso",
@@ -65,10 +65,10 @@
65
65
  "express": "^5.1.0",
66
66
  "oauth2-mock-server": "^7.2.1",
67
67
  "tsdown": "^0.16.0",
68
- "better-auth": "1.4.0-beta.20"
68
+ "better-auth": "1.4.0-beta.21"
69
69
  },
70
70
  "peerDependencies": {
71
- "better-auth": "1.4.0-beta.20"
71
+ "better-auth": "1.4.0-beta.21"
72
72
  },
73
73
  "scripts": {
74
74
  "test": "vitest",
package/src/client.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { BetterAuthClientPlugin } from "better-auth";
2
- import { sso } from "./index";
2
+ import type { sso } from "./index";
3
3
  export const ssoClient = () => {
4
4
  return {
5
5
  id: "sso-client",
package/src/index.ts CHANGED
@@ -9,9 +9,9 @@ import {
9
9
  signInSSO,
10
10
  spMetadata,
11
11
  } from "./routes/sso";
12
- import type { OIDCConfig, SAMLConfig, SSOOptions } from "./types";
12
+ import type { OIDCConfig, SAMLConfig, SSOOptions, SSOProvider } from "./types";
13
13
 
14
- export type { SAMLConfig, OIDCConfig, SSOOptions };
14
+ export type { SAMLConfig, OIDCConfig, SSOOptions, SSOProvider };
15
15
 
16
16
  const fastValidator = {
17
17
  async validate(xml: string) {
package/src/routes/sso.ts CHANGED
@@ -63,6 +63,7 @@ export const spMetadata = () => {
63
63
  }),
64
64
  metadata: {
65
65
  openapi: {
66
+ operationId: "getSSOServiceProviderMetadata",
66
67
  summary: "Get Service Provider metadata",
67
68
  description: "Returns the SAML metadata for the Service Provider",
68
69
  responses: {
@@ -337,6 +338,7 @@ export const registerSSOProvider = (options?: SSOOptions) => {
337
338
  use: [sessionMiddleware],
338
339
  metadata: {
339
340
  openapi: {
341
+ operationId: "registerSSOProvider",
340
342
  summary: "Register an OIDC provider",
341
343
  description:
342
344
  "This endpoint is used to register an OIDC provider. This is used to configure the provider and link it to an organization",
@@ -726,6 +728,7 @@ export const signInSSO = (options?: SSOOptions) => {
726
728
  }),
727
729
  metadata: {
728
730
  openapi: {
731
+ operationId: "signInWithSSO",
729
732
  summary: "Sign in with SSO provider",
730
733
  description:
731
734
  "This endpoint is used to sign in with an SSO provider. It redirects to the provider's authorization URL",
@@ -1014,6 +1017,7 @@ export const callbackSSO = (options?: SSOOptions) => {
1014
1017
  metadata: {
1015
1018
  isAction: false,
1016
1019
  openapi: {
1020
+ operationId: "handleSSOCallback",
1017
1021
  summary: "Callback URL for SSO provider",
1018
1022
  description:
1019
1023
  "This endpoint is used as the callback URL for SSO providers. It handles the authorization code and exchanges it for an access token",
@@ -1362,6 +1366,7 @@ export const callbackSSOSAML = (options?: SSOOptions) => {
1362
1366
  metadata: {
1363
1367
  isAction: false,
1364
1368
  openapi: {
1369
+ operationId: "handleSAMLCallback",
1365
1370
  summary: "Callback URL for SAML provider",
1366
1371
  description:
1367
1372
  "This endpoint is used as the callback URL for SAML providers.",
@@ -1584,6 +1589,14 @@ export const callbackSSOSAML = (options?: SSOOptions) => {
1584
1589
  if (existingUser) {
1585
1590
  user = existingUser;
1586
1591
  } else {
1592
+ // if implicit sign up is disabled, we should not create a new user nor a new account.
1593
+ if (options?.disableImplicitSignUp) {
1594
+ throw new APIError("UNAUTHORIZED", {
1595
+ message:
1596
+ "User not found and implicit sign up is disabled for this provider",
1597
+ });
1598
+ }
1599
+
1587
1600
  user = await ctx.context.internalAdapter.createUser({
1588
1601
  email: userInfo.email,
1589
1602
  name: userInfo.name,
@@ -1687,6 +1700,7 @@ export const acsEndpoint = (options?: SSOOptions) => {
1687
1700
  metadata: {
1688
1701
  isAction: false,
1689
1702
  openapi: {
1703
+ operationId: "handleSAMLAssertionConsumerService",
1690
1704
  summary: "SAML Assertion Consumer Service",
1691
1705
  description:
1692
1706
  "Handles SAML responses from IdP after successful authentication",
package/src/saml.test.ts CHANGED
@@ -1118,4 +1118,68 @@ describe("SAML SSO", async () => {
1118
1118
  },
1119
1119
  });
1120
1120
  });
1121
+
1122
+ it("should reject SAML sign-in when disableImplicitSignUp is true and user doesn't exist", async () => {
1123
+ const { auth: authWithDisabledSignUp, signInWithTestUser } =
1124
+ await getTestInstance({
1125
+ plugins: [sso({ disableImplicitSignUp: true })],
1126
+ });
1127
+
1128
+ const { headers } = await signInWithTestUser();
1129
+
1130
+ // Register SAML provider
1131
+ await authWithDisabledSignUp.api.registerSSOProvider({
1132
+ body: {
1133
+ providerId: "saml-test-provider",
1134
+ issuer: "http://localhost:8081",
1135
+ domain: "http://localhost:8081",
1136
+ samlConfig: {
1137
+ entryPoint: "http://localhost:8081/api/sso/saml2/idp/post",
1138
+ cert: certificate,
1139
+ callbackUrl: "http://localhost:3000/dashboard",
1140
+ wantAssertionsSigned: false,
1141
+ signatureAlgorithm: "sha256",
1142
+ digestAlgorithm: "sha256",
1143
+ idpMetadata: {
1144
+ metadata: idpMetadata,
1145
+ },
1146
+ spMetadata: {
1147
+ metadata: spMetadata,
1148
+ },
1149
+ identifierFormat:
1150
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
1151
+ },
1152
+ },
1153
+ headers: headers,
1154
+ });
1155
+
1156
+ // Identity Provider-initiated: Get SAML response directly from IdP
1157
+ // The mock IdP will return test@email.com, which doesn't exist in the DB
1158
+ let samlResponse: any;
1159
+ await betterFetch("http://localhost:8081/api/sso/saml2/idp/post", {
1160
+ onSuccess: async (context) => {
1161
+ samlResponse = await context.data;
1162
+ },
1163
+ });
1164
+
1165
+ // Attempt to complete SAML callback - should fail because test@email.com doesn't exist
1166
+ // and disableImplicitSignUp is true
1167
+ await expect(
1168
+ authWithDisabledSignUp.api.callbackSSOSAML({
1169
+ body: {
1170
+ SAMLResponse: samlResponse.samlResponse,
1171
+ RelayState: "http://localhost:3000/dashboard",
1172
+ },
1173
+ params: {
1174
+ providerId: "saml-test-provider",
1175
+ },
1176
+ }),
1177
+ ).rejects.toMatchObject({
1178
+ status: "UNAUTHORIZED",
1179
+ body: {
1180
+ message:
1181
+ "User not found and implicit sign up is disabled for this provider",
1182
+ },
1183
+ });
1184
+ });
1121
1185
  });