mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-02 14:12:27 +00:00
fix(login): fallback for idp login (#10876)
Closes #10671 # Which Problems Are Solved Users with password authentication disabled in their organization were seeing "Username Password not allowed!" error instead of being redirected to their organization's configured Identity Provider. This affected domain discovery and multi-tenancy use cases in Login V2. # How the Problems Are Solved - Updated `redirectUserToIDP` to accept optional `userId` and `organization` parameters - Added fallback logic to check organization-level IDPs via `getActiveIdentityProviders` - Updated all call sites to pass appropriate organization context - Added test coverage for the fallback behavior # Additional Changes - Consolidated duplicate logic by removing `redirectUserToSingleIDPIfAvailable` function, which is now handled by the unified `redirectUserToIDP` function - improved error handling on verification page --------- Co-authored-by: Ramon <mail@conblem.me>
This commit is contained in:
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "Sitzung konnte nicht erstellt werden",
|
||||
"noHostFound": "Kein Host gefunden",
|
||||
"userAlreadyVerified": "Benutzer ist bereits verifiziert!",
|
||||
"couldNotResendInvite": "Einladung konnte nicht erneut gesendet werden"
|
||||
"couldNotResendInvite": "Einladung konnte nicht erneut gesendet werden",
|
||||
"inviteSendFailed": "Einladungs-E-Mail konnte nicht gesendet werden",
|
||||
"emailSendFailed": "Verifizierungs-E-Mail konnte nicht gesendet werden"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "Could not create session",
|
||||
"noHostFound": "No host found",
|
||||
"userAlreadyVerified": "User is already verified!",
|
||||
"couldNotResendInvite": "Could not resend invite"
|
||||
"couldNotResendInvite": "Could not resend invite",
|
||||
"inviteSendFailed": "Failed to send invitation email",
|
||||
"emailSendFailed": "Failed to send verification email"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "No se pudo crear la sesión",
|
||||
"noHostFound": "No se encontró el host",
|
||||
"userAlreadyVerified": "¡El usuario ya está verificado!",
|
||||
"couldNotResendInvite": "No se pudo reenviar la invitación"
|
||||
"couldNotResendInvite": "No se pudo reenviar la invitación",
|
||||
"inviteSendFailed": "No se pudo enviar el correo de invitación",
|
||||
"emailSendFailed": "No se pudo enviar el correo de verificación"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "Impossibile creare la sessione",
|
||||
"noHostFound": "Nessun host trovato",
|
||||
"userAlreadyVerified": "L'utente è già verificato!",
|
||||
"couldNotResendInvite": "Impossibile reinviare l'invito"
|
||||
"couldNotResendInvite": "Impossibile reinviare l'invito",
|
||||
"inviteSendFailed": "Impossibile inviare l'email di invito",
|
||||
"emailSendFailed": "Impossibile inviare l'email di verifica"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -287,6 +287,21 @@
|
||||
"required": {
|
||||
"code": "必須項目です"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"couldNotResendEmail": "メールを再送信できませんでした",
|
||||
"couldNotVerifyUser": "ユーザーを確認できませんでした",
|
||||
"couldNotVerifyInvite": "招待を確認できませんでした",
|
||||
"couldNotVerifyEmail": "メールアドレスを確認できませんでした",
|
||||
"couldNotVerify": "確認できませんでした",
|
||||
"couldNotLoadUser": "ユーザーを読み込めませんでした",
|
||||
"couldNotLoadAuthenticators": "認証方法を読み込めませんでした",
|
||||
"couldNotCreateSession": "セッションを作成できませんでした",
|
||||
"noHostFound": "ホストが見つかりません",
|
||||
"userAlreadyVerified": "ユーザーは既に確認済みです!",
|
||||
"couldNotResendInvite": "招待を再送信できませんでした",
|
||||
"inviteSendFailed": "招待メールの送信に失敗しました",
|
||||
"emailSendFailed": "確認メールの送信に失敗しました"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "Nie udało się utworzyć sesji",
|
||||
"noHostFound": "Nie znaleziono hosta",
|
||||
"userAlreadyVerified": "Użytkownik jest już zweryfikowany!",
|
||||
"couldNotResendInvite": "Nie udało się ponownie wysłać zaproszenia"
|
||||
"couldNotResendInvite": "Nie udało się ponownie wysłać zaproszenia",
|
||||
"inviteSendFailed": "Nie udało się wysłać wiadomości e-mail z zaproszeniem",
|
||||
"emailSendFailed": "Nie udało się wysłać wiadomości e-mail weryfikacyjnej"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "Не удалось создать сеанс",
|
||||
"noHostFound": "Хост не найден",
|
||||
"userAlreadyVerified": "Пользователь уже подтверждён!",
|
||||
"couldNotResendInvite": "Не удалось повторно отправить приглашение"
|
||||
"couldNotResendInvite": "Не удалось повторно отправить приглашение",
|
||||
"inviteSendFailed": "Не удалось отправить письмо с приглашением",
|
||||
"emailSendFailed": "Не удалось отправить письмо с подтверждением"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -386,7 +386,9 @@
|
||||
"couldNotCreateSession": "无法创建会话",
|
||||
"noHostFound": "未找到主机",
|
||||
"userAlreadyVerified": "用户已验证!",
|
||||
"couldNotResendInvite": "无法重新发送邀请"
|
||||
"couldNotResendInvite": "无法重新发送邀请",
|
||||
"inviteSendFailed": "发送邀请邮件失败",
|
||||
"emailSendFailed": "发送验证邮件失败"
|
||||
}
|
||||
},
|
||||
"authenticator": {
|
||||
|
||||
@@ -193,6 +193,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
</div>
|
||||
|
||||
<SignInWithIdp
|
||||
showLabel={false}
|
||||
identityProviders={identityProviders}
|
||||
requestId={requestId}
|
||||
organization={sessionWithData.factors?.user?.organizationId}
|
||||
|
||||
@@ -36,6 +36,8 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
let human: HumanUser | undefined;
|
||||
let id: string | undefined;
|
||||
|
||||
let error: string | undefined;
|
||||
|
||||
const doSend = send === "true";
|
||||
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||
@@ -49,9 +51,9 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
urlTemplate:
|
||||
`${hostWithProtocol}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` +
|
||||
(requestId ? `&requestId=${requestId}` : ""),
|
||||
}).catch((error) => {
|
||||
console.error("Could not send invitation email", error);
|
||||
throw Error("Failed to send invitation email");
|
||||
}).catch((apiError) => {
|
||||
console.error("Could not send invitation email", apiError);
|
||||
error = "inviteSendFailed";
|
||||
});
|
||||
} else {
|
||||
await sendEmailCode({
|
||||
@@ -59,9 +61,9 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
urlTemplate:
|
||||
`${hostWithProtocol}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
|
||||
(requestId ? `&requestId=${requestId}` : ""),
|
||||
}).catch((error) => {
|
||||
console.error("Could not send verification email", error);
|
||||
throw Error("Failed to send verification email");
|
||||
}).catch((apiError) => {
|
||||
console.error("Could not send verification email", apiError);
|
||||
error = "emailSendFailed";
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -143,6 +145,14 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
{error && (
|
||||
<div className="py-4">
|
||||
<Alert>
|
||||
<Translated i18nKey={`errors.${error}`} namespace="verify" />
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!id && (
|
||||
<div className="py-4">
|
||||
<Alert>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
LoginSettings,
|
||||
PasskeysType,
|
||||
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { LoginSettings, PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||
import { Alert, AlertType } from "./alert";
|
||||
import { PASSKEYS, PASSWORD } from "./auth-methods";
|
||||
@@ -13,11 +10,7 @@ type Props = {
|
||||
loginSettings: LoginSettings;
|
||||
};
|
||||
|
||||
export function ChooseAuthenticatorToSetup({
|
||||
authMethods,
|
||||
params,
|
||||
loginSettings,
|
||||
}: Props) {
|
||||
export function ChooseAuthenticatorToSetup({ authMethods, params, loginSettings }: Props) {
|
||||
if (authMethods.length !== 0) {
|
||||
return (
|
||||
<Alert type={AlertType.ALERT}>
|
||||
@@ -26,26 +19,14 @@ export function ChooseAuthenticatorToSetup({
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{loginSettings.passkeysType == PasskeysType.NOT_ALLOWED &&
|
||||
!loginSettings.allowUsernamePassword && (
|
||||
<Alert type={AlertType.ALERT}>
|
||||
<Translated
|
||||
i18nKey="noMethodsAvailable"
|
||||
namespace="authenticator"
|
||||
/>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="grid w-full grid-cols-1 gap-5 pt-4">
|
||||
{!authMethods.includes(AuthenticationMethodType.PASSWORD) &&
|
||||
loginSettings.allowUsernamePassword &&
|
||||
PASSWORD(false, "/password/set?" + params)}
|
||||
{!authMethods.includes(AuthenticationMethodType.PASSKEY) &&
|
||||
loginSettings.passkeysType == PasskeysType.ALLOWED &&
|
||||
PASSKEYS(false, "/passkey/set?" + params)}
|
||||
</div>
|
||||
</>
|
||||
<div className="grid w-full grid-cols-1 gap-5 pt-4">
|
||||
{!authMethods.includes(AuthenticationMethodType.PASSWORD) &&
|
||||
loginSettings.allowUsernamePassword &&
|
||||
PASSWORD(false, "/password/set?" + params)}
|
||||
{!authMethods.includes(AuthenticationMethodType.PASSKEY) &&
|
||||
loginSettings.passkeysType == PasskeysType.ALLOWED &&
|
||||
PASSKEYS(false, "/passkey/set?" + params)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,7 @@ describe("sendLoginname", () => {
|
||||
authMethodTypes: [AuthenticationMethodType.PASSWORD],
|
||||
});
|
||||
mockListIDPLinks.mockResolvedValue({ result: [] });
|
||||
mockGetActiveIdentityProviders.mockResolvedValue({ identityProviders: [] });
|
||||
|
||||
const result = await sendLoginname({
|
||||
loginName: "user@example.com",
|
||||
@@ -259,6 +260,29 @@ describe("sendLoginname", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("should redirect to organization IDP when password not allowed, no user IDP links, but organization has active IDP", async () => {
|
||||
mockGetLoginSettings.mockResolvedValue({ allowUsernamePassword: false });
|
||||
mockListAuthenticationMethodTypes.mockResolvedValue({
|
||||
authMethodTypes: [AuthenticationMethodType.PASSWORD],
|
||||
});
|
||||
mockListIDPLinks.mockResolvedValue({ result: [] });
|
||||
mockGetActiveIdentityProviders.mockResolvedValue({
|
||||
identityProviders: [{ id: "org-idp-123", type: 0 }],
|
||||
});
|
||||
mockIdpTypeToSlug.mockReturnValue("google");
|
||||
mockStartIdentityProviderFlow.mockResolvedValue("https://org-idp.example.com/auth");
|
||||
|
||||
const result = await sendLoginname({
|
||||
loginName: "user@example.com",
|
||||
});
|
||||
|
||||
expect(result).toEqual({ redirect: "https://org-idp.example.com/auth" });
|
||||
expect(mockGetActiveIdentityProviders).toHaveBeenCalledWith({
|
||||
serviceUrl: "https://api.example.com",
|
||||
orgId: "org123", // User's organization from resourceOwner
|
||||
});
|
||||
});
|
||||
|
||||
test("should redirect to passkey when user has only passkey method and it's allowed", async () => {
|
||||
mockGetLoginSettings.mockResolvedValue({ passkeysType: PasskeysType.ALLOWED });
|
||||
mockListAuthenticationMethodTypes.mockResolvedValue({
|
||||
@@ -373,6 +397,7 @@ describe("sendLoginname", () => {
|
||||
authMethodTypes: [AuthenticationMethodType.PASSWORD],
|
||||
});
|
||||
mockListIDPLinks.mockResolvedValue({ result: [] });
|
||||
mockGetActiveIdentityProviders.mockResolvedValue({ identityProviders: [] });
|
||||
|
||||
const result = await sendLoginname({
|
||||
loginName: "user@example.com",
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from "../zitadel";
|
||||
import { createSessionAndUpdateCookie } from "./cookie";
|
||||
import { getOriginalHost } from "./host";
|
||||
import { IDPLink } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||
|
||||
export type SendLoginnameCommand = {
|
||||
loginName: string;
|
||||
@@ -68,63 +69,72 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
|
||||
const { result: potentialUsers } = searchResult;
|
||||
|
||||
const redirectUserToSingleIDPIfAvailable = async () => {
|
||||
const identityProviders = await getActiveIdentityProviders({
|
||||
serviceUrl,
|
||||
orgId: command.organization,
|
||||
}).then((resp) => {
|
||||
return resp.identityProviders;
|
||||
});
|
||||
|
||||
if (identityProviders.length === 1) {
|
||||
const _headers = await headers();
|
||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||
const host = await getOriginalHost();
|
||||
|
||||
const identityProviderType = identityProviders[0].type;
|
||||
|
||||
const provider = idpTypeToSlug(identityProviderType);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (command.requestId) {
|
||||
params.set("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization) {
|
||||
params.set("organization", command.organization);
|
||||
}
|
||||
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||
|
||||
const url = await startIdentityProviderFlow({
|
||||
const redirectUserToIDP = async (userId?: string, organization?: string) => {
|
||||
// If userId is provided, check for user-specific IDP links first
|
||||
let identityProviders: IDPLink[] = [];
|
||||
if (userId) {
|
||||
identityProviders = await listIDPLinks({
|
||||
serviceUrl,
|
||||
idpId: identityProviders[0].id,
|
||||
urls: {
|
||||
successUrl:
|
||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/success?` +
|
||||
new URLSearchParams(params),
|
||||
failureUrl:
|
||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/failure?` +
|
||||
new URLSearchParams(params),
|
||||
},
|
||||
userId,
|
||||
}).then((resp) => {
|
||||
return resp.result;
|
||||
});
|
||||
}
|
||||
|
||||
// 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({
|
||||
serviceUrl,
|
||||
orgId: organization,
|
||||
}).then((resp) => {
|
||||
return resp.identityProviders;
|
||||
});
|
||||
|
||||
if (!url) {
|
||||
return { error: t("errors.couldNotStartIDPFlow") };
|
||||
// If exactly one active IDP exists in the organization, redirect to it
|
||||
if (activeIdps.length === 1) {
|
||||
const _headers = await headers();
|
||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||
const host = await getOriginalHost();
|
||||
|
||||
const identityProviderType = activeIdps[0].type;
|
||||
const provider = idpTypeToSlug(identityProviderType);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
params.set("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||
|
||||
const url = await startIdentityProviderFlow({
|
||||
serviceUrl,
|
||||
idpId: activeIdps[0].id,
|
||||
urls: {
|
||||
successUrl:
|
||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/success?` +
|
||||
new URLSearchParams(params),
|
||||
failureUrl:
|
||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/failure?` +
|
||||
new URLSearchParams(params),
|
||||
},
|
||||
});
|
||||
|
||||
if (!url) {
|
||||
return { error: t("errors.couldNotStartIDPFlow") };
|
||||
}
|
||||
|
||||
return { redirect: url };
|
||||
}
|
||||
|
||||
return { redirect: url };
|
||||
}
|
||||
};
|
||||
|
||||
const redirectUserToIDP = async (userId: string) => {
|
||||
const identityProviders = await listIDPLinks({
|
||||
serviceUrl,
|
||||
userId,
|
||||
}).then((resp) => {
|
||||
return resp.result;
|
||||
});
|
||||
|
||||
if (identityProviders.length === 1) {
|
||||
const _headers = await headers();
|
||||
@@ -147,14 +157,18 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
const identityProviderType = idpTypeToIdentityProviderType(idpType);
|
||||
const provider = idpTypeToSlug(identityProviderType);
|
||||
|
||||
const params = new URLSearchParams({ userId });
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
params.set("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization) {
|
||||
params.set("organization", command.organization);
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||
@@ -240,6 +254,9 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
return { error: t("errors.initialUserNotSupported") };
|
||||
}
|
||||
|
||||
// Resolve organization from command or session
|
||||
const organization = command.organization ?? session.factors?.user?.organizationId;
|
||||
|
||||
const methods = await listAuthenticationMethodTypes({
|
||||
serviceUrl,
|
||||
userId: session.factors?.user?.id,
|
||||
@@ -250,15 +267,15 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
const params = new URLSearchParams({
|
||||
loginName: session.factors?.user?.loginName as string,
|
||||
send: "true", // set this to true to request a new code immediately
|
||||
invite: "true",
|
||||
invite: "true", // humanUser?.email?.isVerified ? "false" : "true", // sendInviteEmailCode results in an error if user is already initialized
|
||||
});
|
||||
|
||||
if (command.requestId) {
|
||||
params.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
params.append("organization", command.organization ?? (session.factors?.user?.organizationId as string));
|
||||
if (organization) {
|
||||
params.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: `/verify?` + params };
|
||||
@@ -270,7 +287,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
case AuthenticationMethodType.PASSWORD: // user has only password as auth method
|
||||
if (!userLoginSettings?.allowUsernamePassword) {
|
||||
// Check if user has IDPs available as alternative, that could eventually be used to register/link.
|
||||
const idpResp = await redirectUserToIDP(userId);
|
||||
const idpResp = await redirectUserToIDP(userId, organization);
|
||||
if (idpResp?.redirect) {
|
||||
return idpResp;
|
||||
}
|
||||
@@ -286,8 +303,8 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
|
||||
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
paramsPassword.append("organization", command.organization ?? session.factors?.user?.organizationId);
|
||||
if (organization) {
|
||||
paramsPassword.append("organization", organization);
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
@@ -312,14 +329,14 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
paramsPasskey.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
paramsPasskey.append("organization", command.organization ?? session.factors?.user?.organizationId);
|
||||
if (organization) {
|
||||
paramsPasskey.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + paramsPasskey };
|
||||
|
||||
case AuthenticationMethodType.IDP:
|
||||
const resp = await redirectUserToIDP(userId);
|
||||
const resp = await redirectUserToIDP(userId, organization);
|
||||
|
||||
if (resp?.error) {
|
||||
return { error: resp.error };
|
||||
@@ -339,13 +356,13 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
passkeyParams.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
passkeyParams.append("organization", command.organization ?? session.factors?.user?.organizationId);
|
||||
if (organization) {
|
||||
passkeyParams.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + passkeyParams };
|
||||
} else if (methods.authMethodTypes.includes(AuthenticationMethodType.IDP)) {
|
||||
return redirectUserToIDP(userId);
|
||||
return redirectUserToIDP(userId, organization);
|
||||
} else if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD)) {
|
||||
// Check if password authentication is allowed
|
||||
if (!userLoginSettings?.allowUsernamePassword) {
|
||||
@@ -363,8 +380,8 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
paramsPasswordDefault.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
paramsPasswordDefault.append("organization", command.organization ?? session.factors?.user?.organizationId);
|
||||
if (organization) {
|
||||
paramsPasswordDefault.append("organization", organization);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -376,7 +393,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
|
||||
// user not found, check if register is enabled on instance / organization context
|
||||
if (loginSettingsByContext?.allowRegister && !loginSettingsByContext?.allowUsernamePassword) {
|
||||
const resp = await redirectUserToSingleIDPIfAvailable();
|
||||
const resp = await redirectUserToIDP(undefined, command.organization);
|
||||
if (resp) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user