fix(login, console): replace allowUsernamePassword with allowLocalAuthentication in login, change translation in console (#11413)

# Which Problems Are Solved

This PR deprecates and replaces the use of `allowUsernamePassword` with
`allowLocalAuthentication`.
It additionally disables the username on organizations that disallow
local authentication. In such a case, only IDPs are shown.

# How the Problems Are Solved

- replace `allowUsernamePassword` by `allowLocalAuthentication`
- disable username form if !allowLocalAuthentication
- translate property in console UI
This commit is contained in:
Max Peintner
2026-02-04 12:54:37 +00:00
committed by GitHub
parent 6f07b8deee
commit 38dbd4cf3e
46 changed files with 171 additions and 142 deletions
@@ -57,7 +57,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
</div>
<div className="w-full">
<UsernameForm
{loginSettings?.allowLocalAuthentication && <UsernameForm
loginName={loginName}
requestId={requestId}
organization={organization} // stick to "organization" as we still want to do user discovery based on the searchParams not the default organization, later the organization is determined by the found user
@@ -66,7 +66,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
suffix={suffix}
submit={submit}
allowRegister={!!loginSettings?.allowRegister}
></UsernameForm>
></UsernameForm>}
{loginSettings?.allowExternalIdp && !!identityProviders?.length && (
<div className="w-full pb-4 pt-6">
@@ -75,6 +75,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
requestId={requestId}
organization={organization}
postErrorRedirectUrl="/loginname"
showLabel={loginSettings?.allowLocalAuthentication}
></SignInWithIdp>
</div>
)}
+1 -2
View File
@@ -13,7 +13,6 @@ import {
getPasswordComplexitySettings,
} from "@/lib/zitadel";
import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { headers } from "next/headers";
@@ -93,7 +92,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
{legal &&
passwordComplexitySettings &&
organization &&
(loginSettings.allowUsernamePassword || loginSettings.passkeysType == PasskeysType.ALLOWED) && (
loginSettings.allowLocalAuthentication && (
<RegisterForm
idpCount={!loginSettings?.allowExternalIdp ? 0 : identityProviders.length}
legal={legal}
@@ -52,7 +52,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
</div>
<div className="w-full"></div>
</DynamicTheme>
) : loginSettings?.allowRegister && loginSettings.allowUsernamePassword ? (
) : loginSettings?.allowRegister && loginSettings.allowLocalAuthentication ? (
<DynamicTheme branding={branding}>
<div className="flex flex-col space-y-4">
<h1>
@@ -20,16 +20,17 @@ export function ChooseAuthenticatorToLogin({
return (
<>
{authMethods.includes(AuthenticationMethodType.PASSWORD) &&
loginSettings?.allowUsernamePassword && (
loginSettings?.allowLocalAuthentication && (
<div className="ztdl-p">
<Translated i18nKey="chooseAlternativeMethod" namespace="idp" />
</div>
)}
<div className="grid w-full grid-cols-1 gap-5 pt-4">
{authMethods.includes(AuthenticationMethodType.PASSWORD) &&
loginSettings?.allowUsernamePassword &&
loginSettings?.allowLocalAuthentication &&
PASSWORD(false, "/password?" + params)}
{authMethods.includes(AuthenticationMethodType.PASSKEY) &&
loginSettings?.allowLocalAuthentication &&
loginSettings?.passkeysType == PasskeysType.ALLOWED &&
PASSKEYS(false, "/passkey?" + params)}
</div>
@@ -21,7 +21,7 @@ export function ChooseAuthenticatorToSetup({ authMethods, params, loginSettings
return (
<div className="grid w-full grid-cols-1 gap-5 pt-4">
{!authMethods.includes(AuthenticationMethodType.PASSWORD) &&
loginSettings.allowUsernamePassword &&
loginSettings.allowLocalAuthentication &&
PASSWORD(false, "/password/set?" + params)}
{!authMethods.includes(AuthenticationMethodType.PASSKEY) &&
loginSettings.passkeysType == PasskeysType.ALLOWED &&
+4 -4
View File
@@ -159,7 +159,7 @@ export function RegisterForm({
<PrivacyPolicyCheckboxes legal={legal} onChange={setTosAndPolicyAccepted} />
)}
{/* show chooser if both methods are allowed */}
{loginSettings && loginSettings.allowUsernamePassword && loginSettings.passkeysType == PasskeysType.ALLOWED && (
{loginSettings && loginSettings.allowLocalAuthentication && loginSettings.passkeysType == PasskeysType.ALLOWED && (
<>
<p className="ztdl-p mb-6 mt-4 block text-left">
<Translated i18nKey="selectMethod" namespace="register" />
@@ -170,7 +170,7 @@ export function RegisterForm({
</div>
</>
)}
{!loginSettings?.allowUsernamePassword &&
{!loginSettings?.allowLocalAuthentication &&
loginSettings?.passkeysType !== PasskeysType.ALLOWED &&
(!loginSettings?.allowExternalIdp || !idpCount) && (
<div className="py-4">
@@ -194,9 +194,9 @@ export function RegisterForm({
disabled={loading || !canSubmit}
onClick={handleSubmit((values) => {
const usePasswordToContinue: boolean =
loginSettings?.allowUsernamePassword && loginSettings?.passkeysType == PasskeysType.ALLOWED
loginSettings?.allowLocalAuthentication && loginSettings?.passkeysType == PasskeysType.ALLOWED
? !(selected === methods[0]) // choose selection if both available
: !!loginSettings?.allowUsernamePassword; // if password is chosen
: !!loginSettings?.allowLocalAuthentication; // if password is chosen
// set password as default if only password is allowed
return submitAndContinue(values, usePasswordToContinue);
})}
+61 -41
View File
@@ -134,7 +134,7 @@ describe("sendLoginname", () => {
});
test("should return error when user search fails", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({ error: "Search failed" });
const result = await sendLoginname({
@@ -145,7 +145,7 @@ describe("sendLoginname", () => {
});
test("should return error when search result has no result field", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({});
const result = await sendLoginname({
@@ -156,7 +156,7 @@ describe("sendLoginname", () => {
});
test("should return error when more than one user found", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({
result: [
{ userId: "user1", preferredLoginName: "[email protected]" },
@@ -192,7 +192,7 @@ describe("sendLoginname", () => {
};
beforeEach(() => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
mockCreate.mockReturnValue({});
mockCreateSessionAndUpdateCookie.mockResolvedValue({ session: mockSession, sessionCookie: {} });
@@ -232,7 +232,7 @@ describe("sendLoginname", () => {
});
test("should attempt IDP redirect when password is not allowed but user has IDP links", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: false });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
});
@@ -253,7 +253,7 @@ describe("sendLoginname", () => {
});
test("should return error when password not allowed and no IDP links available", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: false });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
});
@@ -265,12 +265,12 @@ describe("sendLoginname", () => {
});
expect(result).toEqual({
error: "errors.usernamePasswordNotAllowed",
error: "errors.localAuthenticationNotAllowed",
});
});
test("should redirect to organization IDP when password not allowed, no user IDP links, but organization has active IDP", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: false });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
});
@@ -293,7 +293,7 @@ describe("sendLoginname", () => {
});
test("should redirect to passkey when user has only passkey method and it's allowed", async () => {
mockGetLoginSettings.mockResolvedValue({ passkeysType: PasskeysType.ALLOWED });
mockGetLoginSettings.mockResolvedValue({ passkeysType: PasskeysType.ALLOWED, allowLocalAuthentication: true });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSKEY],
});
@@ -310,7 +310,25 @@ describe("sendLoginname", () => {
});
test("should return error when passkeys are not allowed", async () => {
mockGetLoginSettings.mockResolvedValue({ passkeysType: PasskeysType.NOT_ALLOWED });
mockGetLoginSettings.mockResolvedValue({ passkeysType: PasskeysType.NOT_ALLOWED, allowLocalAuthentication: true });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSKEY],
});
const result = await sendLoginname({
loginName: "[email protected]",
});
expect(result).toEqual({
error: "errors.passkeysNotAllowed",
});
});
test("should return error when passkeys are allowed but allowLocalAuthentication is false", async () => {
mockGetLoginSettings.mockResolvedValue({
passkeysType: PasskeysType.ALLOWED,
allowLocalAuthentication: false,
});
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSKEY],
});
@@ -342,7 +360,7 @@ describe("sendLoginname", () => {
test("should NOT create session when ignoreUnknownUsernames is true", async () => {
mockGetLoginSettings.mockResolvedValue({
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: true,
});
mockListAuthenticationMethodTypes.mockResolvedValue({
@@ -377,8 +395,8 @@ describe("sendLoginname", () => {
expect((result as any).redirect).toContain("altPassword=true"); // password is allowed
});
test("should not show password alternative when password is not allowed", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
test("should return error when allowLocalAuthentication is false (disabling both password and passkey)", async () => {
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: false });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD, AuthenticationMethodType.PASSKEY],
});
@@ -387,9 +405,9 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toBeDefined();
expect(result?.redirect).toMatch(/^\/passkey\?/);
expect(result?.redirect).toContain("altPassword=false"); // password is not allowed
expect(result).toEqual({
error: "errors.localAuthenticationNotAllowed",
});
});
test("should redirect to IDP when no passkey but IDP available", async () => {
@@ -422,7 +440,7 @@ describe("sendLoginname", () => {
});
test("should return error when password is only method in multi-method scenario but not allowed", async () => {
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: false });
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
});
@@ -434,7 +452,7 @@ describe("sendLoginname", () => {
});
expect(result).toEqual({
error: "errors.usernamePasswordNotAllowed",
error: "errors.localAuthenticationNotAllowed",
});
});
});
@@ -448,7 +466,7 @@ describe("sendLoginname", () => {
test("should redirect to single IDP when register allowed but password not allowed", async () => {
mockGetLoginSettings.mockResolvedValue({
allowRegister: true,
allowUsernamePassword: false,
allowLocalAuthentication: false,
});
mockGetActiveIdentityProviders.mockResolvedValue({
identityProviders: [{ id: "idp123", type: "OIDC" }],
@@ -465,7 +483,7 @@ describe("sendLoginname", () => {
test("should redirect to register when both register and password allowed", async () => {
mockGetLoginSettings.mockResolvedValue({
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
});
@@ -504,7 +522,7 @@ describe("sendLoginname", () => {
test("should return error when user not found and no registration allowed", async () => {
mockGetLoginSettings.mockResolvedValue({
allowRegister: false,
allowUsernamePassword: true,
allowLocalAuthentication: true,
});
const result = await sendLoginname({
@@ -519,14 +537,14 @@ describe("sendLoginname", () => {
mockGetLoginSettings
.mockResolvedValueOnce({
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
})
// Mock login settings for discovered org - must include all necessary flags
.mockResolvedValueOnce({
allowDomainDiscovery: true,
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
});
@@ -559,13 +577,13 @@ describe("sendLoginname", () => {
mockGetLoginSettings
.mockResolvedValueOnce({
allowRegister: true,
allowUsernamePassword: false,
allowLocalAuthentication: false,
})
// Mock login settings for discovered org - must include all necessary flags
.mockResolvedValueOnce({
allowDomainDiscovery: true,
allowRegister: true,
allowUsernamePassword: false,
allowLocalAuthentication: false,
});
// Mock org discovery to return one org with matching domain
@@ -603,7 +621,7 @@ describe("sendLoginname", () => {
mockGetLoginSettings
.mockResolvedValueOnce({
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
})
// Mock login settings for org with domain discovery disabled
@@ -627,7 +645,7 @@ describe("sendLoginname", () => {
test("should not discover org if multiple orgs match the domain", async () => {
mockGetLoginSettings.mockResolvedValue({
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
});
@@ -651,7 +669,7 @@ describe("sendLoginname", () => {
test("should use provided organization instead of discovering when org context exists", async () => {
mockGetLoginSettings.mockResolvedValue({
allowRegister: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: false,
});
@@ -669,11 +687,11 @@ describe("sendLoginname", () => {
expect(mockGetOrgsByDomain).not.toHaveBeenCalled();
});
test("should redirect to password when ignoreUnknownUsernames is true, allowRegister is true, but allowUsernamePassword is false (User not found)", async () => {
test("should redirect to password when ignoreUnknownUsernames is true, allowRegister is true, but allowLocalAuthentication is false (User not found)", async () => {
mockSearchUsers.mockResolvedValue({ result: [] });
mockGetLoginSettings.mockResolvedValue({
allowRegister: true,
allowUsernamePassword: false,
allowLocalAuthentication: false,
ignoreUnknownUsernames: true,
});
mockGetActiveIdentityProviders.mockResolvedValue({ identityProviders: [] });
@@ -747,7 +765,7 @@ describe("sendLoginname", () => {
});
mockGetLoginSettings.mockResolvedValue({
ignoreUnknownUsernames: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
});
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
@@ -777,7 +795,7 @@ describe("sendLoginname", () => {
});
mockGetLoginSettings.mockResolvedValue({
ignoreUnknownUsernames: true,
allowUsernamePassword: true,
allowLocalAuthentication: true,
});
mockListAuthenticationMethodTypes.mockResolvedValue({
authMethodTypes: [AuthenticationMethodType.PASSWORD],
@@ -809,7 +827,7 @@ describe("sendLoginname", () => {
};
mockGetLoginSettings.mockResolvedValue({
allowUsernamePassword: false,
allowLocalAuthentication: false,
ignoreUnknownUsernames: true,
});
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
@@ -826,7 +844,7 @@ describe("sendLoginname", () => {
ignoreUnknownUsernames: true,
});
expect(result).not.toEqual({ error: "errors.usernamePasswordNotAllowed" });
expect(result).not.toEqual({ error: "errors.localAuthenticationNotAllowed" });
expect(result).toHaveProperty("redirect");
expect((result as any).redirect).toMatch(/^\/password\?/);
});
@@ -892,7 +910,7 @@ describe("sendLoginname", () => {
},
};
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
mockCreate.mockReturnValue({});
mockCreateSessionAndUpdateCookie.mockResolvedValue({ session: mockSession, sessionCookie: {} });
@@ -923,7 +941,7 @@ describe("sendLoginname", () => {
},
};
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: true });
mockGetLoginSettings.mockResolvedValue({ allowLocalAuthentication: true });
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
mockCreate.mockReturnValue({});
mockCreateSessionAndUpdateCookie.mockResolvedValue({ session: mockSession, sessionCookie: {} });
@@ -962,7 +980,7 @@ describe("sendLoginname", () => {
};
mockGetLoginSettings.mockResolvedValue({
allowUsernamePassword: true,
allowLocalAuthentication: true,
ignoreUnknownUsernames: true,
});
// Mock search result returns a user with resolved/different loginName
@@ -1006,6 +1024,7 @@ describe("sendLoginname", () => {
mockGetLoginSettings.mockResolvedValue({
passkeysType: PasskeysType.ALLOWED,
allowLocalAuthentication: true,
ignoreUnknownUsernames: true,
});
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
@@ -1047,6 +1066,7 @@ describe("sendLoginname", () => {
mockGetLoginSettings.mockResolvedValue({
passkeysType: PasskeysType.ALLOWED,
allowLocalAuthentication: true,
ignoreUnknownUsernames: true,
});
mockSearchUsers.mockResolvedValue({ result: [mockUser] });
@@ -1088,10 +1108,10 @@ describe("sendLoginname", () => {
// Mock implementation to return different settings based on organization
mockGetLoginSettings.mockImplementation(async (args: any) => {
if (args.organization === "context-org") {
return { allowUsernamePassword: true, ignoreUnknownUsernames: true };
return { allowLocalAuthentication: true, ignoreUnknownUsernames: true };
}
if (args.organization === "user-org") {
return { allowUsernamePassword: true, ignoreUnknownUsernames: false };
return { allowLocalAuthentication: true, ignoreUnknownUsernames: false };
}
return {};
});
@@ -1136,10 +1156,10 @@ describe("sendLoginname", () => {
mockGetLoginSettings.mockImplementation(async (args: any) => {
if (args.organization === "context-org") {
return { allowUsernamePassword: true, ignoreUnknownUsernames: false };
return { allowLocalAuthentication: true, ignoreUnknownUsernames: false };
}
if (args.organization === "user-org") {
return { allowUsernamePassword: true, ignoreUnknownUsernames: true };
return { allowLocalAuthentication: true, ignoreUnknownUsernames: true };
}
return {};
});
+16 -9
View File
@@ -336,7 +336,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const method = methods.authMethodTypes[0];
switch (method) {
case AuthenticationMethodType.PASSWORD: // user has only password as auth method
if (!userLoginSettings?.allowUsernamePassword) {
if (!userLoginSettings?.allowLocalAuthentication) {
// Check if user has IDPs available as alternative, that could eventually be used to register/link.
const idpResp = await redirectUserToIDP(userId, organization);
if (idpResp?.redirect) {
@@ -348,7 +348,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
return {
error: t("errors.usernamePasswordNotAllowed"),
error: t("errors.localAuthenticationNotAllowed"),
};
}
@@ -371,7 +371,10 @@ export async function sendLoginname(command: SendLoginnameCommand) {
};
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED) {
if (
userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED ||
!userLoginSettings?.allowLocalAuthentication
) {
if (command.ignoreUnknownUsernames) {
return preventUserEnumeration(command.organization);
}
@@ -406,12 +409,16 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
} else {
// prefer passkey in favor of other methods
if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY)) {
if (
methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY) &&
userLoginSettings?.passkeysType !== PasskeysType.NOT_ALLOWED &&
userLoginSettings?.allowLocalAuthentication
) {
const passkeyParams = new URLSearchParams({
loginName: command.ignoreUnknownUsernames
? command.loginName
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
altPassword: `${methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD) && userLoginSettings?.allowUsernamePassword}`, // show alternative password option only if allowed
altPassword: `${methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD) && userLoginSettings?.allowLocalAuthentication}`, // show alternative password option only if allowed
});
if (command.requestId) {
@@ -427,12 +434,12 @@ export async function sendLoginname(command: SendLoginnameCommand) {
return redirectUserToIDP(userId, organization);
} else if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD)) {
// Check if password authentication is allowed
if (!userLoginSettings?.allowUsernamePassword) {
if (!userLoginSettings?.allowLocalAuthentication) {
if (command.ignoreUnknownUsernames) {
return preventUserEnumeration(command.organization);
}
return {
error: "Username Password not allowed! Contact your administrator for more information.",
error: t("errors.localAuthenticationNotAllowed"),
};
}
@@ -497,7 +504,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
// user not found, check if register is enabled on instance / organization context
if (effectiveLoginSettings?.allowRegister && !effectiveLoginSettings?.allowUsernamePassword) {
if (effectiveLoginSettings?.allowRegister && !effectiveLoginSettings?.allowLocalAuthentication) {
console.log("redirecting to IDP (register allowed, password not allowed)");
const resp = await redirectUserToIDP(undefined, discoveredOrganization);
if (resp) {
@@ -506,7 +513,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
console.log("IDP redirect failed, returning user not found");
return preventUserEnumeration(discoveredOrganization);
} else if (effectiveLoginSettings?.allowRegister && effectiveLoginSettings?.allowUsernamePassword) {
} else if (effectiveLoginSettings?.allowRegister && effectiveLoginSettings?.allowLocalAuthentication) {
console.log("register and password both allowed");
// do not register user if ignoreUnknownUsernames is set
if (discoveredOrganization && !effectiveLoginSettings?.ignoreUnknownUsernames) {