find sessions for loginHint, send to select account

This commit is contained in:
peintnermax
2023-08-29 14:43:51 +02:00
parent 8056c81161
commit 338c28cd88
6 changed files with 120 additions and 34 deletions

View File

@@ -20,7 +20,13 @@ async function loadSessions(): Promise<Session[]> {
} }
} }
export default async function Page() { export default async function Page({
searchParams,
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const authRequestId = searchParams?.authRequestId;
let sessions = await loadSessions(); let sessions = await loadSessions();
return ( return (
@@ -29,7 +35,7 @@ export default async function Page() {
<p className="ztdl-p mb-6 block">Use your ZITADEL Account</p> <p className="ztdl-p mb-6 block">Use your ZITADEL Account</p>
<div className="flex flex-col w-full space-y-2"> <div className="flex flex-col w-full space-y-2">
<SessionsList sessions={sessions} /> <SessionsList sessions={sessions} authRequestId={authRequestId} />
<Link href="/loginname"> <Link href="/loginname">
<div className="flex flex-row items-center py-3 px-4 hover:bg-black/10 dark:hover:bg-white/10 rounded-md transition-all"> <div className="flex flex-row items-center py-3 px-4 hover:bg-black/10 dark:hover:bg-white/10 rounded-md transition-all">
<div className="w-8 h-8 mr-4 flex flex-row justify-center items-center rounded-full bg-black/5 dark:bg-white/5"> <div className="w-8 h-8 mr-4 flex flex-row justify-center items-center rounded-full bg-black/5 dark:bg-white/5">

View File

@@ -1,21 +1,36 @@
import { getAuthRequest, listSessions, server } from "#/lib/zitadel"; import {
import { getAllSessionIds } from "#/utils/cookies"; createCallback,
import { Session } from "@zitadel/server"; getAuthRequest,
listSessions,
server,
} from "#/lib/zitadel";
import { SessionCookie, getAllSessions } from "#/utils/cookies";
import { Session, AuthRequest, Prompt } from "@zitadel/server";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
async function loadSessions(): Promise<Session[]> { async function loadSessions(ids: string[]): Promise<Session[]> {
const ids: string[] = await getAllSessionIds(); const response = await listSessions(
server,
ids.filter((id: string | undefined) => !!id)
);
return response?.sessions ?? [];
}
if (ids && ids.length) { function findSession(
const response = await listSessions( sessions: Session[],
server, authRequest: AuthRequest
ids.filter((id: string | undefined) => !!id) ): Session | undefined {
); if (authRequest.hintUserId) {
return response?.sessions ?? []; console.log(`find session for hintUserId: ${authRequest.hintUserId}`);
} else { return sessions.find((s) => s.factors?.user?.id === authRequest.hintUserId);
console.info("No session cookie found.");
return [];
} }
if (authRequest.loginHint) {
console.log(`find session for loginHint: ${authRequest.loginHint}`);
return sessions.find(
(s) => s.factors?.user?.loginName === authRequest.loginHint
);
}
return undefined;
} }
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
@@ -23,17 +38,67 @@ export async function GET(request: NextRequest) {
const authRequestId = searchParams.get("authRequest"); const authRequestId = searchParams.get("authRequest");
if (authRequestId) { if (authRequestId) {
const response = await getAuthRequest(server, { authRequestId }); const { authRequest } = await getAuthRequest(server, { authRequestId });
const sessions = await loadSessions(); const sessionCookies: SessionCookie[] = await getAllSessions();
if (sessions.length) { const ids = sessionCookies.map((s) => s.id);
return NextResponse.json(sessions);
let sessions: Session[] = [];
if (ids && ids.length) {
sessions = await loadSessions(ids);
} else {
console.info("No session cookie found.");
return [];
}
// use existing session and hydrate it for oidc
if (authRequest && sessions.length) {
// if some accounts are available for selection and select_account is set
if (authRequest && authRequest.prompt === Prompt.PROMPT_SELECT_ACCOUNT) {
const accountsUrl = new URL("/accounts", request.url);
if (authRequest?.id) {
accountsUrl.searchParams.set("authRequestId", authRequest?.id);
}
return NextResponse.redirect(accountsUrl);
} else {
// check for loginHint, userId hint sessions
let selectedSession = findSession(sessions, authRequest);
if (!selectedSession) {
selectedSession = sessions[0]; // TODO: remove
}
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(server, {
authRequestId,
session,
});
return NextResponse.redirect(callbackUrl);
} else {
const accountsUrl = new URL("/accounts", request.url);
if (authRequest?.id) {
accountsUrl.searchParams.set("authRequestId", authRequest?.id);
}
return NextResponse.redirect(accountsUrl);
}
} else {
return NextResponse.error();
}
}
} else { } else {
const loginNameUrl = new URL("/loginname", request.url); const loginNameUrl = new URL("/loginname", request.url);
if (response.authRequest?.id) { if (authRequest?.id) {
loginNameUrl.searchParams.set( loginNameUrl.searchParams.set("authRequestId", authRequest?.id);
"authRequestId",
response.authRequest?.id
);
} }
return NextResponse.redirect(loginNameUrl); return NextResponse.redirect(loginNameUrl);

View File

@@ -248,13 +248,11 @@ export async function getAuthRequest(
export async function createCallback( export async function createCallback(
server: ZitadelServer, server: ZitadelServer,
{ authRequestId }: CreateCallbackRequest req: CreateCallbackRequest
): Promise<CreateCallbackResponse> { ): Promise<CreateCallbackResponse> {
const oidcService = oidc.getOidc(server); const oidcService = oidc.getOidc(server);
return oidcService.createCallback({ return oidcService.createCallback(req);
authRequestId,
});
} }
export async function verifyEmail( export async function verifyEmail(

View File

@@ -9,9 +9,11 @@ import { XCircleIcon } from "@heroicons/react/24/outline";
export default function SessionItem({ export default function SessionItem({
session, session,
reload, reload,
authRequestId,
}: { }: {
session: Session; session: Session;
reload: () => void; reload: () => void;
authRequestId?: string;
}) { }) {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
@@ -52,10 +54,18 @@ export default function SessionItem({
loginName: session.factors?.user?.loginName as string, loginName: session.factors?.user?.loginName as string,
}) })
: `/loginname?` + : `/loginname?` +
new URLSearchParams({ new URLSearchParams(
loginName: session.factors?.user?.loginName as string, authRequestId
submit: "true", ? {
}) loginName: session.factors?.user?.loginName as string,
submit: "true",
authRequestId,
}
: {
loginName: session.factors?.user?.loginName as string,
submit: "true",
}
)
} }
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" 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"
> >

View File

@@ -7,9 +7,10 @@ import { useEffect, useState } from "react";
type Props = { type Props = {
sessions: Session[]; sessions: Session[];
authRequestId?: string;
}; };
export default function SessionsList({ sessions }: Props) { export default function SessionsList({ sessions, authRequestId }: Props) {
const [list, setList] = useState<Session[]>(sessions); const [list, setList] = useState<Session[]>(sessions);
return sessions ? ( return sessions ? (
<div className="flex flex-col space-y-2"> <div className="flex flex-col space-y-2">
@@ -19,6 +20,7 @@ export default function SessionsList({ sessions }: Props) {
return ( return (
<SessionItem <SessionItem
session={session} session={session}
authRequestId={authRequestId}
reload={() => { reload={() => {
setList(list.filter((s) => s.id !== session.id)); setList(list.filter((s) => s.id !== session.id));
}} }}

View File

@@ -32,6 +32,11 @@ export {
CreateCallbackResponse, CreateCallbackResponse,
} from "./proto/server/zitadel/oidc/v2alpha/oidc_service"; } from "./proto/server/zitadel/oidc/v2alpha/oidc_service";
export {
AuthRequest,
Prompt,
} from "./proto/server/zitadel/oidc/v2alpha/authorization";
export { export {
Session, Session,
Factors, Factors,