Files
web/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/callback/route.ts
Joakim Jäderberg 46ebbbba8f Merged in feature/sas-login (pull request #1256)
First steps towards the SAS partnership

* otp flow now pretends to do the linking

* Update LinkAccountForm header

* Update redirect times

* Clean up comments

* Set maxAge on sas cookies

* make all SAS routes protected

* Merge remote-tracking branch 'refs/remotes/origin/feature/sas-login' into feature/sas-login

* Require auth for sas link flow

* Fix resend otp

* Add error support to OneTimePasswordForm

* Add Sentry to SAS error boundary

* Move SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME

* Add missing translations

* Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/sas-login

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* Add TooManyCodesError component

* Refactor GenericError to support new errors

* Add FailedAttemptsError

* remove removed component <VWOScript/>

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* remove local cookie-bot reference

* Fix sas campaign logo scaling

* feature toggle the SAS stuff

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* fix: use env vars for SAS endpoints


Approved-by: Linus Flood
2025-02-05 14:43:14 +00:00

106 lines
2.8 KiB
TypeScript

import { cookies } from "next/headers"
import { redirect } from "next/navigation"
import { z } from "zod"
import { env } from "@/env/server"
import { serverClient } from "@/lib/trpc/server"
import { safeTry } from "@/utils/safeTry"
import { SAS_TOKEN_STORAGE_KEY, stateSchema } from "../sasUtils"
import type { NextRequest } from "next/server"
const searchParamsSchema = z.object({
code: z.string(),
state: z.string(),
})
const tokenResponseSchema = z.object({
access_token: z.string(),
expires_in: z.number(),
token_type: z.literal("Bearer"),
})
export async function GET(
request: NextRequest,
{ params }: { params: { lang: string } }
) {
const { lang } = params
const result = searchParamsSchema.safeParse({
code: request.nextUrl.searchParams.get("code"),
state: request.nextUrl.searchParams.get("state"),
})
if (!result.success) {
console.error("[SAS] Invalid search params", result.error)
redirect(`/${lang}/sas-x-scandic/error?errorCode=invalid_query`)
}
const { code, state } = result.data
const tokenResponse = await fetch(
new URL("oauth/token", env.SAS_AUTH_ENDPOINT),
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: code,
redirect_uri: new URL(
`/${lang}/sas-x-scandic/callback`,
new URL(env.PUBLIC_URL)
).toString(),
client_id: env.SAS_AUTH_CLIENTID,
}),
}
)
if (!tokenResponse.ok) {
const error = await tokenResponse.text()
console.error("[SAS] Failed to get token", error)
redirect(`/${lang}/sas-x-scandic/error?errorCode=token_error`)
}
const tokenData = tokenResponseSchema.parse(await tokenResponse.json())
const stateResult = stateSchema.safeParse(
JSON.parse(decodeURIComponent(state))
)
if (!stateResult.success) {
redirect(`/${lang}/sas-x-scandic/error?errorCode=invalid_state`)
}
const cookieStore = cookies()
cookieStore.set(SAS_TOKEN_STORAGE_KEY, tokenData.access_token, {
maxAge: 3600,
httpOnly: true,
})
if (stateResult.data.intent === "link") {
const [data, error] = await safeTry(
serverClient().partner.sas.requestOtp({})
)
// status: 'SENT' => OK
if (!data || error) {
//TODO: Check what error we get
console.error("[SAS] Failed to request OTP", error)
redirect(`/${lang}/sas-x-scandic/error`)
}
console.log("[SAS] Request OTP response", data)
const otpUrl = new URL(
`/${lang}/sas-x-scandic/otp`,
new URL(env.PUBLIC_URL)
)
otpUrl.searchParams.set("intent", stateResult.data.intent)
otpUrl.searchParams.set("to", data.otpReceiver)
redirect(otpUrl.toString())
}
redirect(`/${lang}/sas-x-scandic/error?errorCode=unknown_intent`)
}