Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/login/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "Mit AzureAD anmelden",
"signInWithGithub": "Mit GitHub anmelden",
"signInWithGitlab": "Mit GitLab anmelden",
"lastUsed": "Zuletzt verwendet",
"loginSuccess": {
"title": "Anmeldung erfolgreich",
"description": "Sie haben sich erfolgreich angemeldet!"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"signInWithAzureAD": "Sign in with AzureAD",
"signInWithGithub": "Sign in with GitHub",
"signInWithGitlab": "Sign in with GitLab",
"lastUsed": "Last used",
"loginSuccess": {
"title": "Sign in confirmed",
"description": "Directing you to your account"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "Iniciar sesión con AzureAD",
"signInWithGithub": "Iniciar sesión con GitHub",
"signInWithGitlab": "Iniciar sesión con GitLab",
"lastUsed": "Usado recientemente",
"loginSuccess": {
"title": "Inicio de sesión exitoso",
"description": "¡Has iniciado sesión con éxito!"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "Accedi con AzureAD",
"signInWithGithub": "Accedi con GitHub",
"signInWithGitlab": "Accedi con GitLab",
"lastUsed": "Usato di recente",
"loginSuccess": {
"title": "Accesso riuscito",
"description": "Accesso effettuato con successo!"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "Zaloguj się przez AzureAD",
"signInWithGithub": "Zaloguj się przez GitHub",
"signInWithGitlab": "Zaloguj się przez GitLab",
"lastUsed": "Ostatnio używane",
"loginSuccess": {
"title": "Logowanie udane",
"description": "Zostałeś pomyślnie zalogowany!"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "Войти через AzureAD",
"signInWithGithub": "Войти через GitHub",
"signInWithGitlab": "Войти через GitLab",
"lastUsed": "Использован последним",
"loginSuccess": {
"title": "Вход выполнен успешно",
"description": "Вы успешно вошли в систему!"
Expand Down
1 change: 1 addition & 0 deletions apps/login/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"signInWithAzureAD": "用 AzureAD 登录",
"signInWithGithub": "用 GitHub 登录",
"signInWithGitlab": "用 GitLab 登录",
"lastUsed": "上次使用",
"loginSuccess": {
"title": "登录成功",
"description": "您已成功登录!"
Expand Down
2 changes: 1 addition & 1 deletion apps/login/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
2 changes: 1 addition & 1 deletion apps/login/src/app/(main)/(boxed)/accounts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default async function Page(props: {
<Link href={`/loginname?` + params}>
<div
className={clsx(
"transition-all gap-3 cursor-pointer flex flex-row items-center border outline-none px-6 text-sm py-2 border-transparent hover:border-navy rounded-lg min-h-[40px]",
"transition-all gap-3 cursor-pointer flex flex-row items-center border outline-none px-6 text-sm py-2 border-transparent hover:border-navy dark:hover:border-cream rounded-lg min-h-[40px]",
sessions.length > 0 ? "" : "justify-center",
)}
>
Expand Down
5 changes: 4 additions & 1 deletion apps/login/src/app/(main)/(boxed)/authenticator/set/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ChooseAuthenticatorToSetup } from "@/components/choose-authenticator-to
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { Translated } from "@/components/translated";
import { UserAvatar } from "@/components/user-avatar";
import { getSessionCookieById } from "@/lib/cookies";
import { getLastUsedIdpId, getSessionCookieById } from "@/lib/cookies";
import { generateRouteMetadata } from "@/lib/metadata";
import { getServiceUrlFromHeaders } from "@/lib/service-url";
import { loadMostRecentSession } from "@/lib/session";
Expand Down Expand Up @@ -146,6 +146,8 @@ export default async function Page(props: {
return resp.identityProviders;
});

const lastUsedIdpId = await getLastUsedIdpId();

const params = new URLSearchParams({
initial: "true", // defines that a code is not required and is therefore not shown in the UI
});
Expand Down Expand Up @@ -200,6 +202,7 @@ export default async function Page(props: {
requestId={requestId}
organization={sessionWithData.factors?.user?.organizationId}
linkOnly={true} // tell the callback function to just link the IDP and not login, to get an error when user is already available
lastUsedIdpId={lastUsedIdpId}
></SignInWithIdp>
</>
)}
Expand Down
6 changes: 5 additions & 1 deletion apps/login/src/app/(main)/(boxed)/idp/link/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { Translated } from "@/components/translated";
import { getMostRecentSessionCookie } from "@/lib/cookies";
import { getLastUsedIdpId, getMostRecentSessionCookie } from "@/lib/cookies";
import { idpTypeToSlug } from "@/lib/idp";
import { generateRouteMetadata } from "@/lib/metadata";
import { redirectToIdp } from "@/lib/server/idp";
Expand Down Expand Up @@ -63,6 +63,8 @@ export default async function Page(props: {
userLoginName = session?.factors?.user?.loginName;
} catch {}

const lastUsedIdpId = await getLastUsedIdpId();

// If not logged in, prompt user to sign-in with the chosen provider
if (!userId) {
let providersToShow = activeIdentityProviders;
Expand Down Expand Up @@ -94,6 +96,7 @@ export default async function Page(props: {
requestId={requestId}
organization={organization}
preventCreation={true} // prevent account creation in case they login with a wrong account
lastUsedIdpId={lastUsedIdpId}
onSuccessRedirectTo={(() => {
// redirect back to the link page with all the params
const params = new URLSearchParams();
Expand Down Expand Up @@ -183,6 +186,7 @@ export default async function Page(props: {
linkOnly={true}
userId={userId}
onSuccessRedirectTo={"/idp/link"}
lastUsedIdpId={lastUsedIdpId}
/>
) : (
<Alert type={AlertType.INFO}>
Expand Down
4 changes: 4 additions & 0 deletions apps/login/src/app/(main)/(boxed)/idp/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { Translated } from "@/components/translated";
import { getLastUsedIdpId } from "@/lib/cookies";
import { generateRouteMetadata } from "@/lib/metadata";
import { getServiceUrlFromHeaders } from "@/lib/service-url";
import { getActiveIdentityProviders } from "@/lib/zitadel";
Expand All @@ -25,6 +26,8 @@ export default async function Page(props: {
return resp.identityProviders;
});

const lastUsedIdpId = await getLastUsedIdpId();

return (
<>
<h1>
Expand All @@ -39,6 +42,7 @@ export default async function Page(props: {
identityProviders={identityProviders}
requestId={requestId}
organization={organization}
lastUsedIdpId={lastUsedIdpId}
></SignInWithIdp>
)}
</>
Expand Down
4 changes: 4 additions & 0 deletions apps/login/src/app/(main)/(illustration)/loginname/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { Translated } from "@/components/translated";
import { UsernameForm } from "@/components/username-form";
import { getLastUsedIdpId } from "@/lib/cookies";
import { generateRouteMetadata } from "@/lib/metadata";
import { getServiceUrlFromHeaders } from "@/lib/service-url";
import {
Expand Down Expand Up @@ -56,6 +57,8 @@ export default async function Page(props: {
return resp.identityProviders;
});

const lastUsedIdpId = await getLastUsedIdpId();

return (
<>
<h1>
Expand Down Expand Up @@ -89,6 +92,7 @@ export default async function Page(props: {
identityProviders={identityProviders}
requestId={requestId}
organization={organization}
lastUsedIdpId={lastUsedIdpId}
></SignInWithIdp>
)}

Expand Down
4 changes: 4 additions & 0 deletions apps/login/src/app/(main)/(illustration)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Alert } from "@/components/alert";
import { RegisterForm } from "@/components/register-form";
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { Translated } from "@/components/translated";
import { getLastUsedIdpId } from "@/lib/cookies";
import { generateRouteMetadata } from "@/lib/metadata";
import { getServiceUrlFromHeaders } from "@/lib/service-url";
import {
Expand Down Expand Up @@ -55,6 +56,8 @@ export default async function Page(props: {
});
});

const lastUsedIdpId = await getLastUsedIdpId();

return (
<>
<h1>
Expand Down Expand Up @@ -100,6 +103,7 @@ export default async function Page(props: {
identityProviders={identityProviders}
requestId={requestId}
organization={organization}
lastUsedIdpId={lastUsedIdpId}
></SignInWithIdp>
)}

Expand Down
15 changes: 12 additions & 3 deletions apps/login/src/components/idps/base-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { clsx } from "clsx";
import { Loader2 } from "lucide-react";
import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef } from "react";
import { useFormStatus } from "react-dom";
import { Translated } from "../translated";

export type SignInWithIdentityProviderProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
Expand All @@ -12,17 +13,20 @@ export type SignInWithIdentityProviderProps = DetailedHTMLProps<
name?: string;
e2e?: string;
containerclassname?: string;
isLastUsed?: boolean;
};

export const BaseButton = forwardRef<
HTMLButtonElement,
SignInWithIdentityProviderProps
>(function BaseButton(props, ref) {
const formStatus = useFormStatus();
const { isLastUsed, containerclassname, e2e, ...buttonProps } = props;

return (
<button
{...props}
{...buttonProps}
data-testid={e2e}
type="submit"
ref={ref}
disabled={formStatus.pending}
Expand All @@ -35,11 +39,16 @@ export const BaseButton = forwardRef<
<div className="flex-1 justify-between flex items-center gap-4 relative">
<div
className={clsx(
"flex-1 flex flex-row items-center",
props.containerclassname,
"flex-1 flex flex-row items-center relative",
containerclassname,
)}
>
{props.children}
{isLastUsed && (
<span className="text-[10px] font-medium px-1.5 py-0.5 rounded-full text-button-primary-foreground shrink-0 absolute -right-10 -top-4 bg-button-primary-background">
<Translated i18nKey="lastUsed" namespace="idp" />
</span>
)}
</div>
{formStatus.pending && (
<div className="absolute right-2">
Expand Down
6 changes: 3 additions & 3 deletions apps/login/src/components/session-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,20 @@ export function SessionItem({
<span className="font-semibold text-sm">
{session.factors?.user?.displayName}
</span>
<span className="text-xs text-navy opacity-60 text-ellipsis font-normal">
<span className="text-xs text-navy dark:text-cream opacity-60 text-ellipsis font-normal">
{session.factors?.user?.loginName}
</span>
{error ? (
<span className="text-xs text-red-500 text-ellipsis text-left pt-2 max-w-52">
{error}
</span>
) : valid ? (
<span className="text-xs text-navy opacity-60 text-ellipsis">
<span className="text-xs text-navy opacity-60 text-ellipsis dark:text-cream">
{verifiedAt && moment(timestampDate(verifiedAt)).fromNow()}
</span>
) : (
verifiedAt && (
<span className="text-xs text-navy opacity-60 text-ellipsis">
<span className="text-xs text-navy opacity-60 text-ellipsis dark:text-cream">
expired{" "}
{session.expirationDate &&
moment(timestampDate(session.expirationDate)).fromNow()}
Expand Down
4 changes: 3 additions & 1 deletion apps/login/src/components/sign-in-with-idp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface SignInWithIDPProps {
userId?: string;
onSuccessRedirectTo?: string;
preventCreation?: boolean;
lastUsedIdpId?: string | null;
}

export function SignInWithIdp({
Expand All @@ -35,6 +36,7 @@ export function SignInWithIdp({
userId,
onSuccessRedirectTo,
preventCreation,
lastUsedIdpId,
}: Readonly<SignInWithIDPProps>) {
const [state, action, _isPending] = useActionState(redirectToIdp, {});

Expand Down Expand Up @@ -86,7 +88,7 @@ export function SignInWithIdp({
{preventCreation && (
<input type="hidden" name="preventCreation" value="true" />
)}
<Component key={id} name={name} />
<Component key={id} name={name} isLastUsed={id === lastUsedIdpId} />
</form>
) : null;
};
Expand Down
22 changes: 22 additions & 0 deletions apps/login/src/lib/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,28 @@ export async function getAllSessions<T>(
* @param organization optional organization to filter cookies
* @returns most recent session
*/
const LAST_USED_IDP_COOKIE_NAME = "last-used-idp-id";

export async function setLastUsedIdpId(idpId: string): Promise<void> {
const cookiesList = await cookies();

await cookiesList.set({
name: LAST_USED_IDP_COOKIE_NAME,
value: idpId,
httpOnly: true,
path: "/",
sameSite: "lax",
secure: process.env.NODE_ENV === "production",
maxAge: 60 * 60 * 24 * 365, // 1 year
});
}

export async function getLastUsedIdpId(): Promise<string | null> {
const cookiesList = await cookies();
const cookie = cookiesList.get(LAST_USED_IDP_COOKIE_NAME);
return cookie?.value ?? null;
}

export async function getMostRecentCookieWithLoginname<T>({
loginName,
organization,
Expand Down
4 changes: 3 additions & 1 deletion apps/login/src/lib/server/idp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { revalidatePath } from "next/cache";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { getNextUrl } from "../client";
import { getMostRecentSessionCookie } from "../cookies";
import { getMostRecentSessionCookie, setLastUsedIdpId } from "../cookies";
import { getServiceUrlFromHeaders } from "../service-url";
import { checkEmailVerification, checkMFAFactors } from "../verify-helper";
import { createSessionForIdpAndUpdateCookie } from "./cookie";
Expand Down Expand Up @@ -52,6 +52,7 @@ export async function redirectToIdp(

// redirect to LDAP page where username and password is requested
if (provider === "ldap") {
await setLastUsedIdpId(idpId);
params.set("idpId", idpId);
redirect(`/idp/ldap?` + params.toString());
}
Expand All @@ -69,6 +70,7 @@ export async function redirectToIdp(
}

if (response && "redirect" in response && response?.redirect) {
await setLastUsedIdpId(idpId);
redirect(response.redirect);
}

Expand Down
Loading