From 8ebc48b1389c450f44fab72aede6216eb814e26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20J=C3=A4derberg?= Date: Mon, 22 Sep 2025 09:30:36 +0000 Subject: [PATCH] Merged in feat/SW-3461-setup-auth-with-sas-eurobonus (pull request #2825) Feat/SW-3461 setup auth with sas eurobonus * feat(SW-3461): Setup auth for sas eurobonus * . * feat: setup auth towards SAS * Fix auth via SAS and add logout route * . * merge * auth via SAS * fix powered by scandic logo * Merge branch 'master' of bitbucket.org:scandic-swap/web into feat/SW-3461-setup-auth-with-sas-eurobonus * Include access_token in jwt after successful login * merge Approved-by: Anton Gunnarsson --- .../app/[lang]/(auth)/login/route.ts | 100 ++++++++++++++ .../app/[lang]/(auth)/logout/route.ts | 17 +++ apps/partner-sas/app/[lang]/layout.tsx | 71 +++------- .../app/api/web/auth/[...nextauth]/route.ts | 11 ++ apps/partner-sas/auth.ts | 127 ++++++++++++++++++ .../components/BookingFlowProviders.tsx | 45 +++++++ apps/partner-sas/components/Header/Header.tsx | 35 +++++ .../components/Header/header.module.css | 1 + .../poweredByScandic.module.css | 1 - apps/partner-sas/env/server.ts | 10 ++ apps/partner-sas/hooks/useIsUserLoggedIn.ts | 32 +++++ apps/partner-sas/next.config.ts | 2 +- apps/partner-sas/package.json | 2 + apps/partner-sas/server/errors/next.ts | 57 ++++++++ apps/partner-sas/turbo.json | 8 +- apps/partner-sas/{app => }/utils/tracking.ts | 0 apps/scandic-web/middlewares/cmsContent.ts | 10 +- apps/scandic-web/next.config.ts | 1 + apps/scandic-web/package.json | 4 +- apps/scandic-web/turbo.json | 8 +- apps/scandic-web/utils/rewards.ts | 2 +- packages/common/constants/loginType.ts | 1 + packages/trpc/package.json | 2 +- yarn.lock | 23 ++-- 24 files changed, 494 insertions(+), 76 deletions(-) create mode 100644 apps/partner-sas/app/[lang]/(auth)/login/route.ts create mode 100644 apps/partner-sas/app/[lang]/(auth)/logout/route.ts create mode 100644 apps/partner-sas/app/api/web/auth/[...nextauth]/route.ts create mode 100644 apps/partner-sas/auth.ts create mode 100644 apps/partner-sas/components/BookingFlowProviders.tsx create mode 100644 apps/partner-sas/hooks/useIsUserLoggedIn.ts create mode 100644 apps/partner-sas/server/errors/next.ts rename apps/partner-sas/{app => }/utils/tracking.ts (100%) diff --git a/apps/partner-sas/app/[lang]/(auth)/login/route.ts b/apps/partner-sas/app/[lang]/(auth)/login/route.ts new file mode 100644 index 000000000..c90b1a92a --- /dev/null +++ b/apps/partner-sas/app/[lang]/(auth)/login/route.ts @@ -0,0 +1,100 @@ +import { type NextRequest, NextResponse } from "next/server" +import { AuthError } from "next-auth" + +import { logger } from "@scandic-hotels/common/logger" + +import { internalServerError } from "@/server/errors/next" +import { getPublicURL } from "@/server/utils" + +import { signIn } from "@/auth" + +import type { Lang } from "@scandic-hotels/common/constants/language" + +export async function GET( + request: NextRequest, + context: { params: Promise<{ lang: Lang }> } +) { + const contextParams = await context.params + const publicURL = getPublicURL(request) + + let redirectHeaders: Headers | undefined = undefined + let redirectTo: string + + const redirectToCookieValue = request.cookies.get("redirectTo")?.value // Cookie gets set by authRequired middleware + const redirectToSearchParamValue = + request.nextUrl.searchParams.get("redirectTo") + const redirectToFallback = "/" + + logger.debug(`[login] redirectTo cookie value: ${redirectToCookieValue}`) + logger.debug( + `[login] redirectTo search param value: ${redirectToSearchParamValue}` + ) + + redirectTo = + redirectToCookieValue || redirectToSearchParamValue || redirectToFallback + + // Make relative URL to absolute URL + if (redirectTo.startsWith("/")) { + logger.debug(`[login] make redirectTo absolute, from ${redirectTo}`) + redirectTo = new URL(redirectTo, publicURL).href + logger.debug(`[login] make redirectTo absolute, to ${redirectTo}`) + } + + // 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" + ) + + const SAS_LANGUAGE_MAP: Record = { + no: "nb", + sv: "sv", + fi: "fi", + da: "da", + en: "en", + de: "de", + } + + try { + logger.debug(`[login] final redirectUrl: ${redirectTo}`) + + /** Record is next-auth typings */ + const params = { + ui_locales: SAS_LANGUAGE_MAP[contextParams.lang], + scope: ["openid", "profile", "email"].join(" "), + } satisfies Record + + /** + * Passing `redirect: false` to `signIn` will return the URL instead of + * automatically redirecting to it inside of `signIn`. + * https://github.com/nextauthjs/next-auth/blob/3c035ec/packages/next-auth/src/lib/actions.ts#L76 + */ + const redirectUrl = await signIn( + "sas", + { + redirectTo, + redirect: false, + }, + params + ) + + if (redirectUrl) { + const redirectOpts = { + headers: redirectHeaders, + } + logger.debug(`[login] redirecting to: ${redirectUrl}`, redirectOpts) + return NextResponse.redirect(redirectUrl, redirectOpts) + } else { + logger.error(`[login] missing redirectUrl reponse from signIn()`) + } + } catch (error) { + if (error instanceof AuthError) { + logger.error("signInAuthError", { signInAuthError: error }) + } else { + logger.error("signInError", { signInError: error }) + } + } + + return internalServerError() +} diff --git a/apps/partner-sas/app/[lang]/(auth)/logout/route.ts b/apps/partner-sas/app/[lang]/(auth)/logout/route.ts new file mode 100644 index 000000000..a4984c591 --- /dev/null +++ b/apps/partner-sas/app/[lang]/(auth)/logout/route.ts @@ -0,0 +1,17 @@ +import { type NextRequest } from "next/server" + +import { getPublicURL } from "@/server/utils" + +import { signOut } from "@/auth" + +import type { Lang } from "@scandic-hotels/common/constants/language" + +export async function GET( + request: NextRequest, + _context: { params: Promise<{ lang: Lang }> } +) { + const publicURL = getPublicURL(request) + + const redirectTo: string = publicURL + await signOut({ redirectTo, redirect: true }) +} diff --git a/apps/partner-sas/app/[lang]/layout.tsx b/apps/partner-sas/app/[lang]/layout.tsx index 47578223b..4f76be8e5 100644 --- a/apps/partner-sas/app/[lang]/layout.tsx +++ b/apps/partner-sas/app/[lang]/layout.tsx @@ -6,10 +6,9 @@ import "../../globals.css" import { ReactQueryDevtools } from "@tanstack/react-query-devtools" import Script from "next/script" +import { SessionProvider } from "next-auth/react" import { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlowConfig" -import { BookingFlowContextProvider } from "@scandic-hotels/booking-flow/BookingFlowContextProvider" -import { BookingFlowTrackingProvider } from "@scandic-hotels/booking-flow/BookingFlowTrackingProvider" import { NuqsAdapter } from "@scandic-hotels/booking-flow/utils/nuqs" import { Lang } from "@scandic-hotels/common/constants/language" import { ToastHandler } from "@scandic-hotels/design-system/ToastHandler" @@ -25,20 +24,9 @@ import { getMessages } from "@/i18n" import ClientIntlProvider from "@/i18n/Provider" import { setLang } from "@/i18n/serverContext" +import { BookingFlowProviders } from "../../components/BookingFlowProviders" import { Footer } from "../../components/Footer/Footer" import { Header } from "../../components/Header/Header" -import { - trackAccordionItemOpen, - trackBedSelection, - trackBookingSearchClick, - trackBreakfastSelection, - trackGenericEvent, - trackGlaSaveCardAttempt, - trackLoginClick, - trackOpenSidePeek, - trackPaymentEvent, - trackUpdatePaymentMethod, -} from "../utils/tracking" import type { Metadata } from "next" @@ -82,35 +70,18 @@ export default async function RootLayout(props: RootLayoutProps) {
- - - - - - - + + + + {/* TODO handle onError */} + + + +
{props.bookingwidget} @@ -119,13 +90,13 @@ export default async function RootLayout(props: RootLayoutProps) { - - - - - - - + + + + + + +