mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 06:42:59 +00:00
handle password attempts error
This commit is contained in:
@@ -8,6 +8,10 @@ import {
|
||||
setSession,
|
||||
} from "@/lib/zitadel";
|
||||
import { Duration, timestampMs } from "@zitadel/client";
|
||||
import {
|
||||
CredentialsCheckError,
|
||||
ErrorDetail,
|
||||
} from "@zitadel/proto/zitadel/message_pb";
|
||||
import {
|
||||
Challenges,
|
||||
RequestChallenges,
|
||||
@@ -89,7 +93,16 @@ export async function createSessionForIdpAndUpdateCookie(
|
||||
userId,
|
||||
idpIntent,
|
||||
lifetime,
|
||||
);
|
||||
).catch((error: ErrorDetail | CredentialsCheckError) => {
|
||||
console.error("Could not set session", error);
|
||||
if ("failedAttempts" in error && error.failedAttempts) {
|
||||
throw {
|
||||
error: `Failed to authenticate: You had ${error.failedAttempts} password attempts.`,
|
||||
failedAttempts: error.failedAttempts,
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
if (!createdSession) {
|
||||
throw "Could not create session";
|
||||
@@ -148,57 +161,68 @@ export async function setSessionAndUpdateCookie(
|
||||
challenges,
|
||||
checks,
|
||||
lifetime,
|
||||
).then((updatedSession) => {
|
||||
if (updatedSession) {
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: recentCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationTs: recentCookie.creationTs,
|
||||
expirationTs: recentCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: recentCookie.loginName,
|
||||
organization: recentCookie.organization,
|
||||
};
|
||||
)
|
||||
.then((updatedSession) => {
|
||||
if (updatedSession) {
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: recentCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationTs: recentCookie.creationTs,
|
||||
expirationTs: recentCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: recentCookie.loginName,
|
||||
organization: recentCookie.organization,
|
||||
};
|
||||
|
||||
if (authRequestId) {
|
||||
sessionCookie.authRequestId = authRequestId;
|
||||
}
|
||||
|
||||
return getSession({
|
||||
sessionId: sessionCookie.id,
|
||||
sessionToken: sessionCookie.token,
|
||||
}).then((response) => {
|
||||
if (response?.session && response.session.factors?.user?.loginName) {
|
||||
const { session } = response;
|
||||
const newCookie: CustomCookieData = {
|
||||
id: sessionCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationTs: sessionCookie.creationTs,
|
||||
expirationTs: sessionCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: session.factors?.user?.loginName ?? "",
|
||||
organization: session.factors?.user?.organizationId ?? "",
|
||||
};
|
||||
|
||||
if (sessionCookie.authRequestId) {
|
||||
newCookie.authRequestId = sessionCookie.authRequestId;
|
||||
}
|
||||
|
||||
return updateSessionCookie(sessionCookie.id, newCookie).then(() => {
|
||||
return { challenges: updatedSession.challenges, ...session };
|
||||
});
|
||||
} else {
|
||||
throw "could not get session or session does not have loginName";
|
||||
if (authRequestId) {
|
||||
sessionCookie.authRequestId = authRequestId;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw "Session not be set";
|
||||
}
|
||||
});
|
||||
|
||||
return getSession({
|
||||
sessionId: sessionCookie.id,
|
||||
sessionToken: sessionCookie.token,
|
||||
}).then((response) => {
|
||||
if (response?.session && response.session.factors?.user?.loginName) {
|
||||
const { session } = response;
|
||||
const newCookie: CustomCookieData = {
|
||||
id: sessionCookie.id,
|
||||
token: updatedSession.sessionToken,
|
||||
creationTs: sessionCookie.creationTs,
|
||||
expirationTs: sessionCookie.expirationTs,
|
||||
// just overwrite the changeDate with the new one
|
||||
changeTs: updatedSession.details?.changeDate
|
||||
? `${timestampMs(updatedSession.details.changeDate)}`
|
||||
: "",
|
||||
loginName: session.factors?.user?.loginName ?? "",
|
||||
organization: session.factors?.user?.organizationId ?? "",
|
||||
};
|
||||
|
||||
if (sessionCookie.authRequestId) {
|
||||
newCookie.authRequestId = sessionCookie.authRequestId;
|
||||
}
|
||||
|
||||
return updateSessionCookie(sessionCookie.id, newCookie).then(() => {
|
||||
return { challenges: updatedSession.challenges, ...session };
|
||||
});
|
||||
} else {
|
||||
throw "could not get session or session does not have loginName";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw "Session not be set";
|
||||
}
|
||||
})
|
||||
.catch((error: ErrorDetail | CredentialsCheckError) => {
|
||||
console.error("Could not set session", error);
|
||||
if ("failedAttempts" in error && error.failedAttempts) {
|
||||
throw {
|
||||
error: `Failed to authenticate: You had ${error.failedAttempts} password attempts.`,
|
||||
failedAttempts: error.failedAttempts,
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
setSessionAndUpdateCookie,
|
||||
} from "@/lib/server/cookie";
|
||||
import {
|
||||
getLockoutSettings,
|
||||
getLoginSettings,
|
||||
getPasswordExpirySettings,
|
||||
getSession,
|
||||
@@ -98,24 +99,48 @@ export async function sendPassword(command: UpdateSessionCommand) {
|
||||
|
||||
loginSettings = await getLoginSettings(command.organization);
|
||||
|
||||
session = await createSessionAndUpdateCookie(
|
||||
checks,
|
||||
undefined,
|
||||
command.authRequestId,
|
||||
loginSettings?.passwordCheckLifetime,
|
||||
);
|
||||
try {
|
||||
session = await createSessionAndUpdateCookie(
|
||||
checks,
|
||||
undefined,
|
||||
command.authRequestId,
|
||||
loginSettings?.passwordCheckLifetime,
|
||||
);
|
||||
} catch (error: any) {
|
||||
if ("failedAttempts" in error && error.failedAttempts) {
|
||||
const lockoutSettings = await getLockoutSettings(
|
||||
command.organization,
|
||||
);
|
||||
|
||||
return {
|
||||
error: `Failed to authenticate: You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.`,
|
||||
};
|
||||
}
|
||||
return { error: "Could not create session for user" };
|
||||
}
|
||||
}
|
||||
|
||||
// this is a fake error message to hide that the user does not even exist
|
||||
return { error: "Could not verify password" };
|
||||
} else {
|
||||
session = await setSessionAndUpdateCookie(
|
||||
sessionCookie,
|
||||
command.checks,
|
||||
undefined,
|
||||
command.authRequestId,
|
||||
loginSettings?.passwordCheckLifetime,
|
||||
);
|
||||
try {
|
||||
session = await setSessionAndUpdateCookie(
|
||||
sessionCookie,
|
||||
command.checks,
|
||||
undefined,
|
||||
command.authRequestId,
|
||||
loginSettings?.passwordCheckLifetime,
|
||||
);
|
||||
} catch (error: any) {
|
||||
if ("failedAttempts" in error && error.failedAttempts) {
|
||||
const lockoutSettings = await getLockoutSettings(command.organization);
|
||||
|
||||
return {
|
||||
error: `Failed to authenticate: You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.`,
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!session?.factors?.user?.id) {
|
||||
return { error: "Could not create session for user" };
|
||||
|
||||
@@ -81,6 +81,14 @@ export async function getLoginSettings(orgId?: string) {
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
}
|
||||
|
||||
export async function getLockoutSettings(orgId?: string) {
|
||||
const callback = settingsService
|
||||
.getLockoutSettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
}
|
||||
|
||||
export async function getPasswordExpirySettings(orgId?: string) {
|
||||
const callback = settingsService
|
||||
.getPasswordExpirySettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
],
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"generate": "buf generate https://github.com/zitadel/zitadel.git --path ./proto/zitadel",
|
||||
"generate": "buf generate https://github.com/zitadel/zitadel.git#branch=fix/9198-user_password_lockout_error_response --path ./proto/zitadel",
|
||||
"clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
Reference in New Issue
Block a user