mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 10:15:04 +00:00
fix: refactor user discovery search
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
listAuthenticationMethodTypes,
|
||||
listIDPLinks,
|
||||
listUsers,
|
||||
ListUsersCommand,
|
||||
startIdentityProviderFlow,
|
||||
} from "../zitadel";
|
||||
import { createSessionAndUpdateCookie } from "./cookie";
|
||||
@@ -29,21 +30,22 @@ export type SendLoginnameCommand = {
|
||||
const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
|
||||
|
||||
export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
const users = await listUsers({
|
||||
loginName: command.loginName,
|
||||
const loginSettingsByContext = await getLoginSettings(command.organization);
|
||||
|
||||
let listUsersRequest: ListUsersCommand = {
|
||||
userName: command.loginName,
|
||||
organizationId: command.organization,
|
||||
});
|
||||
};
|
||||
|
||||
const loginSettings = await getLoginSettings(command.organization);
|
||||
if (!loginSettingsByContext?.disableLoginWithEmail) {
|
||||
listUsersRequest.email = command.loginName;
|
||||
}
|
||||
|
||||
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 (!loginSettingsByContext?.disableLoginWithPhone) {
|
||||
listUsersRequest.phone = command.loginName;
|
||||
}
|
||||
|
||||
const { result: potentialUsers } = await listUsers(listUsersRequest);
|
||||
|
||||
const redirectUserToSingleIDPIfAvailable = async () => {
|
||||
const identityProviders = await getActiveIdentityProviders(
|
||||
@@ -144,9 +146,16 @@ 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,
|
||||
);
|
||||
|
||||
const checks = create(ChecksSchema, {
|
||||
user: { search: { case: "userId", value: userId } },
|
||||
});
|
||||
@@ -162,7 +171,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" };
|
||||
}
|
||||
|
||||
@@ -172,9 +181,9 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
|
||||
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
||||
if (
|
||||
potentialUsers[0].type.case === "human" &&
|
||||
potentialUsers[0].type.value.email &&
|
||||
!potentialUsers[0].type.value.email.isVerified
|
||||
user.type.case === "human" &&
|
||||
user.type.value.email &&
|
||||
!user.type.value.email.isVerified
|
||||
) {
|
||||
const paramsVerify = new URLSearchParams({
|
||||
loginName: session.factors?.user?.loginName,
|
||||
@@ -219,7 +228,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.",
|
||||
@@ -246,7 +255,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.",
|
||||
@@ -309,22 +318,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: "Could not find user" };
|
||||
} 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)
|
||||
@@ -344,7 +355,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) {
|
||||
@@ -358,7 +369,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
}
|
||||
|
||||
if (loginSettings?.ignoreUnknownUsernames) {
|
||||
if (loginSettingsByContext?.ignoreUnknownUsernames) {
|
||||
const paramsPasswordDefault = new URLSearchParams({
|
||||
loginName: command.loginName,
|
||||
});
|
||||
|
||||
@@ -291,19 +291,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 loginName or userName and email or phone are required
|
||||
if (loginName) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
@@ -316,9 +321,57 @@ export async function listUsers({
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (userName && (email || phone)) {
|
||||
const userNameQuery = create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "userNameQuery",
|
||||
value: {
|
||||
userName: userName,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const orQueries: SearchQuery[] = [userNameQuery];
|
||||
|
||||
if (email) {
|
||||
const emailQuery = create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
value: {
|
||||
emailAddress: email,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
orQueries.push(emailQuery);
|
||||
}
|
||||
|
||||
if (userName) {
|
||||
if (phone) {
|
||||
// TODO add after https://github.com/zitadel/zitadel/issues/9016 is merged
|
||||
// const phoneQuery = create(SearchQuerySchema, {
|
||||
// query: {
|
||||
// case: "phoneQuery",
|
||||
// value: {
|
||||
// emailAddress: email,
|
||||
// method: TextQueryMethod.EQUALS,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// orQueries.push(phoneQuery);
|
||||
}
|
||||
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "orQuery",
|
||||
value: {
|
||||
queries: orQueries,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (userName) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
@@ -345,19 +398,6 @@ export async function listUsers({
|
||||
);
|
||||
}
|
||||
|
||||
if (email) {
|
||||
queries.push(
|
||||
create(SearchQuerySchema, {
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
value: {
|
||||
emailAddress: email,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return userService.listUsers({ queries: queries });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user