describe discovery

This commit is contained in:
peintnermax
2024-09-13 14:47:33 +02:00
parent 1122042101
commit 08de7ddb78
2 changed files with 29 additions and 5 deletions

View File

@@ -58,6 +58,7 @@ Requests to the APIs made:
- `startIdentityProviderFlow`
- `listUsers(org?)`
- `listAuthenticationMethodTypes`
- `getOrgsByDomain`
After a loginname is entered, a `listUsers` request is made using the loginName query to identify already registered users.
@@ -70,7 +71,8 @@ If password is the next step, we check `loginSettings.passkeysType` for Passkeys
If no user is found, we check whether registering is allowed using `loginSettings.allowRegister`.
If `loginSettings?.allowUsernamePassword` is not allowed we continue to check for available IDPs. If a single IDP is available, we directly redirect the user to signup.
If no single IDP is set, we check for `loginSettings.allowUsernamePassword` and redirect the user to /register page.
If no single IDP is set, we check for `loginSettings.allowUsernamePassword` and if no organization is set as context, we check whether we can discover a organization from the loginname of the user (using: `getOrgsByDomain`). Then if an organization is found, we check whether domainDiscovery is allowed on it and redirect the user to /register page including the discovered domain or without.
If no previous condition is met we throw an error stating the user was not found.
If the outcome after this order produces a no authentication methods found, or user not found, we check whether `loginSettings?.ignoreUnknownUsernames` is set to `true` as in this case we redirect to the /password page regardless (to not leak information about a registered user).

View File

@@ -9,6 +9,7 @@ import { idpTypeToSlug } from "../idp";
import {
getActiveIdentityProviders,
getLoginSettings,
getOrgsByDomain,
listAuthenticationMethodTypes,
listUsers,
startIdentityProviderFlow,
@@ -20,6 +21,8 @@ export type SendLoginnameCommand = {
organization?: string;
};
const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
export async function sendLoginname(command: SendLoginnameCommand) {
const users = await listUsers({
loginName: command.loginName,
@@ -181,9 +184,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
}
// TODO: check if allowDomainDiscovery has to be allowed too, to redirect to the register page
// 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?
await redirectUserToSingleIDPIfAvailable();
@@ -193,10 +194,31 @@ export async function sendLoginname(command: SendLoginnameCommand) {
loginSettings?.allowRegister &&
loginSettings?.allowUsernamePassword
) {
let orgToRegisterOn: string | undefined = command.organization;
if (
!orgToRegisterOn &&
command.loginName &&
ORG_SUFFIX_REGEX.test(command.loginName)
) {
const matched = ORG_SUFFIX_REGEX.exec(command.loginName);
const suffix = matched?.[1] ?? "";
// this just returns orgs where the suffix is set as primary domain
const orgs = await getOrgsByDomain(suffix);
const orgToCheckForDiscovery =
orgs.result && orgs.result.length === 1 ? orgs.result[0].id : undefined;
const orgLoginSettings = await getLoginSettings(orgToCheckForDiscovery);
if (orgLoginSettings?.allowDomainDiscovery) {
orgToRegisterOn = orgToCheckForDiscovery;
}
}
const params = new URLSearchParams();
if (command.organization) {
params.set("organization", command.organization);
if (orgToRegisterOn) {
params.set("organization", orgToRegisterOn);
}
if (command.authRequestId) {
params.set("authRequestId", command.authRequestId);