feat(WEB-132): add seamless login flow
This commit is contained in:
@@ -16,4 +16,10 @@ NEXTAUTH_REDIRECT_PROXY_URL="http://localhost:3000/api/auth"
|
|||||||
NEXTAUTH_SECRET=""
|
NEXTAUTH_SECRET=""
|
||||||
NEXTAUTH_URL="http://localhost:3000/api/auth"
|
NEXTAUTH_URL="http://localhost:3000/api/auth"
|
||||||
REVALIDATE_SECRET=""
|
REVALIDATE_SECRET=""
|
||||||
|
SEAMLESS_LOGIN_DA="http://www.example.dk/updatelogin"
|
||||||
|
SEAMLESS_LOGIN_DE="http://www.example.de/updatelogin"
|
||||||
|
SEAMLESS_LOGIN_EN="http://www.example.com/updatelogin"
|
||||||
|
SEAMLESS_LOGIN_FI="http://www.example.fi/updatelogin"
|
||||||
|
SEAMLESS_LOGIN_NO="http://www.example.no/updatelogin"
|
||||||
|
SEAMLESS_LOGIN_SE="http://www.example.se/updatelogin"
|
||||||
WEBVIEW_ENCRYPTION_KEY="MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
|
WEBVIEW_ENCRYPTION_KEY="MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
|
||||||
|
|||||||
@@ -2,18 +2,59 @@ import { NextRequest, NextResponse } from "next/server"
|
|||||||
import { AuthError } from "next-auth"
|
import { AuthError } from "next-auth"
|
||||||
|
|
||||||
import { signIn } from "@/auth"
|
import { signIn } from "@/auth"
|
||||||
import { badRequest } from "@/server/errors/next"
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { env } from "@/env/server"
|
||||||
import type { Lang } from "@/constants/languages"
|
import { badRequest, internalServerError } from "@/server/errors/next"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
context: { params: { lang: Lang } }
|
context: { params: { lang: Lang } }
|
||||||
) {
|
) {
|
||||||
const redirectTo =
|
const returnUrl = request.headers.get("x-returnurl")
|
||||||
|
|
||||||
|
// If all else fails, always redirect to startpage
|
||||||
|
const finalDestination =
|
||||||
|
returnUrl ||
|
||||||
request.headers.get("x-redirect-to") ||
|
request.headers.get("x-redirect-to") ||
|
||||||
request.nextUrl.searchParams.get("redirectTo") ||
|
request.nextUrl.searchParams.get("redirectTo") ||
|
||||||
undefined
|
request.headers.get("Referer") ||
|
||||||
|
"/"
|
||||||
|
|
||||||
|
let redirectTo = finalDestination
|
||||||
|
if (!returnUrl) {
|
||||||
|
// This is a regular login request, not a seamless login request
|
||||||
|
// We should initiate the seamless login flow
|
||||||
|
try {
|
||||||
|
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)
|
||||||
|
redirectUrl.searchParams.set("returnurl", finalDestination)
|
||||||
|
redirectTo = redirectUrl.toString()
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Unable to create URL for seamless login")
|
||||||
|
console.error(e)
|
||||||
|
return internalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
|
|||||||
6
auth.ts
6
auth.ts
@@ -85,8 +85,10 @@ export const config = {
|
|||||||
// Assume absolute URL
|
// Assume absolute URL
|
||||||
try {
|
try {
|
||||||
const parsedUrl = new URL(url)
|
const parsedUrl = new URL(url)
|
||||||
if (parsedUrl.hostname.endsWith(".scandichotels.com")) {
|
if (
|
||||||
// Allows **.scandichotels.com
|
/\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname)
|
||||||
|
) {
|
||||||
|
// Allows any subdomains on all top level domains above
|
||||||
return url
|
return url
|
||||||
} else if (parsedUrl.origin === baseUrl) {
|
} else if (parsedUrl.origin === baseUrl) {
|
||||||
// Allows callback URLs on the same origin
|
// Allows callback URLs on the same origin
|
||||||
|
|||||||
12
env/server.ts
vendored
12
env/server.ts
vendored
@@ -22,6 +22,12 @@ export const env = createEnv({
|
|||||||
NODE_ENV: z.enum(["development", "test", "production"]),
|
NODE_ENV: z.enum(["development", "test", "production"]),
|
||||||
PRINT_QUERY: z.boolean().default(false),
|
PRINT_QUERY: z.boolean().default(false),
|
||||||
REVALIDATE_SECRET: z.string(),
|
REVALIDATE_SECRET: z.string(),
|
||||||
|
SEAMLESS_LOGIN_DA: z.string(),
|
||||||
|
SEAMLESS_LOGIN_DE: z.string(),
|
||||||
|
SEAMLESS_LOGIN_EN: z.string(),
|
||||||
|
SEAMLESS_LOGIN_FI: z.string(),
|
||||||
|
SEAMLESS_LOGIN_NO: z.string(),
|
||||||
|
SEAMLESS_LOGIN_SV: z.string(),
|
||||||
WEBVIEW_ENCRYPTION_KEY: z.string(),
|
WEBVIEW_ENCRYPTION_KEY: z.string(),
|
||||||
},
|
},
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
@@ -45,6 +51,12 @@ export const env = createEnv({
|
|||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
PRINT_QUERY: process.env.PRINT_QUERY,
|
PRINT_QUERY: process.env.PRINT_QUERY,
|
||||||
REVALIDATE_SECRET: process.env.REVALIDATE_SECRET,
|
REVALIDATE_SECRET: process.env.REVALIDATE_SECRET,
|
||||||
|
SEAMLESS_LOGIN_DA: process.env.SEAMLESS_LOGIN_DA,
|
||||||
|
SEAMLESS_LOGIN_DE: process.env.SEAMLESS_LOGIN_DE,
|
||||||
|
SEAMLESS_LOGIN_EN: process.env.SEAMLESS_LOGIN_EN,
|
||||||
|
SEAMLESS_LOGIN_FI: process.env.SEAMLESS_LOGIN_FI,
|
||||||
|
SEAMLESS_LOGIN_NO: process.env.SEAMLESS_LOGIN_NO,
|
||||||
|
SEAMLESS_LOGIN_SV: process.env.SEAMLESS_LOGIN_SV,
|
||||||
WEBVIEW_ENCRYPTION_KEY: process.env.WEBVIEW_ENCRYPTION_KEY,
|
WEBVIEW_ENCRYPTION_KEY: process.env.WEBVIEW_ENCRYPTION_KEY,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import type { NextMiddleware } from "next/server"
|
|||||||
import type { MiddlewareMatcher } from "@/types/middleware"
|
import type { MiddlewareMatcher } from "@/types/middleware"
|
||||||
|
|
||||||
export const middleware: NextMiddleware = (request) => {
|
export const middleware: NextMiddleware = (request) => {
|
||||||
const redirectTo = request.nextUrl.searchParams.get("returnurl")
|
const returnUrl = request.nextUrl.searchParams.get("returnurl")
|
||||||
|
|
||||||
if (!redirectTo) {
|
if (!returnUrl) {
|
||||||
return badRequest()
|
return badRequest()
|
||||||
}
|
}
|
||||||
|
|
||||||
const lang = findLang(request.nextUrl.pathname)!
|
const lang = findLang(request.nextUrl.pathname)!
|
||||||
|
|
||||||
const headers = new Headers(request.headers)
|
const headers = new Headers(request.headers)
|
||||||
headers.set("x-redirect-to", redirectTo)
|
headers.set("x-returnurl", returnUrl)
|
||||||
|
|
||||||
return NextResponse.rewrite(new URL(`/${lang}/login`, request.nextUrl), {
|
return NextResponse.rewrite(new URL(`/${lang}/login`, request.nextUrl), {
|
||||||
request: {
|
request: {
|
||||||
|
|||||||
Reference in New Issue
Block a user