diff --git a/.env.local.example b/.env.local.example index c1e013b50..f24f3b573 100644 --- a/.env.local.example +++ b/.env.local.example @@ -25,4 +25,10 @@ SEAMLESS_LOGIN_EN="http://www.example.com/updatelogin" SEAMLESS_LOGIN_FI="http://www.example.fi/updatelogin" SEAMLESS_LOGIN_NO="http://www.example.no/updatelogin" SEAMLESS_LOGIN_SV="http://www.example.se/updatelogin" -WEBVIEW_ENCRYPTION_KEY="MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" +SEAMLESS_LOGOUT_DA="http://www.example.dk/updatelogout?newweb" +SEAMLESS_LOGOUT_DE="http://www.example.de/updatelogout?newweb" +SEAMLESS_LOGOUT_EN="http://www.example.com/updatelogout?newweb" +SEAMLESS_LOGOUT_FI="http://www.example.fi/updatelogout?newweb" +SEAMLESS_LOGOUT_NO="http://www.example.no/updatelogout?newweb" +SEAMLESS_LOGOUT_SV="http://www.example.sv/updatelogout?newweb" +WEBVIEW_ENCRYPTION_KEY="MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" \ No newline at end of file diff --git a/app/[lang]/(live)/(protected)/logout/route.ts b/app/[lang]/(live)/(protected)/logout/route.ts index 336613db0..23ae2ffc0 100644 --- a/app/[lang]/(live)/(protected)/logout/route.ts +++ b/app/[lang]/(live)/(protected)/logout/route.ts @@ -1,32 +1,110 @@ +import { createActionURL } from "@auth/core" +import { headers as nextHeaders } from "next/headers" import { NextRequest, NextResponse } from "next/server" import { AuthError } from "next-auth" -import { badRequest } from "@/server/errors/next" +import { Lang } from "@/constants/languages" +import { env } from "@/env/server" +import { internalServerError } from "@/server/errors/next" import { signOut } from "@/auth" -export async function GET(request: NextRequest) { +export async function GET( + request: NextRequest, + context: { params: { lang: Lang } } +) { + let redirectHeaders: Headers | undefined = undefined + let redirectTo: string + const returnUrl = request.headers.get("x-returnurl") - // If all else fails, always redirect to startpage - const redirectTo = - returnUrl || - request.nextUrl.searchParams.get("redirectTo") || - "/" + if (returnUrl) { + redirectTo = returnUrl + } else { + // Normal logout request from New web + redirectTo = + request.cookies.get("redirectTo")?.value || // Cookie gets set by authRequired middleware + request.nextUrl.searchParams.get("redirectTo") || + "/" + // Make relative URL to absolute URL + if (redirectTo.startsWith("/")) { + if (!env.PUBLIC_URL) { + throw internalServerError("No value for env.PUBLIC_URL") + } + redirectTo = new URL(redirectTo, env.PUBLIC_URL).href + } + // Clean up cookie from authRequired middleware + redirectHeaders = new Headers() + redirectHeaders.append( + "set-cookie", + "redirectTo=; Expires=Thu, 01 Jan 1970 00:00:00 UTC; Path=/; HttpOnly; SameSite=Lax" + ) + + try { + // Initiate the seamless logout flow + let redirectUrlValue + switch (context.params.lang) { + case Lang.da: + redirectUrlValue = env.SEAMLESS_LOGOUT_DA + break + case Lang.de: + redirectUrlValue = env.SEAMLESS_LOGOUT_DE + break + case Lang.en: + redirectUrlValue = env.SEAMLESS_LOGOUT_EN + break + case Lang.fi: + redirectUrlValue = env.SEAMLESS_LOGOUT_FI + break + case Lang.no: + redirectUrlValue = env.SEAMLESS_LOGOUT_NO + break + case Lang.sv: + redirectUrlValue = env.SEAMLESS_LOGOUT_SV + break + } + const redirectUrl = new URL(redirectUrlValue) + redirectUrl.searchParams.set("returnurl", redirectTo) + redirectTo = redirectUrl.toString() + } catch (e) { + console.error( + "Unable to create URL for seamless logout, proceeding without it." + ) + console.error(e) + } + } try { /** * Passing `redirect: false` to `signOut` will return a result object * instead of automatically redirecting inside of `signOut`. * https://github.com/nextauthjs/next-auth/blob/3c035ec/packages/next-auth/src/lib/actions.ts#L104 */ - const obj = await signOut({ + console.log({ logout_NEXTAUTH_URL: process.env.NEXTAUTH_URL }) + console.log({ logout_env: process.env }) + + console.log({ logout_redirectTo: redirectTo }) + + const headers = new Headers(nextHeaders()) + const signOutURL = createActionURL( + "signout", + // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default + headers.get("x-forwarded-proto"), + headers, + process.env + ) + + console.log({ logout_signOutURL: signOutURL }) + + const redirectUrlObj = await signOut({ redirectTo, redirect: false, }) - if (obj) { - return NextResponse.redirect(obj.redirect) + if (redirectUrlObj) { + return NextResponse.redirect(redirectUrlObj.redirect, { + headers: redirectHeaders, + }) } } catch (error) { if (error instanceof AuthError) { @@ -36,5 +114,5 @@ export async function GET(request: NextRequest) { } } - return badRequest() + return internalServerError() } diff --git a/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx b/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx index 3c1ce9f98..d8a292e62 100644 --- a/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx @@ -20,7 +20,7 @@ export default async function MyPages({
- + {accountPage.content.length ? ( diff --git a/app/[lang]/(live)/(public)/login/route.ts b/app/[lang]/(live)/(public)/login/route.ts index 006c8af9e..5cbc22d9b 100644 --- a/app/[lang]/(live)/(public)/login/route.ts +++ b/app/[lang]/(live)/(public)/login/route.ts @@ -15,6 +15,7 @@ export async function GET( let redirectTo: string const returnUrl = request.headers.get("x-returnurl") + if (returnUrl) { // Seamless login request from Current web redirectTo = returnUrl diff --git a/auth.ts b/auth.ts index 59e85c82d..778824aea 100644 --- a/auth.ts +++ b/auth.ts @@ -152,6 +152,11 @@ export const config = { // console.log(arguments) // console.log("#### END - SIGNIN EVENT ARGS ######") // }, + // async signOut() { + // console.log("#### SIGNOUT EVENT ARGS ######") + // console.log(arguments) + // console.log("#### END - SIGNOUT EVENT ARGS ######") + // }, // async session() { // console.log("#### SESSION EVENT ARGS ######") // console.log(arguments) diff --git a/components/MyPages/Sidebar/index.tsx b/components/MyPages/Sidebar/index.tsx index 4bcae43b8..ebb5047b5 100644 --- a/components/MyPages/Sidebar/index.tsx +++ b/components/MyPages/Sidebar/index.tsx @@ -1,6 +1,7 @@ import { Fragment } from "react" import { LogOut } from "react-feather" +import { logout } from "@/constants/routes/handleAuth" import { serverClient } from "@/lib/trpc/server" import Link from "@/components/TempDesignSystem/Link" @@ -8,7 +9,9 @@ import Title from "@/components/Title" import styles from "./sidebar.module.css" -export default async function Sidebar() { +import { LangParams } from "@/types/params" + +export default async function Sidebar({ lang }: LangParams) { const navigation = await serverClient().contentstack.myPages.navigation.get() return ( @@ -41,7 +44,7 @@ export default async function Sidebar() { ))} - + Log out diff --git a/env/server.ts b/env/server.ts index e3a7e1ea8..7a01807d8 100644 --- a/env/server.ts +++ b/env/server.ts @@ -48,6 +48,12 @@ export const env = createEnv({ SEAMLESS_LOGIN_FI: z.string(), SEAMLESS_LOGIN_NO: z.string(), SEAMLESS_LOGIN_SV: z.string(), + SEAMLESS_LOGOUT_DA: z.string(), + SEAMLESS_LOGOUT_DE: z.string(), + SEAMLESS_LOGOUT_EN: z.string(), + SEAMLESS_LOGOUT_FI: z.string(), + SEAMLESS_LOGOUT_NO: z.string(), + SEAMLESS_LOGOUT_SV: z.string(), WEBVIEW_ENCRYPTION_KEY: z.string(), }, emptyStringAsUndefined: true, @@ -82,6 +88,12 @@ export const env = createEnv({ SEAMLESS_LOGIN_FI: process.env.SEAMLESS_LOGIN_FI, SEAMLESS_LOGIN_NO: process.env.SEAMLESS_LOGIN_NO, SEAMLESS_LOGIN_SV: process.env.SEAMLESS_LOGIN_SV, + SEAMLESS_LOGOUT_DA: process.env.SEAMLESS_LOGOUT_DA, + SEAMLESS_LOGOUT_DE: process.env.SEAMLESS_LOGOUT_DE, + SEAMLESS_LOGOUT_EN: process.env.SEAMLESS_LOGOUT_EN, + SEAMLESS_LOGOUT_FI: process.env.SEAMLESS_LOGOUT_FI, + SEAMLESS_LOGOUT_NO: process.env.SEAMLESS_LOGOUT_NO, + SEAMLESS_LOGOUT_SV: process.env.SEAMLESS_LOGOUT_SV, WEBVIEW_ENCRYPTION_KEY: process.env.WEBVIEW_ENCRYPTION_KEY, }, }) diff --git a/middleware.ts b/middleware.ts index b21ef3a30..05299b8f5 100644 --- a/middleware.ts +++ b/middleware.ts @@ -4,6 +4,7 @@ import { findLang } from "./constants/languages" import * as authRequired from "./middlewares/authRequired" import * as cmsContent from "./middlewares/cmsContent" import * as currentWebLogin from "./middlewares/currentWebLogin" +import * as currentWebLogout from "./middlewares/currentWebLogout" import * as handleAuth from "./middlewares/handleAuth" import * as myPages from "./middlewares/myPages" import * as webView from "./middlewares/webView" @@ -26,6 +27,7 @@ export const middleware: NextMiddleware = async (request, event) => { const middlewares = [ currentWebLogin, + currentWebLogout, authRequired, handleAuth, myPages, diff --git a/middlewares/currentWebLogout.ts b/middlewares/currentWebLogout.ts new file mode 100644 index 000000000..8dacd770a --- /dev/null +++ b/middlewares/currentWebLogout.ts @@ -0,0 +1,22 @@ +import { NextResponse } from "next/server" + +import { findLang } from "@/constants/languages" +import { badRequest } from "@/server/errors/next" + +import type { NextMiddleware } from "next/server" + +import type { MiddlewareMatcher } from "@/types/middleware" + +export const middleware: NextMiddleware = (request) => { + const currentwebUrl = request.nextUrl.searchParams.get("currentweb") + if (currentwebUrl == null || undefined) { + return badRequest() + } + const lang = findLang(request.nextUrl.pathname)! + + return NextResponse.rewrite(new URL(`/${lang}/logout`, request.nextUrl)) +} + +export const matcher: MiddlewareMatcher = (request) => { + return request.nextUrl.pathname.endsWith("/updatelogout") +} diff --git a/middlewares/webView.ts b/middlewares/webView.ts index 99dcbec28..7b6fadee6 100644 --- a/middlewares/webView.ts +++ b/middlewares/webView.ts @@ -1,8 +1,9 @@ -import { NextResponse, type NextMiddleware } from "next/server" +import { type NextMiddleware, NextResponse } from "next/server" import { findLang } from "@/constants/languages" import { env } from "@/env/server" import { badRequest, internalServerError } from "@/server/errors/next" + import { decryptData } from "@/utils/aes" import type { MiddlewareMatcher } from "@/types/middleware"