Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 2m52s
Docker images / frontend-image (push) Successful in 2m23s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / publish-release (push) Successful in 8s
JWT на backend, handoff/callback на UI, RBAC mm:*, AUTH_* в compose. Co-authored-by: Cursor <cursoragent@cursor.com>
52 lines
1.1 KiB
TypeScript
52 lines
1.1 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState, type ReactNode } from "react"
|
|
import {
|
|
ensureAuthConfig,
|
|
getClaims,
|
|
getToken,
|
|
isAuthEnabled,
|
|
redirectToPortalLogin,
|
|
redirectToPortalLoginInteractive,
|
|
} from "@/lib/auth"
|
|
|
|
export function AuthGuard({ children }: { children: ReactNode }) {
|
|
const [ready, setReady] = useState(false)
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
void (async () => {
|
|
await ensureAuthConfig()
|
|
if (cancelled) return
|
|
if (!isAuthEnabled()) {
|
|
setReady(true)
|
|
return
|
|
}
|
|
const claims = getClaims()
|
|
if (!getToken() || !claims) {
|
|
const ok = redirectToPortalLogin()
|
|
if (!ok) redirectToPortalLoginInteractive()
|
|
return
|
|
}
|
|
if (!claims.apps.includes("mm")) {
|
|
window.location.assign("/access-denied")
|
|
return
|
|
}
|
|
setReady(true)
|
|
})()
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [])
|
|
|
|
if (!ready) {
|
|
return (
|
|
<div className="text-muted-foreground flex min-h-svh items-center justify-center p-6 text-sm">
|
|
Проверка сессии…
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return children
|
|
}
|