From b33c811a01b20ca13f2d5f1251c09fd72accc6c3 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Thu, 5 Mar 2026 09:16:49 +0100 Subject: [PATCH] fix(login): remove `loginSettings.allowRegister` check for IDPs (#11731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #11138 # Which Problems Are Solved Previously, the application strictly checked the allowRegister setting—which typically applies to local account creation—before considering if a user without an account should be redirected to an IDP, or whether they could access the register page. This blocked users from registering via identity providers when local registration was disabled but IDPs were configured. # How the Problems Are Solved - Removed the allowRegister dependency when deciding to redirect an unknown user to an IDP. The logic now only checks !allowLocalAuthentication, correctly opening up the IDP registration flow even when local auth/registration is not allowed. - Modified the block condition so the register page is only restricted if allowRegister is false and there are no configured identityProviders available. --- apps/login/src/app/(login)/register/page.tsx | 18 ++++++++- apps/login/src/lib/server/loginname.test.ts | 40 ++++++++++++++++++-- apps/login/src/lib/server/loginname.ts | 7 ++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/apps/login/src/app/(login)/register/page.tsx b/apps/login/src/app/(login)/register/page.tsx index 2a74acfe8..34647532a 100644 --- a/apps/login/src/app/(login)/register/page.tsx +++ b/apps/login/src/app/(login)/register/page.tsx @@ -55,7 +55,23 @@ export default async function Page(props: { searchParams: Promise +
+

+ +

+ + + +
+
+ + ); + } + + if (!loginSettings?.allowRegister && (!loginSettings.allowExternalIdp || identityProviders.length === 0)) { return (
diff --git a/apps/login/src/lib/server/loginname.test.ts b/apps/login/src/lib/server/loginname.test.ts index b401cc713..70b0cbb16 100644 --- a/apps/login/src/lib/server/loginname.test.ts +++ b/apps/login/src/lib/server/loginname.test.ts @@ -276,7 +276,7 @@ describe("sendLoginname", () => { }); mockListIDPLinks.mockResolvedValue({ result: [] }); mockGetActiveIdentityProviders.mockResolvedValue({ - identityProviders: [{ id: "org-idp-123", type: 0 }], + identityProviders: [{ id: "org-idp-123", type: 0, options: { isAutoCreation: true } }], }); mockIdpTypeToSlug.mockReturnValue("google"); mockStartIdentityProviderFlow.mockResolvedValue({ url: "https://org-idp.example.com/auth" }); @@ -469,7 +469,7 @@ describe("sendLoginname", () => { allowLocalAuthentication: false, }); mockGetActiveIdentityProviders.mockResolvedValue({ - identityProviders: [{ id: "idp123", type: "OIDC" }], + identityProviders: [{ id: "idp123", type: "OIDC", options: { isAutoCreation: true } }], }); mockStartIdentityProviderFlow.mockResolvedValue({ url: "https://idp.example.com/auth" }); @@ -503,6 +503,7 @@ describe("sendLoginname", () => { test("should redirect to password when ignoreUnknownUsernames is true", async () => { mockGetLoginSettings.mockResolvedValue({ ignoreUnknownUsernames: true, + allowLocalAuthentication: true, }); const result = await sendLoginname({ @@ -532,6 +533,39 @@ describe("sendLoginname", () => { expect(result).toEqual({ error: "errors.userNotFound" }); }); + test("should redirect to IDP when allowRegister: false, allowLocalAuthentication: false and a single active IDP exists", async () => { + mockGetLoginSettings.mockResolvedValue({ + allowRegister: false, + allowLocalAuthentication: false, + }); + mockGetActiveIdentityProviders.mockResolvedValue({ + identityProviders: [{ id: "idp123", type: "OIDC", options: { isAutoCreation: true } }], + }); + mockStartIdentityProviderFlow.mockResolvedValue({ url: "https://idp.example.com/auth" }); + + const result = await sendLoginname({ + loginName: "user@example.com", + }); + + expect(result).toEqual({ redirect: "https://idp.example.com/auth" }); + }); + + test("should not redirect to IDP when single active IDP does not allow creation", async () => { + mockGetLoginSettings.mockResolvedValue({ + allowRegister: false, + allowLocalAuthentication: false, + }); + mockGetActiveIdentityProviders.mockResolvedValue({ + identityProviders: [{ id: "idp123", type: "OIDC", options: { isAutoCreation: false, isCreationAllowed: false } }], + }); + + const result = await sendLoginname({ + loginName: "user@example.com", + }); + + expect(result).toEqual({ error: "errors.userNotFound" }); + }); + test("should discover organization from domain suffix when user not found without org context", async () => { // Mock login settings for instance level (no org context) mockGetLoginSettings @@ -592,7 +626,7 @@ describe("sendLoginname", () => { }); mockGetActiveIdentityProviders.mockResolvedValue({ - identityProviders: [{ id: "idp123", type: "OIDC" }], + identityProviders: [{ id: "idp123", type: "OIDC", options: { isAutoCreation: true } }], }); mockStartIdentityProviderFlow.mockResolvedValue({ url: "https://idp.example.com/auth?org=discovered-org-456" }); diff --git a/apps/login/src/lib/server/loginname.ts b/apps/login/src/lib/server/loginname.ts index 2802e4a35..e31aaf1ec 100644 --- a/apps/login/src/lib/server/loginname.ts +++ b/apps/login/src/lib/server/loginname.ts @@ -119,7 +119,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { // If no IDP links exist for the user (or no userId provided), try to get active IDPs from the organization if (identityProviders.length === 0) { const activeIdps = await getActiveIdentityProviders({ serviceConfig, orgId: organization }).then((resp) => { - return resp.identityProviders; + return resp.identityProviders.filter((idp) => idp.options?.isAutoCreation || idp.options?.isCreationAllowed); }); // If exactly one active IDP exists in the organization, redirect to it @@ -525,8 +525,9 @@ export async function sendLoginname(command: SendLoginnameCommand) { } } - // user not found, check if register is enabled on instance / organization context - if (effectiveLoginSettings?.allowRegister && !effectiveLoginSettings?.allowLocalAuthentication) { + // user not found, check if IDPs are available when local auth is not allowed + if (!effectiveLoginSettings?.allowLocalAuthentication) { + console.log("redirecting to IDP (register allowed, password not allowed)"); logger.debug("redirecting to IDP (register allowed, password not allowed)"); const resp = await redirectUserToIDP(undefined, discoveredOrganization); if (resp) {