108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { useRouter } from "next/navigation"
|
|
import {
|
|
clearPortalHandoffFlag,
|
|
clearToken,
|
|
can,
|
|
ensureAuthConfig,
|
|
firstAllowedPath,
|
|
getClaims,
|
|
getToken,
|
|
parseHashToken,
|
|
permissionForPath,
|
|
redirectToPortalLogin,
|
|
redirectToPortalLoginInteractive,
|
|
setToken,
|
|
} from "@/lib/auth"
|
|
import { permittedAuthReturnPath } from "@/lib/auth-return-path"
|
|
|
|
export default function AuthCallbackPage() {
|
|
const router = useRouter()
|
|
const [message, setMessage] = useState("Перенаправление на Auth Portal…")
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
|
|
void (async () => {
|
|
await ensureAuthConfig()
|
|
if (cancelled) return
|
|
|
|
const params = new URLSearchParams(window.location.search)
|
|
const error = params.get("error")
|
|
const returnPath = () => permittedAuthReturnPath(params.get("next"), pathname => {
|
|
const permission = permissionForPath(pathname)
|
|
return !permission || can(permission)
|
|
}, firstAllowedPath())
|
|
if (error === "sso_loop" || error === "jwt_rejected") {
|
|
redirectToPortalLoginInteractive()
|
|
return
|
|
}
|
|
|
|
const { accessToken } = parseHashToken(window.location.hash)
|
|
if (accessToken) {
|
|
setToken(accessToken)
|
|
clearPortalHandoffFlag()
|
|
const claims = getClaims()
|
|
if (!claims) {
|
|
clearToken()
|
|
redirectToPortalLoginInteractive()
|
|
return
|
|
}
|
|
if (!claims.apps.includes("mm")) {
|
|
setMessage("Нет доступа к приложению")
|
|
router.replace("/access-denied")
|
|
return
|
|
}
|
|
|
|
try {
|
|
const res = await fetch("/api/auth/config", {
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
})
|
|
if (res.status === 401) {
|
|
clearToken()
|
|
redirectToPortalLoginInteractive()
|
|
return
|
|
}
|
|
} catch {
|
|
/* ignore network — proceed */
|
|
}
|
|
|
|
const next = returnPath()
|
|
if (next === "/access-denied") {
|
|
router.replace("/access-denied")
|
|
return
|
|
}
|
|
router.replace(next)
|
|
return
|
|
}
|
|
|
|
if (getToken() && getClaims()) {
|
|
clearPortalHandoffFlag()
|
|
if (!getClaims()!.apps.includes("mm")) {
|
|
router.replace("/access-denied")
|
|
return
|
|
}
|
|
router.replace(returnPath())
|
|
return
|
|
}
|
|
|
|
const callback = new URL(`${window.location.origin}/auth/callback`)
|
|
if (params.get("next")) callback.searchParams.set("next", params.get("next")!)
|
|
const ok = redirectToPortalLogin(callback.toString())
|
|
if (!ok) redirectToPortalLoginInteractive()
|
|
})()
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [router])
|
|
|
|
return (
|
|
<div className="text-muted-foreground flex min-h-svh items-center justify-center p-6 text-sm">
|
|
{message}
|
|
</div>
|
|
)
|
|
}
|