mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 08:23:16 +00:00
move cookie utils to @zitadel/next package
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
"@zitadel/node": "workspace:*",
|
"@zitadel/node": "workspace:*",
|
||||||
"@zitadel/proto": "workspace:*",
|
"@zitadel/proto": "workspace:*",
|
||||||
"@zitadel/react": "workspace:*",
|
"@zitadel/react": "workspace:*",
|
||||||
|
"@zitadel/next": "workspace:*",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import {
|
import { getBrandingSettings } from "@/lib/zitadel";
|
||||||
getBrandingSettings,
|
|
||||||
getLoginSettings,
|
|
||||||
getSession,
|
|
||||||
} from "@/lib/zitadel";
|
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import LoginOTP from "@/ui/LoginOTP";
|
import LoginOTP from "@/ui/LoginOTP";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { loadMostRecentSession } from "@zitadel/next";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -21,21 +17,10 @@ export default async function Page({
|
|||||||
|
|
||||||
const { method } = params;
|
const { method } = params;
|
||||||
|
|
||||||
const { session, token } = await loadSession(loginName, organization);
|
const session = await loadMostRecentSession(loginName, organization);
|
||||||
|
|
||||||
const branding = await getBrandingSettings(organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
async function loadSession(loginName?: string, organization?: string) {
|
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
);
|
|
||||||
|
|
||||||
return getSession(recent.id, recent.token).then((response) => {
|
|
||||||
return { session: response?.session, token: recent.token };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import UserAvatar from "@/ui/UserAvatar";
|
|||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { RegisterTOTPResponse } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
import { RegisterTOTPResponse } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||||
|
import { loadMostRecentSession } from "@zitadel/next";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -28,7 +29,7 @@ export default async function Page({
|
|||||||
const { method } = params;
|
const { method } = params;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
const { session, token } = await loadSession(loginName, organization);
|
const session = await loadMostRecentSession(loginName, organization);
|
||||||
|
|
||||||
let totpResponse: RegisterTOTPResponse | undefined,
|
let totpResponse: RegisterTOTPResponse | undefined,
|
||||||
totpError: Error | undefined;
|
totpError: Error | undefined;
|
||||||
@@ -56,17 +57,6 @@ export default async function Page({
|
|||||||
throw new Error("No session found");
|
throw new Error("No session found");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSession(loginName?: string, organization?: string) {
|
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
);
|
|
||||||
|
|
||||||
return getSession(recent.id, recent.token).then((response) => {
|
|
||||||
return { session: response?.session, token: recent.token };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const paramsToContinue = new URLSearchParams({});
|
const paramsToContinue = new URLSearchParams({});
|
||||||
let urlToContinue = "/accounts";
|
let urlToContinue = "/accounts";
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import DynamicTheme from "@/ui/DynamicTheme";
|
|||||||
import RegisterPasskey from "@/ui/RegisterPasskey";
|
import RegisterPasskey from "@/ui/RegisterPasskey";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
|
import { loadMostRecentSession } from "@zitadel/next";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -13,19 +14,8 @@ export default async function Page({
|
|||||||
const { loginName, promptPasswordless, organization, authRequestId } =
|
const { loginName, promptPasswordless, organization, authRequestId } =
|
||||||
searchParams;
|
searchParams;
|
||||||
|
|
||||||
const sessionFactors = await loadSession(loginName);
|
const sessionFactors = await loadMostRecentSession(loginName, organization);
|
||||||
|
|
||||||
async function loadSession(loginName?: string) {
|
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
);
|
|
||||||
return getSession(recent.id, recent.token).then((response) => {
|
|
||||||
if (response?.session) {
|
|
||||||
return response.session;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const title = !!promptPasswordless
|
const title = !!promptPasswordless
|
||||||
? "Authenticate with a passkey"
|
? "Authenticate with a passkey"
|
||||||
: "Use your passkey to confirm it's really you";
|
: "Use your passkey to confirm it's really you";
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import DynamicTheme from "@/ui/DynamicTheme";
|
|||||||
import PasswordForm from "@/ui/PasswordForm";
|
import PasswordForm from "@/ui/PasswordForm";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
|
import { loadMostRecentSession } from "@zitadel/next";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -16,20 +17,8 @@ export default async function Page({
|
|||||||
}) {
|
}) {
|
||||||
const { loginName, organization, promptPasswordless, authRequestId, alt } =
|
const { loginName, organization, promptPasswordless, authRequestId, alt } =
|
||||||
searchParams;
|
searchParams;
|
||||||
const sessionFactors = await loadSession(loginName, organization);
|
|
||||||
|
|
||||||
async function loadSession(loginName?: string, organization?: string) {
|
const sessionFactors = await loadMostRecentSession(loginName, organization);
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
);
|
|
||||||
|
|
||||||
return getSession(recent.id, recent.token).then((response) => {
|
|
||||||
if (response?.session) {
|
|
||||||
return response.session;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const branding = await getBrandingSettings(organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
const loginSettings = await getLoginSettings(organization);
|
const loginSettings = await getLoginSettings(organization);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import RegisterPasskey from "@/ui/RegisterPasskey";
|
|||||||
import RegisterU2F from "@/ui/RegisterU2F";
|
import RegisterU2F from "@/ui/RegisterU2F";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
|
import { loadMostRecentSession } from "@zitadel/next";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -13,19 +14,8 @@ export default async function Page({
|
|||||||
}) {
|
}) {
|
||||||
const { loginName, organization, authRequestId } = searchParams;
|
const { loginName, organization, authRequestId } = searchParams;
|
||||||
|
|
||||||
const sessionFactors = await loadSession(loginName);
|
const sessionFactors = await loadMostRecentSession(loginName, organization);
|
||||||
|
|
||||||
async function loadSession(loginName?: string) {
|
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
);
|
|
||||||
return getSession(recent.id, recent.token).then((response) => {
|
|
||||||
if (response?.session) {
|
|
||||||
return response.session;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const title = "Use your passkey to confirm it's really you";
|
const title = "Use your passkey to confirm it's really you";
|
||||||
const description =
|
const description =
|
||||||
"Your device will ask for your fingerprint, face, or screen lock";
|
"Your device will ask for your fingerprint, face, or screen lock";
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ export function middleware(request: NextRequest) {
|
|||||||
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
|
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
|
||||||
|
|
||||||
// this is a workaround for the next.js server not forwarding the host header
|
// this is a workaround for the next.js server not forwarding the host header
|
||||||
requestHeaders.set("x-zitadel-forwarded", `host="${request.nextUrl.host}"`);
|
requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
|
||||||
|
|
||||||
|
// this is a workaround for the next.js server not forwarding the host header
|
||||||
|
requestHeaders.set("x-zitadel-instance-host", `${INSTANCE}`);
|
||||||
|
|
||||||
const responseHeaders = new Headers();
|
const responseHeaders = new Headers();
|
||||||
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
||||||
|
|||||||
@@ -9,28 +9,32 @@
|
|||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@zitadel/node#build",
|
"@zitadel/node#build",
|
||||||
"@zitadel/client#build",
|
"@zitadel/client#build",
|
||||||
"@zitadel/react#build"
|
"@zitadel/react#build",
|
||||||
|
"@zitadel/next#build"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"test:integration": {
|
"test:integration": {
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@zitadel/node#build",
|
"@zitadel/node#build",
|
||||||
"@zitadel/client#build",
|
"@zitadel/client#build",
|
||||||
"@zitadel/react#build"
|
"@zitadel/react#build",
|
||||||
|
"@zitadel/next#build"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"test:unit": {
|
"test:unit": {
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@zitadel/node#build",
|
"@zitadel/node#build",
|
||||||
"@zitadel/client#build",
|
"@zitadel/client#build",
|
||||||
"@zitadel/react#build"
|
"@zitadel/react#build",
|
||||||
|
"@zitadel/next#build"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"test:watch": {
|
"test:watch": {
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@zitadel/node#build",
|
"@zitadel/node#build",
|
||||||
"@zitadel/client#build",
|
"@zitadel/client#build",
|
||||||
"@zitadel/react#build"
|
"@zitadel/react#build",
|
||||||
|
"@zitadel/next#build"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**"
|
"dist/**"
|
||||||
],
|
],
|
||||||
@@ -25,6 +26,8 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@zitadel/node": "workspace:*",
|
"@zitadel/node": "workspace:*",
|
||||||
"@zitadel/react": "workspace:*",
|
"@zitadel/react": "workspace:*",
|
||||||
|
"@zitadel/client": "workspace:*",
|
||||||
|
"@zitadel/proto": "workspace:*",
|
||||||
"next": "^14.2.3",
|
"next": "^14.2.3",
|
||||||
"react": "18.2.0"
|
"react": "18.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import "./styles.css";
|
// import "./styles.css";
|
||||||
|
|
||||||
export {
|
export { ZitadelNextProvider, type ZitadelNextProps } from "./components/ZitadelNextProvider";
|
||||||
ZitadelNextProvider,
|
export { loadMostRecentSession } from "./utils/session";
|
||||||
type ZitadelNextProps,
|
|
||||||
} from "./components/ZitadelNextProvider";
|
|
||||||
|
|||||||
237
packages/zitadel-next/src/utils/cookies.ts
Normal file
237
packages/zitadel-next/src/utils/cookies.ts
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
|
export type Cookie = {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
loginName: string;
|
||||||
|
organization?: string;
|
||||||
|
creationDate: string;
|
||||||
|
expirationDate: string;
|
||||||
|
changeDate: string;
|
||||||
|
authRequestId?: string; // if its linked to an OIDC flow
|
||||||
|
};
|
||||||
|
|
||||||
|
type SessionCookie<T> = Cookie & T;
|
||||||
|
|
||||||
|
function setSessionHttpOnlyCookie<T>(sessions: SessionCookie<T>[]) {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
// @ts-ignore
|
||||||
|
return cookiesList.set({
|
||||||
|
name: "sessions",
|
||||||
|
value: JSON.stringify(sessions),
|
||||||
|
httpOnly: true,
|
||||||
|
path: "/",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addSessionToCookie<T>(session: SessionCookie<T>, cleanup: boolean = false): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
let currentSessions: SessionCookie<T>[] = stringifiedCookie?.value ? JSON.parse(stringifiedCookie?.value) : [];
|
||||||
|
|
||||||
|
const index = currentSessions.findIndex((s) => s.loginName === session.loginName);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
currentSessions[index] = session;
|
||||||
|
} else {
|
||||||
|
currentSessions = [...currentSessions, session];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanup) {
|
||||||
|
const now = new Date();
|
||||||
|
const filteredSessions = currentSessions.filter((session) =>
|
||||||
|
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||||
|
);
|
||||||
|
return setSessionHttpOnlyCookie(filteredSessions);
|
||||||
|
} else {
|
||||||
|
return setSessionHttpOnlyCookie(currentSessions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSessionCookie<T>(id: string, session: SessionCookie<T>, cleanup: boolean = false): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
const sessions: SessionCookie<T>[] = stringifiedCookie?.value ? JSON.parse(stringifiedCookie?.value) : [session];
|
||||||
|
|
||||||
|
const foundIndex = sessions.findIndex((session) => session.id === id);
|
||||||
|
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
sessions[foundIndex] = session;
|
||||||
|
if (cleanup) {
|
||||||
|
const now = new Date();
|
||||||
|
const filteredSessions = sessions.filter((session) =>
|
||||||
|
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||||
|
);
|
||||||
|
return setSessionHttpOnlyCookie(filteredSessions);
|
||||||
|
} else {
|
||||||
|
return setSessionHttpOnlyCookie(sessions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw "updateSessionCookie<T>: session id now found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeSessionFromCookie<T>(session: SessionCookie<T>, cleanup: boolean = false): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
const sessions: SessionCookie<T>[] = stringifiedCookie?.value ? JSON.parse(stringifiedCookie?.value) : [session];
|
||||||
|
|
||||||
|
const reducedSessions = sessions.filter((s) => s.id !== session.id);
|
||||||
|
if (cleanup) {
|
||||||
|
const now = new Date();
|
||||||
|
const filteredSessions = reducedSessions.filter((session) =>
|
||||||
|
session.expirationDate ? new Date(session.expirationDate) > now : true,
|
||||||
|
);
|
||||||
|
return setSessionHttpOnlyCookie(filteredSessions);
|
||||||
|
} else {
|
||||||
|
return setSessionHttpOnlyCookie(reducedSessions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMostRecentSessionCookie<T>(): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
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 latest;
|
||||||
|
} else {
|
||||||
|
return Promise.reject("no session cookie found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSessionCookieById<T>(id: string, organization?: string): Promise<SessionCookie<T>> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||||
|
|
||||||
|
const found = sessions.find((s) => (organization ? s.organization === organization && s.id === id : s.id === id));
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
} else {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSessionCookieByLoginName<T>(loginName: string, organization?: string): Promise<SessionCookie<T>> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||||
|
const found = sessions.find((s) =>
|
||||||
|
organization ? s.organization === organization && s.loginName === loginName : s.loginName === loginName,
|
||||||
|
);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
} else {
|
||||||
|
return Promise.reject("no cookie found with loginName: " + loginName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.reject("no session cookie found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param cleanup when true, removes all expired sessions, default true
|
||||||
|
* @returns Session Cookies
|
||||||
|
*/
|
||||||
|
export async function getAllSessionCookieIds<T>(cleanup: boolean = false): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||||
|
|
||||||
|
if (cleanup) {
|
||||||
|
const now = new Date();
|
||||||
|
return sessions
|
||||||
|
.filter((session) => (session.expirationDate ? new Date(session.expirationDate) > now : true))
|
||||||
|
.map((session) => session.id);
|
||||||
|
} else {
|
||||||
|
return sessions.map((session) => session.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param cleanup when true, removes all expired sessions, default true
|
||||||
|
* @returns Session Cookies
|
||||||
|
*/
|
||||||
|
export async function getAllSessions<T>(cleanup: boolean = false): Promise<SessionCookie<T>[]> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||||
|
|
||||||
|
if (cleanup) {
|
||||||
|
const now = new Date();
|
||||||
|
return sessions.filter((session) => (session.expirationDate ? new Date(session.expirationDate) > now : true));
|
||||||
|
} else {
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns most recent session filtered by optinal loginName
|
||||||
|
* @param loginName
|
||||||
|
* @returns most recent session
|
||||||
|
*/
|
||||||
|
export async function getMostRecentCookieWithLoginname<T>(loginName?: string, organization?: string): Promise<any> {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const stringifiedCookie = cookiesList.get("sessions");
|
||||||
|
|
||||||
|
if (stringifiedCookie?.value) {
|
||||||
|
const sessions: SessionCookie<T>[] = JSON.parse(stringifiedCookie?.value);
|
||||||
|
let filtered = sessions.filter((cookie) => {
|
||||||
|
return !!loginName ? cookie.loginName === loginName : true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (organization) {
|
||||||
|
filtered = filtered.filter((cookie) => {
|
||||||
|
return cookie.organization === organization;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const latest =
|
||||||
|
filtered && filtered.length
|
||||||
|
? filtered.reduce((prev, current) => {
|
||||||
|
return new Date(prev.changeDate).getTime() > new Date(current.changeDate).getTime() ? prev : current;
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (latest) {
|
||||||
|
return latest;
|
||||||
|
} else {
|
||||||
|
console.error("sessions", sessions, loginName, organization);
|
||||||
|
return Promise.reject("Could not get the context or retrieve a session");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.reject("Could not read session cookie");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearSessions() {}
|
||||||
22
packages/zitadel-next/src/utils/session.ts
Normal file
22
packages/zitadel-next/src/utils/session.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { createSessionServiceClient } from "@zitadel/client/v2";
|
||||||
|
import { createServerTransport } from "@zitadel/node";
|
||||||
|
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||||
|
import { getMostRecentCookieWithLoginname } from "./cookies";
|
||||||
|
|
||||||
|
const SESSION_LIFETIME_S = 3000;
|
||||||
|
|
||||||
|
const transport = createServerTransport(process.env.ZITADEL_SERVICE_USER_TOKEN!, {
|
||||||
|
baseUrl: process.env.ZITADEL_API_URL!,
|
||||||
|
httpVersion: "2",
|
||||||
|
});
|
||||||
|
|
||||||
|
const sessionService = createSessionServiceClient(transport);
|
||||||
|
|
||||||
|
export async function loadMostRecentSession(loginName?: string, organization?: string): Promise<Session | undefined> {
|
||||||
|
const recent = await getMostRecentCookieWithLoginname(loginName, organization);
|
||||||
|
return getMostRecentSession(recent.id, recent.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMostRecentSession(sessionId: string, sessionToken: string) {
|
||||||
|
return sessionService.getSession({ sessionId, sessionToken }, {}).then((resp) => resp.session);
|
||||||
|
}
|
||||||
@@ -1,20 +1,12 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["//"],
|
||||||
"//"
|
|
||||||
],
|
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"dev": {
|
"dev": {
|
||||||
"dependsOn": [
|
"dependsOn": ["@zitadel/client#build"]
|
||||||
"@zitadel/react#build"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"outputs": [
|
"outputs": ["dist/**"],
|
||||||
"dist/**"
|
"dependsOn": ["@zitadel/client#build", "@zitadel/react#build"]
|
||||||
],
|
|
||||||
"dependsOn": [
|
|
||||||
"@zitadel/react#build"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9711
pnpm-lock.yaml
generated
9711
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user