mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-10-25 02:31:30 +00:00
Names the two halves for what they are. The repo already holds both (12407e9 merged bootnode/bootnode into this history); this makes the frontend directory say so, so the layout reads the way the owner describes the system: backend and frontend separate, one repo. git mv throughout — 187 renames, history preserved. Every path reference follows: hanzo.yml build context and dockerfile, compose.yml, compose.prod.yml, start.sh, LLM.md, infra/k8s/web-deployments.yaml, infra/cloud/deploy.sh, and the Dockerfile's own COPY paths. Image and container names (bootnode-web, the -web tag suffix) are deliberately NOT renamed here: those are a deploy contract the App CR and ingress bind to on port 3001, and breaking them is a separate, staged change. Console builds green after the move; the Go tree builds. Co-authored-by: Hanzo Dev <[email protected]>
59 lines
2.3 KiB
TypeScript
59 lines
2.3 KiB
TypeScript
// Canonical IAM auth (HIP-0111) via @hanzo/iam SPA PKCE.
|
|
//
|
|
// One source of truth for the browser IAM client, white-labelled per brand:
|
|
// serverUrl + clientId come from the active brand (lib/brand.ts), resolved
|
|
// from window.location.hostname. There are no env overrides — see below.
|
|
//
|
|
// HIP-0111: authenticate ONLY through @hanzo/iam against the canonical OIDC
|
|
// endpoints. The SDK resolves /v1/iam/oauth/* via live discovery (with a
|
|
// canonical-path fallback), drives PKCE S256, and owns `state` for CSRF. No
|
|
// hand-rolled authorize URLs, no genericOAuth({discoveryUrl}), no /api/auth.
|
|
"use client"
|
|
|
|
import { IAM } from "@hanzo/iam/browser"
|
|
import { getBrand } from "@/lib/brand"
|
|
|
|
// Stash the post-login destination across the IAM round-trip. The SDK owns
|
|
// `state` for CSRF; the app owns where to land after sign-in.
|
|
const RETURN_URL_KEY = "bootnode_return_url"
|
|
|
|
export function getIam(): IAM {
|
|
// The brand is the ONE source for issuer + client, and it is resolved from
|
|
// the hostname of the page the user is standing on. There is no env
|
|
// override, by design: this deployment serves MANY white-label hosts, so a
|
|
// build-time NEXT_PUBLIC_IAM_URL is a single global value that every brand
|
|
// would share — it cannot express per-host white-labeling, and it silently
|
|
// outranked the correct per-brand default. That is exactly what shipped:
|
|
// bootno.de/dashboard bounced to hanzo.id and asked a Bootnode customer to
|
|
// "Sign in to Hanzo ID" under the Hanzo mark.
|
|
//
|
|
// `getBrand()` and not `useBrand()`: this runs imperatively at click time,
|
|
// when `window` exists, so it reads the true host rather than the prerender
|
|
// default — and it must stay callable outside React.
|
|
const { url: serverUrl, clientId } = getBrand().iam
|
|
return new IAM({
|
|
serverUrl,
|
|
clientId,
|
|
redirectUri: `${window.location.origin}/auth/callback`,
|
|
scope: "openid profile email",
|
|
})
|
|
}
|
|
|
|
export function setReturnUrl(url: string): void {
|
|
try {
|
|
sessionStorage.setItem(RETURN_URL_KEY, url)
|
|
} catch {
|
|
/* sessionStorage unavailable — fall back to default destination */
|
|
}
|
|
}
|
|
|
|
export function takeReturnUrl(): string {
|
|
try {
|
|
const url = sessionStorage.getItem(RETURN_URL_KEY)
|
|
sessionStorage.removeItem(RETURN_URL_KEY)
|
|
return url || "/dashboard"
|
|
} catch {
|
|
return "/dashboard"
|
|
}
|
|
}
|