mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-25 19:10:53 +00:00
check for valid sessions, cleanup
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
|||||||
listSessions,
|
listSessions,
|
||||||
startIdentityProviderFlow,
|
startIdentityProviderFlow,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { create } from "@zitadel/client";
|
import { create, timestampDate } from "@zitadel/client";
|
||||||
import {
|
import {
|
||||||
AuthRequest,
|
AuthRequest,
|
||||||
Prompt,
|
Prompt,
|
||||||
@@ -37,24 +37,51 @@ const ORG_SCOPE_REGEX = /urn:zitadel:iam:org:id:([0-9]+)/;
|
|||||||
const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/; // TODO: check regex for all domain character options
|
const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/; // TODO: check regex for all domain character options
|
||||||
const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/;
|
const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/;
|
||||||
|
|
||||||
function findSession(
|
function isSessionValid(session: Session): boolean {
|
||||||
|
const validPassword = session?.factors?.password?.verifiedAt;
|
||||||
|
const validPasskey = session?.factors?.webAuthN?.verifiedAt;
|
||||||
|
const validIDP = session?.factors?.intent?.verifiedAt;
|
||||||
|
|
||||||
|
const stillValid = session.expirationDate
|
||||||
|
? timestampDate(session.expirationDate) > new Date()
|
||||||
|
: true;
|
||||||
|
|
||||||
|
const validFactors = !!(
|
||||||
|
(validPassword || validPasskey || validIDP) &&
|
||||||
|
stillValid
|
||||||
|
);
|
||||||
|
|
||||||
|
return stillValid && validFactors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findValidSession(
|
||||||
sessions: Session[],
|
sessions: Session[],
|
||||||
authRequest: AuthRequest,
|
authRequest: AuthRequest,
|
||||||
): Session | undefined {
|
): Session | undefined {
|
||||||
if (authRequest.hintUserId) {
|
const validSessionsWithHint = sessions
|
||||||
console.log(`find session for hintUserId: ${authRequest.hintUserId}`);
|
.filter((s) => {
|
||||||
return sessions.find((s) => s.factors?.user?.id === authRequest.hintUserId);
|
if (authRequest.hintUserId) {
|
||||||
|
return s.factors?.user?.id === authRequest.hintUserId;
|
||||||
|
}
|
||||||
|
if (authRequest.loginHint) {
|
||||||
|
return s.factors?.user?.loginName === authRequest.loginHint;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.filter(isSessionValid);
|
||||||
|
|
||||||
|
if (validSessionsWithHint.length === 0) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
if (authRequest.loginHint) {
|
|
||||||
console.log(`find session for loginHint: ${authRequest.loginHint}`);
|
validSessionsWithHint.sort((a, b) => {
|
||||||
return sessions.find(
|
const dateA = a.changeDate ? timestampDate(a.changeDate).getTime() : 0;
|
||||||
(s) => s.factors?.user?.loginName === authRequest.loginHint,
|
const dateB = b.changeDate ? timestampDate(b.changeDate).getTime() : 0;
|
||||||
);
|
return dateB - dateA;
|
||||||
}
|
});
|
||||||
if (sessions.length) {
|
|
||||||
return sessions[0];
|
// return most recently changed session
|
||||||
}
|
return sessions[0];
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
@@ -226,8 +253,8 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
if (authRequest && authRequest.prompt.includes(Prompt.CREATE)) {
|
if (authRequest && authRequest.prompt.includes(Prompt.CREATE)) {
|
||||||
const registerUrl = new URL("/register", request.url);
|
const registerUrl = new URL("/register", request.url);
|
||||||
if (authRequest?.id) {
|
if (authRequest.id) {
|
||||||
registerUrl.searchParams.set("authRequestId", authRequest?.id);
|
registerUrl.searchParams.set("authRequestId", authRequest.id);
|
||||||
}
|
}
|
||||||
if (organization) {
|
if (organization) {
|
||||||
registerUrl.searchParams.set("organization", organization);
|
registerUrl.searchParams.set("organization", organization);
|
||||||
@@ -260,8 +287,8 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loginNameUrl = new URL("/loginname", request.url);
|
const loginNameUrl = new URL("/loginname", request.url);
|
||||||
if (authRequest?.id) {
|
if (authRequest.id) {
|
||||||
loginNameUrl.searchParams.set("authRequestId", authRequest?.id);
|
loginNameUrl.searchParams.set("authRequestId", authRequest.id);
|
||||||
}
|
}
|
||||||
if (authRequest.loginHint) {
|
if (authRequest.loginHint) {
|
||||||
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
||||||
@@ -272,81 +299,82 @@ export async function GET(request: NextRequest) {
|
|||||||
return NextResponse.redirect(loginNameUrl);
|
return NextResponse.redirect(loginNameUrl);
|
||||||
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
||||||
// NONE prompt - silent authentication
|
// NONE prompt - silent authentication
|
||||||
|
const selectedSession = findValidSession(sessions, authRequest);
|
||||||
|
|
||||||
let selectedSession = findSession(sessions, authRequest);
|
if (!selectedSession || !selectedSession.id) {
|
||||||
|
|
||||||
if (selectedSession && selectedSession.id) {
|
|
||||||
const cookie = sessionCookies.find(
|
|
||||||
(cookie) => cookie.id === selectedSession?.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cookie && cookie.id && cookie.token) {
|
|
||||||
const session = {
|
|
||||||
sessionId: cookie?.id,
|
|
||||||
sessionToken: cookie?.token,
|
|
||||||
};
|
|
||||||
const { callbackUrl } = await createCallback(
|
|
||||||
create(CreateCallbackRequestSchema, {
|
|
||||||
authRequestId,
|
|
||||||
callbackKind: {
|
|
||||||
case: "session",
|
|
||||||
value: create(SessionSchema, session),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
return NextResponse.redirect(callbackUrl);
|
|
||||||
} else {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "No active session found" },
|
|
||||||
{ status: 400 }, // TODO: check for correct status code
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "No active session found" },
|
{ error: "No active session found" },
|
||||||
{ status: 400 }, // TODO: check for correct status code
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// check for loginHint, userId hint sessions
|
|
||||||
let selectedSession = findSession(sessions, authRequest);
|
|
||||||
|
|
||||||
if (selectedSession && selectedSession.id) {
|
const cookie = sessionCookies.find(
|
||||||
const cookie = sessionCookies.find(
|
(cookie) => cookie.id === selectedSession.id,
|
||||||
(cookie) => cookie.id === selectedSession?.id,
|
);
|
||||||
|
|
||||||
|
if (!cookie || !cookie.id || !cookie.token) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "No active session found" },
|
||||||
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (cookie && cookie.id && cookie.token) {
|
const session = {
|
||||||
const session = {
|
sessionId: cookie.id,
|
||||||
sessionId: cookie?.id,
|
sessionToken: cookie.token,
|
||||||
sessionToken: cookie?.token,
|
};
|
||||||
};
|
|
||||||
try {
|
const { callbackUrl } = await createCallback(
|
||||||
const { callbackUrl } = await createCallback(
|
create(CreateCallbackRequestSchema, {
|
||||||
create(CreateCallbackRequestSchema, {
|
authRequestId,
|
||||||
authRequestId,
|
callbackKind: {
|
||||||
callbackKind: {
|
case: "session",
|
||||||
case: "session",
|
value: create(SessionSchema, session),
|
||||||
value: create(SessionSchema, session),
|
},
|
||||||
},
|
}),
|
||||||
}),
|
);
|
||||||
);
|
return NextResponse.redirect(callbackUrl);
|
||||||
if (callbackUrl) {
|
} else {
|
||||||
return NextResponse.redirect(callbackUrl);
|
// check for loginHint, userId hint and valid sessions
|
||||||
} else {
|
let selectedSession = findValidSession(sessions, authRequest);
|
||||||
console.log(
|
|
||||||
"could not create callback, redirect user to choose other account",
|
if (!selectedSession || !selectedSession.id) {
|
||||||
);
|
return gotoAccounts();
|
||||||
return gotoAccounts();
|
}
|
||||||
}
|
|
||||||
} catch (error) {
|
const cookie = sessionCookies.find(
|
||||||
console.error(error);
|
(cookie) => cookie.id === selectedSession.id,
|
||||||
return gotoAccounts();
|
);
|
||||||
}
|
|
||||||
|
if (!cookie || !cookie.id || !cookie.token) {
|
||||||
|
return gotoAccounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = {
|
||||||
|
sessionId: cookie.id,
|
||||||
|
sessionToken: cookie.token,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { callbackUrl } = await createCallback(
|
||||||
|
create(CreateCallbackRequestSchema, {
|
||||||
|
authRequestId,
|
||||||
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: create(SessionSchema, session),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (callbackUrl) {
|
||||||
|
return NextResponse.redirect(callbackUrl);
|
||||||
} else {
|
} else {
|
||||||
|
console.log(
|
||||||
|
"could not create callback, redirect user to choose other account",
|
||||||
|
);
|
||||||
return gotoAccounts();
|
return gotoAccounts();
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
return gotoAccounts();
|
return gotoAccounts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user