fix: refactor session handling
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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`,
|
||||||
|
|||||||
@@ -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}`)}`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user