move cookie utils to @zitadel/next package

This commit is contained in:
peintnermax
2024-08-07 15:53:55 +02:00
parent 6e2441feac
commit a58ccbf671
14 changed files with 4512 additions and 5581 deletions

View File

@@ -6,6 +6,7 @@
"types": "./dist/index.d.ts",
"sideEffects": false,
"license": "MIT",
"type": "module",
"files": [
"dist/**"
],
@@ -25,6 +26,8 @@
"peerDependencies": {
"@zitadel/node": "workspace:*",
"@zitadel/react": "workspace:*",
"@zitadel/client": "workspace:*",
"@zitadel/proto": "workspace:*",
"next": "^14.2.3",
"react": "18.2.0"
},

View File

@@ -1,6 +1,4 @@
import "./styles.css";
// import "./styles.css";
export {
ZitadelNextProvider,
type ZitadelNextProps,
} from "./components/ZitadelNextProvider";
export { ZitadelNextProvider, type ZitadelNextProps } from "./components/ZitadelNextProvider";
export { loadMostRecentSession } from "./utils/session";

View 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() {}

View 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);
}

View File

@@ -1,20 +1,12 @@
{
"extends": [
"//"
],
"extends": ["//"],
"tasks": {
"dev": {
"dependsOn": [
"@zitadel/react#build"
]
"dependsOn": ["@zitadel/client#build"]
},
"build": {
"outputs": [
"dist/**"
],
"dependsOn": [
"@zitadel/react#build"
]
"outputs": ["dist/**"],
"dependsOn": ["@zitadel/client#build", "@zitadel/react#build"]
}
}
}