mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-14 06:29:20 +00:00
Merge pull request #313 from zitadel/user-discovery
fix(loginname page): refactor user discovery search
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
zitadel:
|
||||
user: "${ZITADEL_DEV_UID}"
|
||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v2.65.0}"
|
||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v2.67.2}"
|
||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml'
|
||||
ports:
|
||||
- "8080:8080"
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
command: postgres -c shared_preload_libraries=pg_stat_statements -c pg_stat_statements.track=all -c shared_buffers=1GB -c work_mem=16MB -c effective_io_concurrency=100 -c wal_level=minimal -c archive_mode=off -c max_wal_senders=0
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready" ]
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: "10s"
|
||||
timeout: "30s"
|
||||
retries: 5
|
||||
|
@@ -395,6 +395,5 @@ Timebased features like the multifactor init prompt or password expiry, are not
|
||||
- Password Expiry Settings
|
||||
- Login Settings: multifactor init prompt
|
||||
- forceMFA on login settings is not checked for IDPs
|
||||
- disablePhone / disableEmail from loginSettings will be implemented right after https://github.com/zitadel/zitadel/issues/9016 is merged
|
||||
|
||||
Also note that IDP logins are considered as valid MFA. An additional MFA check will be implemented in future if enforced.
|
||||
|
@@ -20,6 +20,7 @@ export default async function Page(props: {
|
||||
const loginName = searchParams?.loginName;
|
||||
const authRequestId = searchParams?.authRequestId;
|
||||
const organization = searchParams?.organization;
|
||||
const suffix = searchParams?.suffix;
|
||||
const submit: boolean = searchParams?.submit === "true";
|
||||
|
||||
let defaultOrganization;
|
||||
@@ -34,6 +35,8 @@ export default async function Page(props: {
|
||||
organization ?? defaultOrganization,
|
||||
);
|
||||
|
||||
const contextLoginSettings = await getLoginSettings(organization);
|
||||
|
||||
const identityProviders = await getActiveIdentityProviders(
|
||||
organization ?? defaultOrganization,
|
||||
).then((resp) => {
|
||||
@@ -54,6 +57,8 @@ export default async function Page(props: {
|
||||
loginName={loginName}
|
||||
authRequestId={authRequestId}
|
||||
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
|
||||
loginSettings={contextLoginSettings}
|
||||
suffix={suffix}
|
||||
submit={submit}
|
||||
allowRegister={!!loginSettings?.allowRegister}
|
||||
>
|
||||
|
@@ -300,6 +300,7 @@ export async function GET(request: NextRequest) {
|
||||
const { authRequest } = await getAuthRequest({ authRequestId });
|
||||
|
||||
let organization = "";
|
||||
let suffix = "";
|
||||
let idpId = "";
|
||||
|
||||
if (authRequest?.scope) {
|
||||
@@ -326,6 +327,7 @@ export async function GET(request: NextRequest) {
|
||||
const orgs = await getOrgsByDomain(orgDomain);
|
||||
if (orgs.result && orgs.result.length === 1) {
|
||||
organization = orgs.result[0].id ?? "";
|
||||
suffix = orgDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,6 +450,9 @@ export async function GET(request: NextRequest) {
|
||||
if (organization) {
|
||||
loginNameUrl.searchParams.set("organization", organization);
|
||||
}
|
||||
if (suffix) {
|
||||
loginNameUrl.searchParams.set("suffix", suffix);
|
||||
}
|
||||
return NextResponse.redirect(loginNameUrl);
|
||||
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
||||
/**
|
||||
|
@@ -15,6 +15,7 @@ export type TextInputProps = DetailedHTMLProps<
|
||||
HTMLInputElement
|
||||
> & {
|
||||
label: string;
|
||||
suffix?: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
error?: string | ReactNode;
|
||||
@@ -45,6 +46,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
label,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
suffix,
|
||||
required = false,
|
||||
error,
|
||||
disabled,
|
||||
@@ -56,7 +58,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<label className="flex flex-col text-12px text-input-light-label dark:text-input-dark-label">
|
||||
<label className="relative flex flex-col text-12px text-input-light-label dark:text-input-dark-label">
|
||||
<span
|
||||
className={`leading-3 mb-1 ${
|
||||
error ? "text-warn-light-500 dark:text-warn-dark-500" : ""
|
||||
@@ -78,6 +80,12 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
{...props}
|
||||
/>
|
||||
|
||||
{suffix && (
|
||||
<span className="z-30 absolute right-[3px] bottom-[22px] transform translate-y-1/2 bg-background-light-500 dark:bg-background-dark-500 p-2 rounded-sm">
|
||||
@{suffix}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="leading-14.5px h-14.5px text-warn-light-500 dark:text-warn-dark-500 flex flex-row items-center text-12px">
|
||||
<span>{error ? error : " "}</span>
|
||||
</div>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { sendLoginname } from "@/lib/server/loginname";
|
||||
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
@@ -18,7 +19,9 @@ type Inputs = {
|
||||
type Props = {
|
||||
loginName: string | undefined;
|
||||
authRequestId: string | undefined;
|
||||
loginSettings: LoginSettings | undefined;
|
||||
organization?: string;
|
||||
suffix?: string;
|
||||
submit: boolean;
|
||||
allowRegister: boolean;
|
||||
children?: ReactNode;
|
||||
@@ -28,6 +31,8 @@ export function UsernameForm({
|
||||
loginName,
|
||||
authRequestId,
|
||||
organization,
|
||||
suffix,
|
||||
loginSettings,
|
||||
submit,
|
||||
allowRegister,
|
||||
children,
|
||||
@@ -52,6 +57,7 @@ export function UsernameForm({
|
||||
loginName: values.loginName,
|
||||
organization,
|
||||
authRequestId,
|
||||
suffix,
|
||||
})
|
||||
.catch(() => {
|
||||
setError("An internal error occurred");
|
||||
@@ -80,6 +86,18 @@ export function UsernameForm({
|
||||
}
|
||||
}, []);
|
||||
|
||||
let inputLabel = "Loginname";
|
||||
if (
|
||||
loginSettings?.disableLoginWithEmail &&
|
||||
loginSettings?.disableLoginWithPhone
|
||||
) {
|
||||
inputLabel = "Username";
|
||||
} else if (loginSettings?.disableLoginWithEmail) {
|
||||
inputLabel = "Username or phone number";
|
||||
} else if (loginSettings?.disableLoginWithPhone) {
|
||||
inputLabel = "Username or email";
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="w-full">
|
||||
<div className="">
|
||||
@@ -87,8 +105,9 @@ export function UsernameForm({
|
||||
type="text"
|
||||
autoComplete="username"
|
||||
{...register("loginName", { required: "This field is required" })}
|
||||
label="Loginname"
|
||||
label={inputLabel}
|
||||
data-testid="username-text-input"
|
||||
suffix={suffix}
|
||||
/>
|
||||
{allowRegister && (
|
||||
<button
|
||||
|
@@ -16,7 +16,8 @@ import {
|
||||
getOrgsByDomain,
|
||||
listAuthenticationMethodTypes,
|
||||
listIDPLinks,
|
||||
listUsers,
|
||||
searchUsers,
|
||||
SearchUsersCommand,
|
||||
startIdentityProviderFlow,
|
||||
} from "../zitadel";
|
||||
import { createSessionAndUpdateCookie } from "./cookie";
|
||||
@@ -25,26 +26,36 @@ export type SendLoginnameCommand = {
|
||||
loginName: string;
|
||||
authRequestId?: string;
|
||||
organization?: string;
|
||||
suffix?: string;
|
||||
};
|
||||
|
||||
const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
|
||||
|
||||
export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
const users = await listUsers({
|
||||
loginName: command.loginName,
|
||||
const loginSettingsByContext = await getLoginSettings(command.organization);
|
||||
|
||||
if (!loginSettingsByContext) {
|
||||
return { error: "Could not get login settings" };
|
||||
}
|
||||
|
||||
let searchUsersRequest: SearchUsersCommand = {
|
||||
searchValue: command.loginName,
|
||||
organizationId: command.organization,
|
||||
});
|
||||
loginSettings: loginSettingsByContext,
|
||||
suffix: command.suffix,
|
||||
};
|
||||
|
||||
const loginSettings = await getLoginSettings(command.organization);
|
||||
const searchResult = await searchUsers(searchUsersRequest);
|
||||
|
||||
const potentialUsers = users.result.filter((u) => {
|
||||
const human = u.type.case === "human" ? u.type.value : undefined;
|
||||
return loginSettings?.disableLoginWithEmail
|
||||
? human?.email?.isVerified && human?.email?.email !== command.loginName
|
||||
: loginSettings?.disableLoginWithPhone
|
||||
? human?.phone?.isVerified && human?.phone?.phone !== command.loginName
|
||||
: true;
|
||||
});
|
||||
if ("error" in searchResult && searchResult.error) {
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
if (!("result" in searchResult)) {
|
||||
return { error: "Could not search users" };
|
||||
}
|
||||
|
||||
const { result: potentialUsers } = searchResult;
|
||||
|
||||
const redirectUserToSingleIDPIfAvailable = async () => {
|
||||
const identityProviders = await getActiveIdentityProviders(
|
||||
@@ -145,9 +156,50 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
};
|
||||
|
||||
if (potentialUsers.length == 1 && potentialUsers[0].userId) {
|
||||
if (potentialUsers.length > 1) {
|
||||
return { error: "More than one user found. Provide a unique identifier." };
|
||||
} else if (potentialUsers.length == 1 && potentialUsers[0].userId) {
|
||||
const user = potentialUsers[0];
|
||||
const userId = potentialUsers[0].userId;
|
||||
|
||||
const userLoginSettings = await getLoginSettings(
|
||||
user.details?.resourceOwner,
|
||||
);
|
||||
|
||||
// compare with the concatenated suffix when set
|
||||
const concatLoginname = command.suffix
|
||||
? `${command.loginName}@${command.suffix}`
|
||||
: command.loginName;
|
||||
|
||||
const humanUser =
|
||||
potentialUsers[0].type.case === "human"
|
||||
? potentialUsers[0].type.value
|
||||
: undefined;
|
||||
|
||||
// recheck login settings after user discovery, as the search might have been done without org scope
|
||||
if (
|
||||
userLoginSettings?.disableLoginWithEmail &&
|
||||
userLoginSettings?.disableLoginWithPhone
|
||||
) {
|
||||
if (user.preferredLoginName !== concatLoginname) {
|
||||
return { error: "User not found in the system!" };
|
||||
}
|
||||
} else if (userLoginSettings?.disableLoginWithEmail) {
|
||||
if (
|
||||
user.preferredLoginName !== concatLoginname ||
|
||||
humanUser?.phone?.phone !== command.loginName
|
||||
) {
|
||||
return { error: "User not found in the system!" };
|
||||
}
|
||||
} else if (userLoginSettings?.disableLoginWithPhone) {
|
||||
if (
|
||||
user.preferredLoginName !== concatLoginname ||
|
||||
humanUser?.email?.email !== command.loginName
|
||||
) {
|
||||
return { error: "User not found in the system!" };
|
||||
}
|
||||
}
|
||||
|
||||
const checks = create(ChecksSchema, {
|
||||
user: { search: { case: "userId", value: userId } },
|
||||
});
|
||||
@@ -163,7 +215,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
|
||||
// TODO: check if handling of userstate INITIAL is needed
|
||||
if (potentialUsers[0].state === UserState.INITIAL) {
|
||||
if (user.state === UserState.INITIAL) {
|
||||
return { error: "Initial User not supported" };
|
||||
}
|
||||
|
||||
@@ -173,11 +225,6 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
|
||||
// this can be expected to be an invite as users created in console have a password set.
|
||||
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
||||
const humanUser =
|
||||
potentialUsers[0].type.case === "human"
|
||||
? potentialUsers[0].type.value
|
||||
: undefined;
|
||||
|
||||
// redirect to /verify invite if no auth method is set and email is not verified
|
||||
const inviteCheck = checkInvite(
|
||||
session,
|
||||
@@ -213,7 +260,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 (!loginSettings?.allowUsernamePassword) {
|
||||
if (!userLoginSettings?.allowUsernamePassword) {
|
||||
return {
|
||||
error:
|
||||
"Username Password not allowed! Contact your administrator for more information.",
|
||||
@@ -240,7 +287,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
};
|
||||
|
||||
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
|
||||
if (loginSettings?.passkeysType === PasskeysType.NOT_ALLOWED) {
|
||||
if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED) {
|
||||
return {
|
||||
error:
|
||||
"Passkeys not allowed! Contact your administrator for more information.",
|
||||
@@ -303,22 +350,24 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
}
|
||||
|
||||
// user not found, check if register is enabled on organization
|
||||
if (loginSettings?.allowRegister && !loginSettings?.allowUsernamePassword) {
|
||||
// TODO: do we need to handle login hints for IDPs here?
|
||||
// user not found, check if register is enabled on instance / organization context
|
||||
if (
|
||||
loginSettingsByContext?.allowRegister &&
|
||||
!loginSettingsByContext?.allowUsernamePassword
|
||||
) {
|
||||
const resp = await redirectUserToSingleIDPIfAvailable();
|
||||
if (resp) {
|
||||
return resp;
|
||||
}
|
||||
return { error: "User not found in the system" };
|
||||
} else if (
|
||||
loginSettings?.allowRegister &&
|
||||
loginSettings?.allowUsernamePassword
|
||||
loginSettingsByContext?.allowRegister &&
|
||||
loginSettingsByContext?.allowUsernamePassword
|
||||
) {
|
||||
let orgToRegisterOn: string | undefined = command.organization;
|
||||
|
||||
if (
|
||||
!loginSettings?.ignoreUnknownUsernames &&
|
||||
!loginSettingsByContext?.ignoreUnknownUsernames &&
|
||||
!orgToRegisterOn &&
|
||||
command.loginName &&
|
||||
ORG_SUFFIX_REGEX.test(command.loginName)
|
||||
@@ -338,7 +387,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
|
||||
// do not register user if ignoreUnknownUsernames is set
|
||||
if (orgToRegisterOn && !loginSettings?.ignoreUnknownUsernames) {
|
||||
if (orgToRegisterOn && !loginSettingsByContext?.ignoreUnknownUsernames) {
|
||||
const params = new URLSearchParams({ organization: orgToRegisterOn });
|
||||
|
||||
if (command.authRequestId) {
|
||||
@@ -353,7 +402,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
}
|
||||
|
||||
if (loginSettings?.ignoreUnknownUsernames) {
|
||||
if (loginSettingsByContext?.ignoreUnknownUsernames) {
|
||||
const paramsPasswordDefault = new URLSearchParams({
|
||||
loginName: command.loginName,
|
||||
});
|
||||
|
@@ -26,6 +26,7 @@ import { create, Duration } from "@zitadel/client";
|
||||
import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
||||
import { CreateCallbackRequest } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
|
||||
import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
|
||||
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { SendEmailVerificationCodeSchema } from "@zitadel/proto/zitadel/user/v2/email_pb";
|
||||
import type { RedirectURLsJson } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||
import {
|
||||
@@ -324,19 +325,24 @@ export async function createInviteCode(userId: string, host: string | null) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function listUsers({
|
||||
loginName,
|
||||
userName,
|
||||
email,
|
||||
organizationId,
|
||||
}: {
|
||||
export type ListUsersCommand = {
|
||||
loginName?: string;
|
||||
userName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
organizationId?: string;
|
||||
}) {
|
||||
};
|
||||
|
||||
export async function listUsers({
|
||||
loginName,
|
||||
userName,
|
||||
phone,
|
||||
email,
|
||||
organizationId,
|
||||
}: ListUsersCommand) {
|
||||
const queries: SearchQuery[] = [];
|
||||
|
||||
// either use loginName or userName, email, phone
|
||||
if (loginName) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
@@ -349,11 +355,11 @@ export async function listUsers({
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else if (userName || email || phone) {
|
||||
const orQueries: SearchQuery[] = [];
|
||||
|
||||
if (userName) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
if (userName) {
|
||||
const userNameQuery = create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "userNameQuery",
|
||||
value: {
|
||||
@@ -361,6 +367,44 @@ export async function listUsers({
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
orQueries.push(userNameQuery);
|
||||
}
|
||||
|
||||
if (email) {
|
||||
const emailQuery = create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
value: {
|
||||
emailAddress: email,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
orQueries.push(emailQuery);
|
||||
}
|
||||
|
||||
if (phone) {
|
||||
const phoneQuery = create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "phoneQuery",
|
||||
value: {
|
||||
number: phone,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
orQueries.push(phoneQuery);
|
||||
}
|
||||
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "orQuery",
|
||||
value: {
|
||||
queries: orQueries,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -378,20 +422,165 @@ export async function listUsers({
|
||||
);
|
||||
}
|
||||
|
||||
if (email) {
|
||||
return userService.listUsers({ queries: queries });
|
||||
}
|
||||
|
||||
export type SearchUsersCommand = {
|
||||
searchValue: string;
|
||||
loginSettings: LoginSettings;
|
||||
organizationId?: string;
|
||||
suffix?: string;
|
||||
};
|
||||
|
||||
const PhoneQuery = (searchValue: string) =>
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "phoneQuery",
|
||||
value: {
|
||||
number: searchValue,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const LoginNameQuery = (searchValue: string) =>
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "loginNameQuery",
|
||||
value: {
|
||||
loginName: searchValue,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const EmailQuery = (searchValue: string) =>
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
value: {
|
||||
emailAddress: searchValue,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* this is a dedicated search function to search for users from the loginname page
|
||||
* it searches users based on the loginName or userName and org suffix combination, and falls back to email and phone if no users are found
|
||||
* */
|
||||
|
||||
export async function searchUsers({
|
||||
searchValue,
|
||||
loginSettings,
|
||||
organizationId,
|
||||
suffix,
|
||||
}: SearchUsersCommand) {
|
||||
const queries: SearchQuery[] = [];
|
||||
|
||||
// if a suffix is provided, we search for the userName concatenated with the suffix
|
||||
if (suffix) {
|
||||
const searchValueWithSuffix = `${searchValue}@${suffix}`;
|
||||
const loginNameQuery = LoginNameQuery(searchValueWithSuffix);
|
||||
queries.push(loginNameQuery);
|
||||
} else {
|
||||
const loginNameQuery = LoginNameQuery(searchValue);
|
||||
queries.push(loginNameQuery);
|
||||
}
|
||||
|
||||
if (organizationId) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
case: "organizationIdQuery",
|
||||
value: {
|
||||
emailAddress: email,
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return userService.listUsers({ queries: queries });
|
||||
const loginNameResult = await userService.listUsers({ queries: queries });
|
||||
|
||||
if (!loginNameResult || !loginNameResult.details) {
|
||||
return { error: "An error occurred." };
|
||||
}
|
||||
|
||||
if (loginNameResult.result.length > 1) {
|
||||
return { error: "Multiple users found" };
|
||||
}
|
||||
|
||||
if (loginNameResult.result.length == 1) {
|
||||
return loginNameResult;
|
||||
}
|
||||
|
||||
const emailAndPhoneQueries: SearchQuery[] = [];
|
||||
if (
|
||||
loginSettings.disableLoginWithEmail &&
|
||||
loginSettings.disableLoginWithPhone
|
||||
) {
|
||||
return { error: "User not found in the system" };
|
||||
} else if (loginSettings.disableLoginWithEmail && searchValue.length <= 20) {
|
||||
const phoneQuery = PhoneQuery(searchValue);
|
||||
emailAndPhoneQueries.push(phoneQuery);
|
||||
} else if (loginSettings.disableLoginWithPhone) {
|
||||
const emailQuery = EmailQuery(searchValue);
|
||||
emailAndPhoneQueries.push(emailQuery);
|
||||
} else {
|
||||
const emailAndPhoneOrQueries: SearchQuery[] = [];
|
||||
|
||||
const emailQuery = EmailQuery(searchValue);
|
||||
emailAndPhoneOrQueries.push(emailQuery);
|
||||
|
||||
let phoneQuery;
|
||||
if (searchValue.length <= 20) {
|
||||
phoneQuery = PhoneQuery(searchValue);
|
||||
emailAndPhoneOrQueries.push(phoneQuery);
|
||||
}
|
||||
|
||||
emailAndPhoneQueries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "orQuery",
|
||||
value: {
|
||||
queries: emailAndPhoneOrQueries,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (organizationId) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "organizationIdQuery",
|
||||
value: {
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const emailOrPhoneResult = await userService.listUsers({
|
||||
queries: emailAndPhoneQueries,
|
||||
});
|
||||
|
||||
if (!emailOrPhoneResult || !emailOrPhoneResult.details) {
|
||||
return { error: "An error occurred." };
|
||||
}
|
||||
|
||||
if (emailOrPhoneResult.result.length > 1) {
|
||||
return { error: "Multiple users found." };
|
||||
}
|
||||
|
||||
if (emailOrPhoneResult.result.length == 1) {
|
||||
return loginNameResult;
|
||||
}
|
||||
|
||||
return { error: "User not found in the system" };
|
||||
}
|
||||
|
||||
export async function getDefaultOrg(): Promise<Organization | null> {
|
||||
|
Reference in New Issue
Block a user