fix: refactor session handling

This commit is contained in:
Christel Westerberg
2024-05-28 14:41:05 +02:00
parent cd3c5491ec
commit 07f81c34e3
7 changed files with 34 additions and 36 deletions

View File

@@ -1,6 +1,5 @@
.container { .container {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;

View File

@@ -26,7 +26,7 @@
width: 3px; width: 3px;
height: 9px; height: 9px;
border-radius: 20%; border-radius: 20%;
background: var(--Brand-Main-Strong); background: var(--Scandic-Brand-Burgundy);
} }
.spinner div:nth-child(1) { .spinner div:nth-child(1) {

View File

@@ -1,13 +1,3 @@
/**
* @file Due to these records being used in next.config.js, and that is required
* to be a js file, we use jsdoc to type these.
*/
/**
* These are routes that define code entries for My pages
*/
/** @type {import('@/types/routes').LangRoute} */
const myPages = { const myPages = {
da: "/da/webview/mine-sider", da: "/da/webview/mine-sider",
de: "/de/webview/mein-profil", de: "/de/webview/mein-profil",
@@ -17,7 +7,6 @@ const myPages = {
sv: "/sv/webview/mina-sidor", sv: "/sv/webview/mina-sidor",
} }
/** @type {import('@/types/routes').LangRoute} */
export const overview = { export const overview = {
da: `${myPages.da}/oversigt`, da: `${myPages.da}/oversigt`,
de: `${myPages.de}/uberblick`, de: `${myPages.de}/uberblick`,
@@ -27,7 +16,6 @@ export const overview = {
sv: `${myPages.sv}/oversikt`, sv: `${myPages.sv}/oversikt`,
} }
/** @type {import('@/types/routes').LangRoute} */
export const benefits = { export const benefits = {
da: `${myPages.da}/fordele`, da: `${myPages.da}/fordele`,
de: `${myPages.de}/vorteile`, de: `${myPages.de}/vorteile`,
@@ -37,7 +25,6 @@ export const benefits = {
sv: `${myPages.sv}/formaner`, sv: `${myPages.sv}/formaner`,
} }
/** @type {import('@/types/routes').LangRoute} */
export const points = { export const points = {
da: `${myPages.da}/point`, da: `${myPages.da}/point`,
de: `${myPages.de}/punkte`, de: `${myPages.de}/punkte`,
@@ -47,7 +34,6 @@ export const points = {
sv: `${myPages.sv}/poang`, sv: `${myPages.sv}/poang`,
} }
/** @type {import('@/types/routes').LangRoute} */
export const programOverview = { export const programOverview = {
da: `/da/webview/about-scandic-friends`, da: `/da/webview/about-scandic-friends`,
de: `/de/webview/about-scandic-friends`, de: `/de/webview/about-scandic-friends`,
@@ -57,7 +43,6 @@ export const programOverview = {
sv: `/sv/webview/om-scandic-friends`, sv: `/sv/webview/om-scandic-friends`,
} }
/** @type {import('@/types/routes').LangRoute} */
const refreshUrl = { const refreshUrl = {
da: `/da/webview/refresh`, da: `/da/webview/refresh`,
de: `/de/webview/refresh`, de: `/de/webview/refresh`,

View File

@@ -3,6 +3,7 @@ import { redirect } from "next/navigation"
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import { Lang } from "@/constants/languages" import { Lang } from "@/constants/languages"
import { webviews } from "@/constants/routes/webviews"
import { env } from "@/env/server" import { env } from "@/env/server"
import { appRouter } from "@/server" import { appRouter } from "@/server"
import { createContext } from "@/server/context" import { createContext } from "@/server/context"
@@ -24,8 +25,12 @@ export function serverClient() {
if (error instanceof TRPCError) { if (error instanceof TRPCError) {
if (error.code === "UNAUTHORIZED") { if (error.code === "UNAUTHORIZED") {
const lang = ctx?.lang || Lang.en const lang = ctx?.lang || Lang.en
if (ctx?.webToken) {
const returnUrl = ctx.url const langIndex = ctx!.url.indexOf(`/${lang}`)
const pathname = ctx?.url.substring(langIndex)
if (pathname && webviews.includes(pathname)) {
const returnUrl = ctx!.url
const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(returnUrl)}` const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(returnUrl)}`
console.error( console.error(
@@ -36,8 +41,6 @@ export function serverClient() {
redirect(redirectUrl) redirect(redirectUrl)
} }
const pathname = ctx?.pathname || "/"
redirect( redirect(
`/${lang}/login?redirectTo=${encodeURIComponent(`/${lang}/${pathname}`)}` `/${lang}/login?redirectTo=${encodeURIComponent(`/${lang}/${pathname}`)}`
) )

View File

@@ -77,10 +77,10 @@ export const middleware: NextMiddleware = async (request) => {
try { try {
// Authorization header is required for webviews // Authorization header is required for webviews
// It should be base64 encoded // It should be base64 encoded
const authorization = request.headers.get("Authorization")! const authorization = request.headers.get("X-Authorization")!
if (!authorization) { if (!authorization) {
console.error("Authorization header is missing") console.error("Authorization header is missing")
return badRequest() return badRequest("Authorization header is missing")
} }
// Initialization vector header is required for webviews // Initialization vector header is required for webviews
@@ -88,7 +88,7 @@ export const middleware: NextMiddleware = async (request) => {
const initializationVector = request.headers.get("X-AES-IV")! const initializationVector = request.headers.get("X-AES-IV")!
if (!initializationVector) { if (!initializationVector) {
console.error("initializationVector header is missing") console.error("initializationVector header is missing")
return badRequest() return badRequest("initializationVector header is missing")
} }
const decryptedData = await decryptData( const decryptedData = await decryptData(
@@ -97,16 +97,15 @@ export const middleware: NextMiddleware = async (request) => {
authorization authorization
) )
headers.set( headers.append("Cookie", `webviewToken=${decryptedData}`)
"Set-Cookie",
`webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`
)
headers.set("Cookie", `webviewToken=${decryptedData}`)
if (myPagesWebviews.includes(nextUrl.pathname)) { if (myPagesWebviews.includes(nextUrl.pathname)) {
return NextResponse.rewrite( return NextResponse.rewrite(
new URL(`/${lang}/webview/account-page/${uid}`, nextUrl), new URL(`/${lang}/webview/account-page/${uid}`, nextUrl),
{ {
headers: {
"Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
},
request: { request: {
headers, headers,
}, },
@@ -116,6 +115,9 @@ export const middleware: NextMiddleware = async (request) => {
return NextResponse.rewrite( return NextResponse.rewrite(
new URL(`/${lang}/webview/loyalty-page/${uid}`, nextUrl), new URL(`/${lang}/webview/loyalty-page/${uid}`, nextUrl),
{ {
headers: {
"Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
},
request: { request: {
headers, headers,
}, },

View File

@@ -1,11 +1,16 @@
import { cookies, headers } from "next/headers" import { cookies, headers } from "next/headers"
import { type Session } from "next-auth"
import { Lang } from "@/constants/languages" import { Lang } from "@/constants/languages"
import { auth } from "@/auth" import { auth } from "@/auth"
import { unauthorizedError } from "./errors/trpc"
typeof auth
type CreateContextOptions = { type CreateContextOptions = {
auth: typeof auth auth: () => Promise<Session>
lang: Lang lang: Lang
pathname: string pathname: string
uid?: string | null uid?: string | null
@@ -39,7 +44,15 @@ export function createContext() {
const webviewTokenCookie = cookie.get("webviewToken") const webviewTokenCookie = cookie.get("webviewToken")
return createContextInner({ return createContextInner({
auth, auth: async () => {
const session = await auth()
const webToken = webviewTokenCookie?.value
if (!session?.token && !webToken) {
throw unauthorizedError()
}
return session || ({ token: { access_token: webToken } } as Session)
},
lang: h.get("x-lang") as Lang, lang: h.get("x-lang") as Lang,
pathname: h.get("x-pathname")!, pathname: h.get("x-pathname")!,
uid: h.get("x-uid"), uid: h.get("x-uid"),

View File

@@ -41,13 +41,9 @@ export const protectedProcedure = t.procedure.use(async function (opts) {
throw sessionExpiredError() throw sessionExpiredError()
} }
if (!session?.token.access_token && !opts.ctx.webToken) {
throw unauthorizedError()
}
return opts.next({ return opts.next({
ctx: { ctx: {
session: session || { token: { access_token: opts.ctx.webToken } }, session,
}, },
}) })
}) })