mirror of
https://github.com/zitadel/zitadel.git
synced 2025-11-03 06:31:58 +00:00
fix(login): add email verification check before callback (#10516)
Closes https://github.com/zitadel/typescript/issues/539 This PR adds an additional email verification check before completing an auth flow, if the environment configuration `EMAIL_VERIFICATION` asks for it. # Which Problems Are Solved https://github.com/zitadel/typescript/issues/539 # How the Problems Are Solved Adds an additional check before completing an auth flow
This commit is contained in:
@@ -5,11 +5,7 @@ import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||
import { GetSessionResponse } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||
import { getMostRecentCookieWithLoginname } from "./cookies";
|
||||
import {
|
||||
getLoginSettings,
|
||||
getSession,
|
||||
listAuthenticationMethodTypes,
|
||||
} from "./zitadel";
|
||||
import { getLoginSettings, getSession, getUserByID, listAuthenticationMethodTypes } from "./zitadel";
|
||||
|
||||
type LoadMostRecentSessionParams = {
|
||||
serviceUrl: string;
|
||||
@@ -39,13 +35,7 @@ export async function loadMostRecentSession({
|
||||
* mfa is required, session is not valid anymore (e.g. session expired, user logged out, etc.)
|
||||
* to check for mfa for automatically selected session -> const response = await listAuthenticationMethodTypes(userId);
|
||||
**/
|
||||
export async function isSessionValid({
|
||||
serviceUrl,
|
||||
session,
|
||||
}: {
|
||||
serviceUrl: string;
|
||||
session: Session;
|
||||
}): Promise<boolean> {
|
||||
export async function isSessionValid({ serviceUrl, session }: { serviceUrl: string; session: Session }): Promise<boolean> {
|
||||
// session can't be checked without user
|
||||
if (!session.factors?.user) {
|
||||
console.warn("Session has no user");
|
||||
@@ -63,43 +53,22 @@ export async function isSessionValid({
|
||||
if (authMethods && authMethods.includes(AuthenticationMethodType.TOTP)) {
|
||||
mfaValid = !!session.factors.totp?.verifiedAt;
|
||||
if (!mfaValid) {
|
||||
console.warn(
|
||||
"Session has no valid totpEmail factor",
|
||||
session.factors.totp?.verifiedAt,
|
||||
);
|
||||
console.warn("Session has no valid totpEmail factor", session.factors.totp?.verifiedAt);
|
||||
}
|
||||
} else if (
|
||||
authMethods &&
|
||||
authMethods.includes(AuthenticationMethodType.OTP_EMAIL)
|
||||
) {
|
||||
} else if (authMethods && authMethods.includes(AuthenticationMethodType.OTP_EMAIL)) {
|
||||
mfaValid = !!session.factors.otpEmail?.verifiedAt;
|
||||
if (!mfaValid) {
|
||||
console.warn(
|
||||
"Session has no valid otpEmail factor",
|
||||
session.factors.otpEmail?.verifiedAt,
|
||||
);
|
||||
console.warn("Session has no valid otpEmail factor", session.factors.otpEmail?.verifiedAt);
|
||||
}
|
||||
} else if (
|
||||
authMethods &&
|
||||
authMethods.includes(AuthenticationMethodType.OTP_SMS)
|
||||
) {
|
||||
} else if (authMethods && authMethods.includes(AuthenticationMethodType.OTP_SMS)) {
|
||||
mfaValid = !!session.factors.otpSms?.verifiedAt;
|
||||
if (!mfaValid) {
|
||||
console.warn(
|
||||
"Session has no valid otpSms factor",
|
||||
session.factors.otpSms?.verifiedAt,
|
||||
);
|
||||
console.warn("Session has no valid otpSms factor", session.factors.otpSms?.verifiedAt);
|
||||
}
|
||||
} else if (
|
||||
authMethods &&
|
||||
authMethods.includes(AuthenticationMethodType.U2F)
|
||||
) {
|
||||
} else if (authMethods && authMethods.includes(AuthenticationMethodType.U2F)) {
|
||||
mfaValid = !!session.factors.webAuthN?.verifiedAt;
|
||||
if (!mfaValid) {
|
||||
console.warn(
|
||||
"Session has no valid u2f factor",
|
||||
session.factors.webAuthN?.verifiedAt,
|
||||
);
|
||||
console.warn("Session has no valid u2f factor", session.factors.webAuthN?.verifiedAt);
|
||||
}
|
||||
} else {
|
||||
// only check settings if no auth methods are available, as this would require a setup
|
||||
@@ -128,22 +97,42 @@ export async function isSessionValid({
|
||||
const validPasskey = session?.factors?.webAuthN?.verifiedAt;
|
||||
const validIDP = session?.factors?.intent?.verifiedAt;
|
||||
|
||||
const stillValid = session.expirationDate
|
||||
? timestampDate(session.expirationDate).getTime() > new Date().getTime()
|
||||
: true;
|
||||
const stillValid = session.expirationDate ? timestampDate(session.expirationDate).getTime() > new Date().getTime() : true;
|
||||
|
||||
if (!stillValid) {
|
||||
console.warn(
|
||||
"Session is expired",
|
||||
session.expirationDate
|
||||
? timestampDate(session.expirationDate).toDateString()
|
||||
: "no expiration date",
|
||||
session.expirationDate ? timestampDate(session.expirationDate).toDateString() : "no expiration date",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const validChecks = !!(validPassword || validPasskey || validIDP);
|
||||
|
||||
return stillValid && validChecks && mfaValid;
|
||||
if (!validChecks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mfaValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check email verification if EMAIL_VERIFICATION environment variable is enabled
|
||||
if (process.env.EMAIL_VERIFICATION === "true") {
|
||||
const userResponse = await getUserByID({
|
||||
serviceUrl,
|
||||
userId: session.factors.user.id,
|
||||
});
|
||||
|
||||
const humanUser = userResponse?.user?.type.case === "human" ? userResponse?.user.type.value : undefined;
|
||||
|
||||
if (humanUser && !humanUser.email?.isVerified) {
|
||||
console.warn("Session invalid: Email not verified and EMAIL_VERIFICATION is enabled", session.factors.user.id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function findValidSession({
|
||||
|
||||
Reference in New Issue
Block a user