From 51a408611be2b2297234d05b05bf538d30ade29d Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 6 Jun 2023 17:11:49 +0200 Subject: [PATCH 1/3] delete account api --- apps/login/app/(login)/accounts/page.tsx | 64 +++------------ apps/login/app/session/route.ts | 45 ++++++++++- apps/login/lib/zitadel.ts | 10 +++ apps/login/ui/SessionItem.tsx | 99 ++++++++++++++++++++++++ apps/login/utils/cookies.ts | 18 +++++ packages/zitadel-server/src/index.ts | 1 + 6 files changed, 182 insertions(+), 55 deletions(-) create mode 100644 apps/login/ui/SessionItem.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index b280580ce8c..645f6392ca8 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -1,11 +1,10 @@ -import { Session } from "#/../../packages/zitadel-server/dist"; +import { Session } from "@zitadel/server"; import { listSessions, server } from "#/lib/zitadel"; import Alert from "#/ui/Alert"; -import { Avatar } from "#/ui/Avatar"; import { getAllSessionIds } from "#/utils/cookies"; -import { UserPlusIcon, XCircleIcon } from "@heroicons/react/24/outline"; -import moment from "moment"; +import { UserPlusIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; +import SessionItem from "#/ui/SessionItem"; async function loadSessions(): Promise { const ids = await getAllSessionIds(); @@ -23,7 +22,7 @@ async function loadSessions(): Promise { } export default async function Page() { - const sessions = await loadSessions(); + let sessions = await loadSessions(); return (
@@ -35,56 +34,15 @@ export default async function Page() { sessions .filter((session) => session?.factors?.user?.loginName) .map((session, index) => { - const validPassword = session?.factors?.password?.verifiedAt; return ( - { + "use server"; + sessions = sessions.filter((s) => s.id !== session.id); + }} key={"session-" + index} - href={ - validPassword - ? `/signedin?` + - new URLSearchParams({ - loginName: session.factors?.user?.loginName as string, - }) - : `/password?` + - new URLSearchParams({ - loginName: session.factors?.user?.loginName as string, - }) - } - className="group flex flex-row items-center bg-background-light-400 dark:bg-background-dark-400 border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all" - > -
- -
- -
- - {session.factors?.user?.displayName} - - - {session.factors?.user?.loginName} - - {validPassword && ( - - {moment(new Date(validPassword)).fromNow()} - - )} -
- - -
- {validPassword ? ( -
- ) : ( -
- )} - - -
- + /> ); }) ) : ( diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index 142f5bf51f2..0d4da683874 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -1,8 +1,16 @@ -import { createSession, getSession, server, setSession } from "#/lib/zitadel"; +import { + createSession, + getSession, + server, + setSession, + deleteSession, +} from "#/lib/zitadel"; import { SessionCookie, addSessionToCookie, getMostRecentSessionCookie, + getSessionCookieById, + removeSessionFromCookie, updateSessionCookie, } from "#/utils/cookies"; import { NextRequest, NextResponse } from "next/server"; @@ -115,10 +123,43 @@ export async function PUT(request: NextRequest) { } }) .catch((error) => { - console.error("erasd", error); return NextResponse.json(error, { status: 500 }); }); } else { return NextResponse.error(); } } + +/** + * + * @param request id of the session to be deleted + */ +export async function DELETE(request: NextRequest) { + const { searchParams } = new URL(request.url); + const id = searchParams.get("id"); + if (id) { + const session = await getSessionCookieById(id); + + return deleteSession(server, session.id, session.token) + .then(() => { + return removeSessionFromCookie(session) + .then(() => { + return NextResponse.json({ factors: session.factors }); + }) + .catch((error) => { + return NextResponse.json( + { details: "could not set cookie" }, + { status: 500 } + ); + }); + }) + .catch((error) => { + return NextResponse.json( + { details: "could not delete session" }, + { status: 500 } + ); + }); + } else { + return NextResponse.error(); + } +} diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 4d514d1758e..b44b4793420 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -19,6 +19,7 @@ import { GetSessionResponse, VerifyEmailResponse, SetSessionResponse, + DeleteSessionResponse, } from "@zitadel/server"; export const zitadelConfig: ZitadelServerOptions = { @@ -103,6 +104,15 @@ export function getSession( return sessionService.getSession({ sessionId, sessionToken }, {}); } +export function deleteSession( + server: ZitadelServer, + sessionId: string, + sessionToken: string +): Promise { + const sessionService = session.getSession(server); + return sessionService.deleteSession({ sessionId, sessionToken }, {}); +} + export function listSessions( server: ZitadelServer, ids: string[] diff --git a/apps/login/ui/SessionItem.tsx b/apps/login/ui/SessionItem.tsx new file mode 100644 index 00000000000..f87713459b7 --- /dev/null +++ b/apps/login/ui/SessionItem.tsx @@ -0,0 +1,99 @@ +"use client"; +import { Session } from "@zitadel/server"; +import Link from "next/link"; +import { useState } from "react"; +import { Avatar } from "./Avatar"; +import moment from "moment"; +import { XCircleIcon } from "@heroicons/react/24/outline"; + +export default function SessionItem({ + session, + reload, +}: { + session: Session; + reload: () => void; +}) { + const [loading, setLoading] = useState(false); + + async function clearSession(id: string) { + setLoading(true); + const res = await fetch("/session?" + new URLSearchParams({ id }), { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: id, + }), + }); + + const response = await res.json(); + + if (!res.ok) { + setLoading(false); + // setError(response.details); + return Promise.reject(response); + } else { + setLoading(false); + return response; + } + } + + const validPassword = session?.factors?.password?.verifiedAt; + + return ( + +
+ +
+ +
+ {session.factors?.user?.displayName} + + {session.factors?.user?.loginName} + + {validPassword && ( + + {moment(new Date(validPassword)).fromNow()} + + )} +
+ + +
+ {validPassword ? ( +
+ ) : ( +
+ )} + + { + event.preventDefault(); + clearSession(session.id).then(() => { + reload(); + }); + }} + /> +
+ + ); +} diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 3158e27ab8e..78f00a9bfc1 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -92,6 +92,24 @@ export async function getMostRecentSessionCookie(): Promise { } } +export async function getSessionCookieById(id: string): Promise { + const cookiesList = cookies(); + const stringifiedCookie = cookiesList.get("sessions"); + + if (stringifiedCookie?.value) { + const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); + + const found = sessions.find((s) => s.id === id); + if (found) { + return found; + } else { + return Promise.reject(); + } + } else { + return Promise.reject(); + } +} + export async function getAllSessionIds(): Promise { const cookiesList = cookies(); const stringifiedCookie = cookiesList.get("sessions"); diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index bc8b26f2835..98fbdd84cb8 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -17,6 +17,7 @@ export { GetSessionResponse, CreateSessionResponse, SetSessionResponse, + DeleteSessionResponse, } from "./proto/server/zitadel/session/v2alpha/session_service"; export { GetPasswordComplexitySettingsResponse, From 1b28d50278495193d4926320841e99b5986b348b Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 6 Jun 2023 17:19:30 +0200 Subject: [PATCH 2/3] clear session from the list, once removed --- apps/login/app/(login)/accounts/page.tsx | 22 ++------------- apps/login/ui/SessionsList.tsx | 34 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 apps/login/ui/SessionsList.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index 645f6392ca8..b3b0631e162 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -1,10 +1,9 @@ import { Session } from "@zitadel/server"; import { listSessions, server } from "#/lib/zitadel"; -import Alert from "#/ui/Alert"; import { getAllSessionIds } from "#/utils/cookies"; import { UserPlusIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; -import SessionItem from "#/ui/SessionItem"; +import SessionsList from "#/ui/SessionsList"; async function loadSessions(): Promise { const ids = await getAllSessionIds(); @@ -30,24 +29,7 @@ export default async function Page() {

Use your ZITADEL Account

- {sessions ? ( - sessions - .filter((session) => session?.factors?.user?.loginName) - .map((session, index) => { - return ( - { - "use server"; - sessions = sessions.filter((s) => s.id !== session.id); - }} - key={"session-" + index} - /> - ); - }) - ) : ( - No Sessions available! - )} +
diff --git a/apps/login/ui/SessionsList.tsx b/apps/login/ui/SessionsList.tsx new file mode 100644 index 00000000000..faf497b4b57 --- /dev/null +++ b/apps/login/ui/SessionsList.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { Session } from "@zitadel/server"; +import SessionItem from "./SessionItem"; +import Alert from "./Alert"; +import { useEffect, useState } from "react"; + +type Props = { + sessions: Session[]; +}; + +export default function SessionsList({ sessions }: Props) { + const [list, setList] = useState(sessions); + + return sessions ? ( +
+ {list + .filter((session) => session?.factors?.user?.loginName) + .map((session, index) => { + return ( + { + setList(list.filter((s) => s.id !== session.id)); + }} + key={"session-" + index} + /> + ); + })} +
+ ) : ( + No Sessions available! + ); +} From b493753f36fdafe1b97a5cc8762f5f9a11e913b5 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 7 Jun 2023 16:19:15 +0200 Subject: [PATCH 3/3] Update apps/login/ui/SessionItem.tsx Co-authored-by: Elio Bischof --- apps/login/ui/SessionItem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/login/ui/SessionItem.tsx b/apps/login/ui/SessionItem.tsx index f87713459b7..d766ca0f139 100644 --- a/apps/login/ui/SessionItem.tsx +++ b/apps/login/ui/SessionItem.tsx @@ -29,12 +29,11 @@ export default function SessionItem({ const response = await res.json(); + setLoading(false); if (!res.ok) { - setLoading(false); // setError(response.details); return Promise.reject(response); } else { - setLoading(false); return response; } }