These are now defined in Netlify UI for dedicated environments (test, stage, production): AUTH_URL NEXTAUTH_URL PUBLIC_URL Code now falls back to incoming request host. Mainly used for deployment previews which do not have Akamai in front, meaning we do not need the above workaround as incoming request host matches the actual public facing host. When Akamai is in front, we lose the public facing host in Netlify's routing layer as they internally use `x-forwarded-for` and we can't claim it for our usage.
184 lines
5.9 KiB
TypeScript
184 lines
5.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
|
import { AuthError } from "next-auth"
|
|
|
|
import { Lang } from "@/constants/languages"
|
|
import { env } from "@/env/server"
|
|
import { internalServerError } from "@/server/errors/next"
|
|
import { getPublicURL } from "@/server/utils"
|
|
|
|
import { signIn } from "@/auth"
|
|
|
|
export async function GET(
|
|
request: NextRequest,
|
|
context: { params: { lang: Lang } }
|
|
) {
|
|
const publicURL = getPublicURL(request)
|
|
|
|
let redirectHeaders: Headers | undefined = undefined
|
|
let redirectTo: string
|
|
|
|
const returnUrl = request.headers.get("x-returnurl")
|
|
const isSeamless = request.headers.get("x-login-source") === "seamless"
|
|
const isMFA = request.headers.get("x-login-source") === "mfa"
|
|
const isSeamlessMagicLink =
|
|
request.headers.get("x-login-source") === "seamless-magiclink"
|
|
|
|
console.log(
|
|
`[login] source: ${request.headers.get("x-login-source") || "normal"}`
|
|
)
|
|
|
|
const redirectToCookieValue = request.cookies.get("redirectTo")?.value // Cookie gets set by authRequired middleware
|
|
const redirectToSearchParamValue =
|
|
request.nextUrl.searchParams.get("redirectTo")
|
|
const redirectToFallback = "/"
|
|
|
|
console.log(`[login] redirectTo cookie value: ${redirectToCookieValue}`)
|
|
console.log(
|
|
`[login] redirectTo search param value: ${redirectToSearchParamValue}`
|
|
)
|
|
|
|
if (isSeamless || isSeamlessMagicLink || isMFA) {
|
|
if (returnUrl) {
|
|
redirectTo = returnUrl
|
|
} else {
|
|
console.log(
|
|
`[login] missing returnUrl, using fallback: ${redirectToFallback}`
|
|
)
|
|
redirectTo = redirectToFallback
|
|
}
|
|
} else {
|
|
redirectTo =
|
|
redirectToCookieValue || redirectToSearchParamValue || redirectToFallback
|
|
|
|
// Make relative URL to absolute URL
|
|
if (redirectTo.startsWith("/")) {
|
|
console.log(`[login] make redirectTo absolute, from ${redirectTo}`)
|
|
redirectTo = new URL(redirectTo, publicURL).href
|
|
console.log(`[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"
|
|
)
|
|
|
|
try {
|
|
// Initiate the seamless login flow
|
|
let redirectUrlValue
|
|
switch (context.params.lang) {
|
|
case Lang.da:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_DA
|
|
break
|
|
case Lang.de:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_DE
|
|
break
|
|
case Lang.en:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_EN
|
|
break
|
|
case Lang.fi:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_FI
|
|
break
|
|
case Lang.no:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_NO
|
|
break
|
|
case Lang.sv:
|
|
redirectUrlValue = env.SEAMLESS_LOGIN_SV
|
|
break
|
|
}
|
|
const redirectUrl = new URL(redirectUrlValue)
|
|
console.log(`[login] creating redirect to seamless login: ${redirectUrl}`)
|
|
redirectUrl.searchParams.set("returnurl", redirectTo)
|
|
console.log(
|
|
`[login] returnurl for seamless login: ${redirectUrl.searchParams.get("returnurl")}`
|
|
)
|
|
redirectTo = redirectUrl.toString()
|
|
|
|
/** Set cookie with redirect Url to appropriately redirect user when using magic link login */
|
|
redirectHeaders.append(
|
|
"set-cookie",
|
|
"magicLinkRedirectTo=" +
|
|
redirectTo +
|
|
"; Max-Age=300; Path=/; HttpOnly; SameSite=Lax"
|
|
)
|
|
} catch (e) {
|
|
console.error(
|
|
"[login] unable to create URL for seamless login, proceeding without it.",
|
|
e
|
|
)
|
|
}
|
|
}
|
|
|
|
try {
|
|
console.log(`[login] final redirectUrl: ${redirectTo}`)
|
|
console.log({ login_env: process.env })
|
|
|
|
/** Record<string, any> is next-auth typings */
|
|
const params: Record<string, any> = {
|
|
ui_locales: context.params.lang,
|
|
scope: ["openid", "profile"],
|
|
/**
|
|
* The `acr_values` param is used to make Curity display the proper login
|
|
* page for Scandic. Without the parameter Curity presents some choices
|
|
* to the user which we do not want.
|
|
*/
|
|
acr_values: "acr",
|
|
|
|
/**
|
|
* Both of the below two params are required to send for initiating login as well
|
|
* because user might choose to do Email link login.
|
|
* */
|
|
// The `for_origin` param is used to make Curity email login functionality working.
|
|
for_origin: publicURL,
|
|
// This is new param set for differentiate between the Magic link login of New web and current web
|
|
version: "2",
|
|
}
|
|
|
|
if (isMFA) {
|
|
// Append profile_update scope for MFA
|
|
params.scope.push("profile_update")
|
|
/**
|
|
* The below acr value is required as for New Web same Curity Client is used for MFA
|
|
* while in current web it is being setup using different Curity Client
|
|
*/
|
|
params.acr_values =
|
|
"urn:se:curity:authentication:otp-authenticator:OTP-Authenticator_web"
|
|
} else if (isSeamlessMagicLink) {
|
|
params.acr_values = "abc"
|
|
}
|
|
params.scope = params.scope.join(" ")
|
|
/**
|
|
* 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(
|
|
"curity",
|
|
{
|
|
redirectTo,
|
|
redirect: false,
|
|
},
|
|
params
|
|
)
|
|
|
|
if (redirectUrl) {
|
|
const redirectOpts = {
|
|
headers: redirectHeaders,
|
|
}
|
|
console.log(`[login] redirecting to: ${redirectUrl}`, redirectOpts)
|
|
return NextResponse.redirect(redirectUrl, redirectOpts)
|
|
} else {
|
|
console.error(`[login] missing redirectUrl reponse from signIn()`)
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof AuthError) {
|
|
console.error({ signInAuthError: error })
|
|
} else {
|
|
console.error({ signInError: error })
|
|
}
|
|
}
|
|
|
|
return internalServerError()
|
|
}
|