fix: add headers correctly
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { notFound } from "next/navigation"
|
|
||||||
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import BackButton from "@/components/BackButton"
|
||||||
import { Blocks } from "@/components/Loyalty/Blocks/WebView"
|
import { Blocks } from "@/components/Loyalty/Blocks/WebView"
|
||||||
import Sidebar from "@/components/Loyalty/Sidebar"
|
import Sidebar from "@/components/Loyalty/Sidebar"
|
||||||
import MaxWidth from "@/components/MaxWidth"
|
import MaxWidth from "@/components/MaxWidth"
|
||||||
@@ -19,6 +18,7 @@ export default async function AboutScandicFriends({
|
|||||||
{loyaltyPage.sidebar ? <Sidebar blocks={loyaltyPage.sidebar} /> : null}
|
{loyaltyPage.sidebar ? <Sidebar blocks={loyaltyPage.sidebar} /> : null}
|
||||||
|
|
||||||
<MaxWidth className={styles.blocks} tag="main">
|
<MaxWidth className={styles.blocks} tag="main">
|
||||||
|
<BackButton />
|
||||||
<Blocks blocks={loyaltyPage.blocks} lang={params.lang} />
|
<Blocks blocks={loyaltyPage.blocks} lang={params.lang} />
|
||||||
</MaxWidth>
|
</MaxWidth>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import "@/app/globals.css"
|
import "@/app/globals.css"
|
||||||
import "@scandic-hotels/design-system/style.css"
|
import "@scandic-hotels/design-system/style.css"
|
||||||
|
|
||||||
import { notFound } from "next/navigation"
|
import { overview } from "@/constants/routes/myPages"
|
||||||
|
|
||||||
import { _ } from "@/lib/translation"
|
import { _ } from "@/lib/translation"
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import BackButton from "@/components/BackButton"
|
||||||
import MaxWidth from "@/components/MaxWidth"
|
import MaxWidth from "@/components/MaxWidth"
|
||||||
import Content from "@/components/MyPages/AccountPage/Webview/Content"
|
import Content from "@/components/MyPages/AccountPage/Webview/Content"
|
||||||
|
|
||||||
@@ -16,8 +16,11 @@ import { LangParams, PageArgs } from "@/types/params"
|
|||||||
export default async function MyPages({ params }: PageArgs<LangParams>) {
|
export default async function MyPages({ params }: PageArgs<LangParams>) {
|
||||||
const accountPage = await serverClient().contentstack.accountPage.get()
|
const accountPage = await serverClient().contentstack.accountPage.get()
|
||||||
|
|
||||||
|
const isNotOverviewPage = accountPage.url !== overview[params.lang]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MaxWidth className={styles.blocks} tag="main">
|
<MaxWidth className={styles.blocks} tag="main">
|
||||||
|
{isNotOverviewPage ? <BackButton /> : null}
|
||||||
<Content lang={params.lang} content={accountPage.content} />
|
<Content lang={params.lang} content={accountPage.content} />
|
||||||
</MaxWidth>
|
</MaxWidth>
|
||||||
)
|
)
|
||||||
|
|||||||
15
components/BackButton/index.tsx
Normal file
15
components/BackButton/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
|
|
||||||
|
import Button from "../TempDesignSystem/Button"
|
||||||
|
|
||||||
|
export default function BackButton() {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Button onClick={goBack}>Go Back!</Button>
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent"
|
|||||||
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
||||||
import { modWebviewLink } from "@/utils/webviews"
|
import { modWebviewLink } from "@/utils/webviews"
|
||||||
|
|
||||||
import CardGrid from "../CardGrid"
|
import CardsGrid from "../CardsGrid"
|
||||||
|
|
||||||
import type { BlocksProps } from "@/types/components/loyalty/blocks"
|
import type { BlocksProps } from "@/types/components/loyalty/blocks"
|
||||||
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
||||||
@@ -12,20 +12,8 @@ import { LangParams } from "@/types/params"
|
|||||||
export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
|
export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
|
||||||
return blocks.map((block) => {
|
return blocks.map((block) => {
|
||||||
switch (block.__typename) {
|
switch (block.__typename) {
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
||||||
const cardGrid = {
|
return <CardsGrid cards_grid={block.cards_grid} />
|
||||||
...block.card_grid,
|
|
||||||
cards: block.card_grid.cards.map((card) => {
|
|
||||||
return {
|
|
||||||
...card,
|
|
||||||
link: card.link
|
|
||||||
? { ...card.link, href: modWebviewLink(card.link.href, lang) }
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
return <CardGrid card_grid={cardGrid} />
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function serverClient() {
|
|||||||
const lang = ctx?.lang || Lang.en
|
const lang = ctx?.lang || Lang.en
|
||||||
if (ctx?.webToken) {
|
if (ctx?.webToken) {
|
||||||
console.log({ ctx })
|
console.log({ ctx })
|
||||||
const returnUrl = ctx.pathname || overview[lang]
|
const returnUrl = ctx.url
|
||||||
|
|
||||||
const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(returnUrl)}`
|
const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(returnUrl)}`
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ export function getDefaultRequestHeaders(request: NextRequest) {
|
|||||||
|
|
||||||
const headers = new Headers(request.headers)
|
const headers = new Headers(request.headers)
|
||||||
headers.set("x-lang", lang)
|
headers.set("x-lang", lang)
|
||||||
headers.set("x-pathname", request.nextUrl.pathname.replace(`/${lang}`, ""))
|
headers.set(
|
||||||
|
"x-pathname",
|
||||||
|
request.nextUrl.pathname.replace(`/${lang}`, "").replace(`/webview`, "")
|
||||||
|
)
|
||||||
headers.set("x-url", request.nextUrl.href)
|
headers.set("x-url", request.nextUrl.href)
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { notFound } from "next/navigation"
|
|
||||||
import { type NextMiddleware, NextResponse } from "next/server"
|
import { type NextMiddleware, NextResponse } from "next/server"
|
||||||
|
|
||||||
import { findLang } from "@/constants/languages"
|
import { findLang } from "@/constants/languages"
|
||||||
@@ -9,9 +8,12 @@ import {
|
|||||||
webviews,
|
webviews,
|
||||||
} from "@/constants/routes/webviews"
|
} from "@/constants/routes/webviews"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import { badRequest } from "@/server/errors/next"
|
import { badRequest, notFound } from "@/server/errors/next"
|
||||||
|
|
||||||
import { decryptData } from "@/utils/aes"
|
import { decryptData } from "@/utils/aes"
|
||||||
|
import { resolve as resolveEntry } from "@/utils/entry"
|
||||||
|
|
||||||
|
import { getDefaultRequestHeaders } from "./utils"
|
||||||
|
|
||||||
import type { MiddlewareMatcher } from "@/types/middleware"
|
import type { MiddlewareMatcher } from "@/types/middleware"
|
||||||
|
|
||||||
@@ -19,22 +21,25 @@ export const middleware: NextMiddleware = async (request) => {
|
|||||||
const { nextUrl } = request
|
const { nextUrl } = request
|
||||||
const lang = findLang(nextUrl.pathname)
|
const lang = findLang(nextUrl.pathname)
|
||||||
|
|
||||||
const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}/webview`, "")
|
|
||||||
const headers = new Headers()
|
|
||||||
|
|
||||||
// If user is redirected to /lang/webview/refresh/, the webview token is invalid and we remove the cookie
|
// If user is redirected to /lang/webview/refresh/, the webview token is invalid and we remove the cookie
|
||||||
if (refreshWebviews.includes(nextUrl.pathname)) {
|
if (refreshWebviews.includes(nextUrl.pathname)) {
|
||||||
headers.set(
|
|
||||||
"Set-Cookie",
|
|
||||||
`webviewToken=0; Max-Age=0; Secure; HttpOnly; Path=/; SameSite=Strict;`
|
|
||||||
)
|
|
||||||
return NextResponse.rewrite(new URL(`/${lang}/webview/refresh`, nextUrl), {
|
return NextResponse.rewrite(new URL(`/${lang}/webview/refresh`, nextUrl), {
|
||||||
headers,
|
headers: {
|
||||||
|
"Set-Cookie": `webviewToken=0; Max-Age=0; Secure; HttpOnly; Path=/; SameSite=Strict;`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(request.nextUrl.searchParams)
|
const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}/webview`, "")
|
||||||
searchParams.set("uri", pathNameWithoutLang)
|
|
||||||
|
const { uid } = await resolveEntry(pathNameWithoutLang, lang)
|
||||||
|
if (!uid) {
|
||||||
|
throw notFound(
|
||||||
|
`Unable to resolve CMS entry for locale "${lang}": ${pathNameWithoutLang}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const headers = getDefaultRequestHeaders(request)
|
||||||
|
headers.set("x-uid", uid)
|
||||||
|
|
||||||
const webviewToken = request.cookies.get("webviewToken")
|
const webviewToken = request.cookies.get("webviewToken")
|
||||||
if (webviewToken) {
|
if (webviewToken) {
|
||||||
@@ -42,14 +47,21 @@ export const middleware: NextMiddleware = async (request) => {
|
|||||||
// we're done, allow it
|
// we're done, allow it
|
||||||
if (myPagesWebviews.includes(nextUrl.pathname)) {
|
if (myPagesWebviews.includes(nextUrl.pathname)) {
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(`/${lang}/webview/my-pages?${searchParams.toString()}`, nextUrl)
|
new URL(`/${lang}/webview/my-pages`, nextUrl),
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
|
} else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(
|
new URL(`/${lang}/webview/loyalty-page`, nextUrl),
|
||||||
`/${lang}/webview/loyalty-page?${searchParams.toString()}`,
|
{
|
||||||
nextUrl
|
request: {
|
||||||
)
|
headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return notFound()
|
return notFound()
|
||||||
@@ -77,32 +89,30 @@ export const middleware: NextMiddleware = async (request) => {
|
|||||||
authorization
|
authorization
|
||||||
)
|
)
|
||||||
|
|
||||||
headers.set(
|
|
||||||
"Set-Cookie",
|
|
||||||
`webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`
|
|
||||||
)
|
|
||||||
headers.set("Cookie", `webviewToken=${decryptedData}`)
|
|
||||||
|
|
||||||
console.log("IN WEBVIEW MIDDLEWARE", decryptedData)
|
|
||||||
|
|
||||||
if (myPagesWebviews.includes(nextUrl.pathname)) {
|
if (myPagesWebviews.includes(nextUrl.pathname)) {
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(
|
new URL(`/${lang}/webview/my-pages`, nextUrl),
|
||||||
`/${lang}/webview/my-pages?${searchParams.toString()}`,
|
|
||||||
nextUrl
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
headers,
|
headers: {
|
||||||
|
"Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
|
||||||
|
Cookie: `webviewToken=${decryptedData}`,
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
|
} else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(
|
new URL(`/${lang}/webview/loyalty-page`, nextUrl),
|
||||||
`/${lang}/webview/loyalty-page?${searchParams.toString()}`,
|
|
||||||
nextUrl
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
headers,
|
headers: {
|
||||||
|
"Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
|
||||||
|
Cookie: `webviewToken=${decryptedData}`,
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const protectedProcedure = t.procedure.use(async function (opts) {
|
|||||||
throw sessionExpiredError()
|
throw sessionExpiredError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session?.user) {
|
if (!session?.user || !opts.ctx.webToken) {
|
||||||
throw unauthorizedError()
|
throw unauthorizedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
82
test.js
Normal file
82
test.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
function base64ToUint8Array(base64String) {
|
||||||
|
const binaryString = atob(base64String)
|
||||||
|
const byteArray = new Uint8Array(binaryString.length)
|
||||||
|
for (let i = 0; i < binaryString.length; i++) {
|
||||||
|
byteArray[i] = binaryString.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return byteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
function utf8ToUint8Array(utf8String) {
|
||||||
|
return new TextEncoder().encode(utf8String)
|
||||||
|
}
|
||||||
|
|
||||||
|
function uint8ArrayToBase64(uint8Array) {
|
||||||
|
let binaryString = ""
|
||||||
|
const len = uint8Array.byteLength
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
binaryString += String.fromCharCode(uint8Array[i])
|
||||||
|
}
|
||||||
|
return btoa(binaryString)
|
||||||
|
}
|
||||||
|
async function encryptData(keyBase64, ivBase64, data) {
|
||||||
|
const keyBuffer = await crypto.subtle.importKey(
|
||||||
|
"raw",
|
||||||
|
base64ToUint8Array(keyBase64),
|
||||||
|
"AES-CBC",
|
||||||
|
false,
|
||||||
|
["encrypt"]
|
||||||
|
)
|
||||||
|
|
||||||
|
const dataBuffer = utf8ToUint8Array(data)
|
||||||
|
const ivBuffer = base64ToUint8Array(ivBase64)
|
||||||
|
const encryptedDataBuffer = await crypto.subtle.encrypt(
|
||||||
|
{ name: "AES-CBC", iv: ivBuffer },
|
||||||
|
keyBuffer,
|
||||||
|
dataBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedData = uint8ArrayToBase64(new Uint8Array(encryptedDataBuffer))
|
||||||
|
return encryptedData
|
||||||
|
}
|
||||||
|
|
||||||
|
function uint8ArrayToUtf8(uint8Array) {
|
||||||
|
return new TextDecoder().decode(uint8Array)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decryptData(keyBase64, ivBase64, encryptedDataBase64) {
|
||||||
|
const keyBuffer = await crypto.subtle.importKey(
|
||||||
|
"raw",
|
||||||
|
base64ToUint8Array(keyBase64),
|
||||||
|
"AES-CBC",
|
||||||
|
false,
|
||||||
|
["decrypt"]
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedDataBuffer = base64ToUint8Array(encryptedDataBase64)
|
||||||
|
const ivBuffer = base64ToUint8Array(ivBase64)
|
||||||
|
const decryptedDataBuffer = await crypto.subtle.decrypt(
|
||||||
|
{ name: "AES-CBC", iv: ivBuffer },
|
||||||
|
keyBuffer,
|
||||||
|
encryptedDataBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
const decryptedData = uint8ArrayToUtf8(new Uint8Array(decryptedDataBuffer))
|
||||||
|
return decryptedData
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = "_0XBPWQQ_e81346b1-6e8f-44bf-ad9c-33fd2dcc1abd"
|
||||||
|
const iv = btoa("abcdefghijklmnop")
|
||||||
|
const tegwpjke = await encryptData(
|
||||||
|
"JYekSRT8YXWquXpxxukJR0GsELl5Nt4KdcCbaCvSzHE=",
|
||||||
|
iv,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
const decrypttionData = await decryptData(
|
||||||
|
"JYekSRT8YXWquXpxxukJR0GsELl5Nt4KdcCbaCvSzHE=",
|
||||||
|
iv,
|
||||||
|
tegwpjke
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(tegwpjke, btoa("abcdefghijklmnop"), decrypttionData === data)
|
||||||
Reference in New Issue
Block a user