mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 01:02:17 +00:00
ts for cookie, loginname to verification
This commit is contained in:
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"verify": {
|
||||
"userIdMissing": "Keine Benutzer-ID angegeben!",
|
||||
"success": "Erfolgreich verifiziert",
|
||||
"verify": {
|
||||
"title": "Benutzer verifizieren",
|
||||
"description": "Geben Sie den Code ein, der in der Bestätigungs-E-Mail angegeben ist.",
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"verify": {
|
||||
"userIdMissing": "No userId provided!",
|
||||
"success": "The user has been verified successfully.",
|
||||
"verify": {
|
||||
"title": "Verify user",
|
||||
"description": "Enter the Code provided in the verification email.",
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"verify": {
|
||||
"userIdMissing": "¡No se proporcionó userId!",
|
||||
"success": "¡Verificación exitosa!",
|
||||
"verify": {
|
||||
"title": "Verificar usuario",
|
||||
"description": "Introduce el código proporcionado en el correo electrónico de verificación.",
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"verify": {
|
||||
"userIdMissing": "Nessun userId fornito!",
|
||||
"success": "Verifica effettuata con successo!",
|
||||
"verify": {
|
||||
"title": "Verifica utente",
|
||||
"description": "Inserisci il codice fornito nell'email di verifica.",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Alert } from "@/components/alert";
|
||||
import { BackButton } from "@/components/back-button";
|
||||
import { ChooseAuthenticatorToSetup } from "@/components/choose-authenticator-to-setup";
|
||||
import { DynamicTheme } from "@/components/dynamic-theme";
|
||||
import { UserAvatar } from "@/components/user-avatar";
|
||||
import { getSessionCookieById } from "@/lib/cookies";
|
||||
@@ -41,13 +40,14 @@ export default async function Page({
|
||||
const t = await getTranslations({ locale, namespace: "authenticator" });
|
||||
const tError = await getTranslations({ locale, namespace: "error" });
|
||||
|
||||
const { loginName, checkAfter, authRequestId, organization, sessionId } =
|
||||
searchParams;
|
||||
const { loginName, authRequestId, organization, sessionId } = searchParams;
|
||||
|
||||
const sessionWithData = sessionId
|
||||
? await loadSessionById(sessionId, organization)
|
||||
: await loadSessionByLoginname(loginName, organization);
|
||||
|
||||
console.log("sessionWithData", sessionWithData);
|
||||
|
||||
async function getAuthMethodsAndUser(session?: Session) {
|
||||
const userId = session?.factors?.user?.id;
|
||||
|
||||
@@ -93,7 +93,9 @@ export default async function Page({
|
||||
});
|
||||
}
|
||||
|
||||
const branding = await getBrandingSettings(organization);
|
||||
const branding = await getBrandingSettings(
|
||||
sessionWithData.factors?.user?.organizationId,
|
||||
);
|
||||
|
||||
const loginSettings = await getLoginSettings(
|
||||
sessionWithData.factors?.user?.organizationId,
|
||||
@@ -141,14 +143,14 @@ export default async function Page({
|
||||
|
||||
{!valid && <Alert>{tError("sessionExpired")}</Alert>}
|
||||
|
||||
{loginSettings && sessionWithData && (
|
||||
{/* {loginSettings && sessionWithData && (
|
||||
<ChooseAuthenticatorToSetup
|
||||
authMethods={sessionWithData.authMethods}
|
||||
sessionFactors={sessionWithData.factors}
|
||||
loginSettings={loginSettings}
|
||||
params={params}
|
||||
></ChooseAuthenticatorToSetup>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<div className="mt-8 flex w-full flex-row items-center">
|
||||
<BackButton />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Alert } from "@/components/alert";
|
||||
import { Alert, AlertType } from "@/components/alert";
|
||||
import { DynamicTheme } from "@/components/dynamic-theme";
|
||||
import { UserAvatar } from "@/components/user-avatar";
|
||||
import { VerifyForm } from "@/components/verify-form";
|
||||
import { getBrandingSettings, getUserByID } from "@/lib/zitadel";
|
||||
import { HumanUser, User } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
||||
@@ -47,6 +48,9 @@ export default async function Page({ searchParams }: { searchParams: any }) {
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>{t("verify.title")}</h1>
|
||||
<p className="ztdl-p mb-6 block">{t("verify.description")}</p>
|
||||
|
||||
{!userId && (
|
||||
<>
|
||||
<h1>{t("verify.title")}</h1>
|
||||
@@ -58,12 +62,24 @@ export default async function Page({ searchParams }: { searchParams: any }) {
|
||||
</>
|
||||
)}
|
||||
|
||||
<VerifyForm
|
||||
userId={userId}
|
||||
code={code}
|
||||
isInvite={invite === "true"}
|
||||
params={params}
|
||||
/>
|
||||
{user && (
|
||||
<UserAvatar
|
||||
loginName={user.preferredLoginName}
|
||||
displayName={human?.profile?.displayName}
|
||||
showDropdown={false}
|
||||
/>
|
||||
)}
|
||||
{human?.email?.isVerified ? (
|
||||
<Alert type={AlertType.INFO}>{t("success")}</Alert>
|
||||
) : (
|
||||
// check if auth methods are set
|
||||
<VerifyForm
|
||||
userId={userId}
|
||||
code={code}
|
||||
isInvite={invite === "true"}
|
||||
params={params}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { Alert } from "@/components/alert";
|
||||
import {
|
||||
resendVerification,
|
||||
verifyUserAndCreateSession,
|
||||
} from "@/lib/server/email";
|
||||
import { resendVerification, sendVerification } from "@/lib/server/email";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Button, ButtonVariants } from "./button";
|
||||
import { TextInput } from "./input";
|
||||
@@ -41,6 +38,12 @@ export function VerifyForm({ userId, code, isInvite, params }: Props) {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (code) {
|
||||
submitCodeAndContinue({ code });
|
||||
}
|
||||
}, []);
|
||||
|
||||
async function resendCode() {
|
||||
setLoading(true);
|
||||
|
||||
@@ -60,7 +63,7 @@ export function VerifyForm({ userId, code, isInvite, params }: Props) {
|
||||
async function submitCodeAndContinue(value: Inputs): Promise<boolean | void> {
|
||||
setLoading(true);
|
||||
|
||||
const verifyResponse = await verifyUserAndCreateSession({
|
||||
const verifyResponse = await sendVerification({
|
||||
code: value.code,
|
||||
userId,
|
||||
isInvite: isInvite,
|
||||
@@ -83,9 +86,6 @@ export function VerifyForm({ userId, code, isInvite, params }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>{t("verify.title")}</h1>
|
||||
<p className="ztdl-p mb-6 block">{t("verify.description")}</p>
|
||||
|
||||
<form className="w-full">
|
||||
<div className="">
|
||||
<TextInput
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { timestampDate, timestampFromMs } from "@zitadel/client";
|
||||
import { cookies } from "next/headers";
|
||||
import { LANGUAGE_COOKIE_NAME } from "./i18n";
|
||||
|
||||
@@ -11,9 +12,9 @@ export type Cookie = {
|
||||
token: string;
|
||||
loginName: string;
|
||||
organization?: string;
|
||||
creationDate: string;
|
||||
expirationDate: string;
|
||||
changeDate: string;
|
||||
creationTs: string;
|
||||
expirationTs: string;
|
||||
changeTs: string;
|
||||
authRequestId?: string; // if its linked to an OIDC flow
|
||||
};
|
||||
|
||||
@@ -66,13 +67,17 @@ export async function addSessionToCookie<T>(
|
||||
// TODO: improve cookie handling
|
||||
// this replaces the first session (oldest) with the new one
|
||||
currentSessions = [session].concat(currentSessions.slice(1));
|
||||
} else {
|
||||
currentSessions = [session].concat(currentSessions);
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanup) {
|
||||
const now = new Date();
|
||||
const filteredSessions = currentSessions.filter((session) =>
|
||||
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||
session.expirationTs
|
||||
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
|
||||
: true,
|
||||
);
|
||||
return setSessionHttpOnlyCookie(filteredSessions);
|
||||
} else {
|
||||
@@ -99,7 +104,9 @@ export async function updateSessionCookie<T>(
|
||||
if (cleanup) {
|
||||
const now = new Date();
|
||||
const filteredSessions = sessions.filter((session) =>
|
||||
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||
session.expirationTs
|
||||
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
|
||||
: true,
|
||||
);
|
||||
return setSessionHttpOnlyCookie(filteredSessions);
|
||||
} else {
|
||||
@@ -125,7 +132,9 @@ export async function removeSessionFromCookie<T>(
|
||||
if (cleanup) {
|
||||
const now = new Date();
|
||||
const filteredSessions = reducedSessions.filter((session) =>
|
||||
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||
session.expirationTs
|
||||
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
|
||||
: true,
|
||||
);
|
||||
return setSessionHttpOnlyCookie(filteredSessions);
|
||||
} else {
|
||||
@@ -141,10 +150,7 @@ export async function getMostRecentSessionCookie<T>(): Promise<any> {
|
||||
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||
|
||||
const latest = sessions.reduce((prev, current) => {
|
||||
return new Date(prev.changeDate).getTime() >
|
||||
new Date(current.changeDate).getTime()
|
||||
? prev
|
||||
: current;
|
||||
return prev.changeTs > current.changeTs ? prev : current;
|
||||
});
|
||||
|
||||
return latest;
|
||||
@@ -226,8 +232,8 @@ export async function getAllSessionCookieIds<T>(
|
||||
const now = new Date();
|
||||
return sessions
|
||||
.filter((session) =>
|
||||
session.expirationDate
|
||||
? new Date(session.expirationDate) > now
|
||||
session.expirationTs
|
||||
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
|
||||
: true,
|
||||
)
|
||||
.map((session) => session.id);
|
||||
@@ -256,7 +262,9 @@ export async function getAllSessions<T>(
|
||||
if (cleanup) {
|
||||
const now = new Date();
|
||||
return sessions.filter((session) =>
|
||||
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||
session.expirationTs
|
||||
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
|
||||
: true,
|
||||
);
|
||||
} else {
|
||||
return sessions;
|
||||
@@ -297,10 +305,7 @@ export async function getMostRecentCookieWithLoginname<T>({
|
||||
const latest =
|
||||
filtered && filtered.length
|
||||
? filtered.reduce((prev, current) => {
|
||||
return new Date(prev.changeDate).getTime() >
|
||||
new Date(current.changeDate).getTime()
|
||||
? prev
|
||||
: current;
|
||||
return prev.changeTs > current.changeTs ? prev : current;
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ type CustomCookieData = {
|
||||
token: string;
|
||||
loginName: string;
|
||||
organization?: string;
|
||||
creationDate: string;
|
||||
expirationDate: string;
|
||||
changeDate: string;
|
||||
creationTs: string;
|
||||
expirationTs: string;
|
||||
changeTs: string;
|
||||
authRequestId?: string; // if its linked to an OIDC flow
|
||||
};
|
||||
|
||||
@@ -42,13 +42,13 @@ export async function createSessionAndUpdateCookie(
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: createdSession.sessionId,
|
||||
token: createdSession.sessionToken,
|
||||
creationDate: response.session.creationDate
|
||||
creationTs: response.session.creationDate
|
||||
? `${timestampMs(response.session.creationDate)}`
|
||||
: "",
|
||||
expirationDate: response.session.expirationDate
|
||||
expirationTs: response.session.expirationDate
|
||||
? `${timestampMs(response.session.expirationDate)}`
|
||||
: "",
|
||||
changeDate: response.session.changeDate
|
||||
changeTs: response.session.changeDate
|
||||
? `${timestampMs(response.session.changeDate)}`
|
||||
: "",
|
||||
loginName: response.session.factors.user.loginName ?? "",
|
||||
@@ -97,13 +97,13 @@ export async function createSessionForIdpAndUpdateCookie(
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: createdSession.sessionId,
|
||||
token: createdSession.sessionToken,
|
||||
creationDate: response.session.creationDate
|
||||
creationTs: response.session.creationDate
|
||||
? `${timestampMs(response.session.creationDate)}`
|
||||
: "",
|
||||
expirationDate: response.session.expirationDate
|
||||
expirationTs: response.session.expirationDate
|
||||
? `${timestampMs(response.session.expirationDate)}`
|
||||
: "",
|
||||
changeDate: response.session.changeDate
|
||||
changeTs: response.session.changeDate
|
||||
? `${timestampMs(response.session.changeDate)}`
|
||||
: "",
|
||||
loginName: response.session.factors.user.loginName ?? "",
|
||||
@@ -151,10 +151,10 @@ export async function setSessionAndUpdateCookie(
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: recentCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationDate: recentCookie.creationDate,
|
||||
expirationDate: recentCookie.expirationDate,
|
||||
creationTs: recentCookie.creationTs,
|
||||
expirationTs: recentCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeDate: updatedSession.details?.changeDate
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: recentCookie.loginName,
|
||||
@@ -174,10 +174,10 @@ export async function setSessionAndUpdateCookie(
|
||||
const newCookie: CustomCookieData = {
|
||||
id: sessionCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationDate: sessionCookie.creationDate,
|
||||
expirationDate: sessionCookie.expirationDate,
|
||||
creationTs: sessionCookie.creationTs,
|
||||
expirationTs: sessionCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeDate: updatedSession.details?.changeDate
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: session.factors?.user?.loginName ?? "",
|
||||
|
||||
@@ -20,9 +20,7 @@ type VerifyUserByEmailCommand = {
|
||||
authRequestId?: string;
|
||||
};
|
||||
|
||||
export async function verifyUserAndCreateSession(
|
||||
command: VerifyUserByEmailCommand,
|
||||
) {
|
||||
export async function sendVerification(command: VerifyUserByEmailCommand) {
|
||||
const verifyResponse = command.isInvite
|
||||
? await verifyInviteCode(command.userId, command.code).catch((error) => {
|
||||
return { error: "Could not verify invite" };
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_se
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { idpTypeToIdentityProviderType, idpTypeToSlug } from "../idp";
|
||||
|
||||
import {
|
||||
getActiveIdentityProviders,
|
||||
getIDPByID,
|
||||
@@ -161,6 +162,29 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
);
|
||||
|
||||
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
||||
if (
|
||||
users.result[0].type.case === "human" &&
|
||||
users.result[0].type.value.email &&
|
||||
!users.result[0].type.value.email.isVerified
|
||||
) {
|
||||
const paramsVerify = new URLSearchParams({
|
||||
loginName: session.factors?.user?.loginName,
|
||||
userId: session.factors?.user?.id, // verify needs user id
|
||||
});
|
||||
|
||||
if (command.organization || session.factors?.user?.organizationId) {
|
||||
paramsVerify.append(
|
||||
"organization",
|
||||
command.organization ?? session.factors?.user?.organizationId,
|
||||
);
|
||||
}
|
||||
|
||||
if (command.authRequestId) {
|
||||
paramsVerify.append("authRequestId", command.authRequestId);
|
||||
}
|
||||
|
||||
redirect("/verify?" + paramsVerify);
|
||||
}
|
||||
return {
|
||||
error:
|
||||
"User has no available authentication methods. Contact your administrator to setup authentication for the requested user.",
|
||||
|
||||
@@ -3,5 +3,5 @@ export { NewAuthorizationBearerInterceptor } from "./interceptors";
|
||||
|
||||
// TODO: Move this to `./protobuf.ts` and export it from there
|
||||
export { create, fromJson, toJson } from "@bufbuild/protobuf";
|
||||
export { TimestampSchema, timestampDate, timestampFromDate, timestampMs } from "@bufbuild/protobuf/wkt";
|
||||
export { TimestampSchema, timestampDate, timestampFromDate, timestampFromMs, timestampMs } from "@bufbuild/protobuf/wkt";
|
||||
export type { Timestamp } from "@bufbuild/protobuf/wkt";
|
||||
|
||||
Reference in New Issue
Block a user