diff --git a/auth/app/api/auth/signedout/route.ts b/auth/app/api/auth/signedout/route.ts new file mode 100644 index 0000000..06db5b6 --- /dev/null +++ b/auth/app/api/auth/signedout/route.ts @@ -0,0 +1,11 @@ +import configuration from "@/configuration"; +import { returnUrlCookieName } from "@/lib/constant"; +import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; + +export async function POST() { + const requestCookie = cookies(); + const returnUrlCookie = requestCookie.get(returnUrlCookieName); + const redirectUrl = returnUrlCookie?.value || configuration.appUrl; + return NextResponse.redirect(redirectUrl); +} diff --git a/auth/app/api/auth/signin/route.ts b/auth/app/api/auth/signin/route.ts index db36646..b731cc8 100644 --- a/auth/app/api/auth/signin/route.ts +++ b/auth/app/api/auth/signin/route.ts @@ -32,6 +32,22 @@ export async function POST(request: NextRequest) { if (!csrfTokenCookie) throw new Error("csrfToken cookie not found"); if (csrfTokenCookie.value !== csrfToken) throw new Error("Invalid csrfToken"); + const wellKnownResponse = await fetch( + `${configuration.portal.issuer}/.well-known/openid-configuration` + ); + + const wellKnown = (await wellKnownResponse.json()) as { + issuer: string; + authorization_endpoint: string; + token_endpoint: string; + userinfo_endpoint: string; + end_session_endpoint: string; + }; + + if (wellKnownResponse.status !== 200) { + throw { code: wellKnownResponse.status, details: wellKnown }; + } + const codeVerifier = generateCodeVerifier(); const codeChallenge = generateCodeChallenge(codeVerifier); const state = generateState(); @@ -59,10 +75,9 @@ export async function POST(request: NextRequest) { if (prompt) params.prompt = prompt; if (loginHint) params.login_hint = loginHint; - const authorizeUrl = new URL( - `/oauth/v2/authorize?${new URLSearchParams(params).toString()}`, - configuration.portal.issuer - ).toString(); + const authorizeUrl = `${ + wellKnown.authorization_endpoint + }?${new URLSearchParams(params).toString()}`; if (returnUrl) setShortLiveCookie(returnUrlCookieName, returnUrl); setShortLiveCookie(stateCookieName, state); diff --git a/auth/app/api/auth/signout/route.ts b/auth/app/api/auth/signout/route.ts index 51ce5ef..cf790e9 100644 --- a/auth/app/api/auth/signout/route.ts +++ b/auth/app/api/auth/signout/route.ts @@ -1,5 +1,64 @@ -import { NextResponse } from "next/server"; +import configuration from "@/configuration"; +import { returnUrlCookieName } from "@/lib/constant"; +import { setShortLiveCookie } from "@/lib/cookie"; +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; -export async function GET() { - return NextResponse.json({}); +export async function POST(request: NextRequest) { + const body = (await request.json()) as { + returnUrl?: string; + idTokenHint?: string; + clientId?: string; + postLogoutRedirectUri?: string; + state?: string; + }; + const { returnUrl, idTokenHint, clientId, postLogoutRedirectUri, state } = + body; + + const wellKnownResponse = await fetch( + `${configuration.portal.issuer}/.well-known/openid-configuration` + ); + + const wellKnown = (await wellKnownResponse.json()) as { + issuer: string; + authorization_endpoint: string; + token_endpoint: string; + userinfo_endpoint: string; + end_session_endpoint: string; + }; + + if (wellKnownResponse.status !== 200) { + throw { code: wellKnownResponse.status, details: wellKnown }; + } + + const params: { + id_token_hint?: string; + client_id?: string; + post_logout_redirect_uri?: string; + state?: string; + } = { + id_token_hint: idTokenHint, + client_id: clientId, + post_logout_redirect_uri: postLogoutRedirectUri, + state: state, + }; + + if (idTokenHint) { + await prisma.session.updateMany({ + where: { + idToken: idTokenHint, + }, + data: { + deletedAt: new Date(), + }, + }); + } + + const endSessionUrl = `${ + wellKnown.end_session_endpoint + }?${new URLSearchParams(params).toString()}`; + + if (returnUrl) setShortLiveCookie(returnUrlCookieName, returnUrl); + + return NextResponse.json({ endSessionUrl }); } diff --git a/auth/app/auth/signedout/page.tsx b/auth/app/auth/signedout/page.tsx new file mode 100644 index 0000000..6fca2d9 --- /dev/null +++ b/auth/app/auth/signedout/page.tsx @@ -0,0 +1,12 @@ +import configuration from "@/configuration"; +import { returnUrlCookieName } from "@/lib/constant"; +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; + +export default async function Page({ searchParams }: { searchParams: {} }) { + console.log(`debug:searchParams`, searchParams); + const requestCookie = cookies(); + const returnUrlCookie = requestCookie.get(returnUrlCookieName); + const redirectUrl = returnUrlCookie?.value || configuration.appUrl; + return redirect(redirectUrl); +} diff --git a/auth/app/auth/signout/page.tsx b/auth/app/auth/signout/page.tsx index acedc1a..a34eb76 100644 --- a/auth/app/auth/signout/page.tsx +++ b/auth/app/auth/signout/page.tsx @@ -5,10 +5,19 @@ export default async function Page({ }: { searchParams: { return_url?: string; - prompt?: string; - scope?: string; - login_hint?: string; + id_token_hint?: string; + client_id?: string; + post_logoutRedirect_uri?: string; + state?: string; }; }) { - return ; + return ( + + ); } diff --git a/auth/ui/SignIn.tsx b/auth/ui/SignIn.tsx index 681d113..3ee5052 100644 --- a/auth/ui/SignIn.tsx +++ b/auth/ui/SignIn.tsx @@ -8,8 +8,8 @@ export default function SignIn(props: { scope?: string; loginHint?: string; }) { - const { returnUrl, prompt, scope, loginHint } = props; const router = useRouter(); + const { returnUrl, prompt, scope, loginHint } = props; useEffect(() => { fetch(`https://auth.example.local/api/auth/csrf`, { diff --git a/auth/ui/SignOut.tsx b/auth/ui/SignOut.tsx index d8263a3..fca97c1 100644 --- a/auth/ui/SignOut.tsx +++ b/auth/ui/SignOut.tsx @@ -2,19 +2,33 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; -export default function SignOut(props: { returnUrl?: string }) { - const { returnUrl } = props; +export default function SignOut(props: { + returnUrl?: string; + idTokenHint?: string; + clientId?: string; + postLogoutRedirectUri?: string; + state?: string; +}) { + const { returnUrl, idTokenHint, clientId, postLogoutRedirectUri, state } = + props; const router = useRouter(); useEffect(() => { fetch(`https://auth.example.local/api/auth/signout`, { - method: "GET", + method: "POST", + body: JSON.stringify({ + returnUrl, + idTokenHint, + clientId, + postLogoutRedirectUri, + state, + }), }) .then((response) => response.json()) - .then(async (data) => { - console.log(`debug:data`, data); + .then(({ endSessionUrl }) => { + if (endSessionUrl) router.replace(endSessionUrl); }); - }, [returnUrl, router]); + }, [clientId, idTokenHint, postLogoutRedirectUri, returnUrl, router, state]); return
Loading...
; }