Skill: Auth Boundary
Use this when a website needs signed-in user state, login, logout, account-gated pages, profile loading, or protected file downloads. This template does not implement its own identity provider. The platform owns auth and exposes a small contract to the website.
Contract
- Browser code must call app-local endpoints only.
- Server code may call
https://fnf.internal/*. - Do not send auth headers to
fnf.internal; identity is attached by the platform/runtime automatically. - Do not call
https://fnf.internal/*from the browser. It is not reachable there and would be a security bug even if it were.
Endpoints:
| Purpose | Endpoint | Caller |
|---|---|---|
| Current user profile | GET https://fnf.internal/user |
server only |
| Browser-safe user proxy | GET /api/user |
frontend/browser |
| Start login | GET /__auth/login?return=<path> |
browser navigation |
| Start logout | GET /__auth/logout?return=<path> |
browser navigation |
GET https://fnf.internal/user returns the current user's profile JSON and
returns 401 when the visitor is not signed in.
GET /api/user must call https://fnf.internal/user server-side and return the
same status code and JSON body unchanged. This endpoint is the only user-profile
endpoint browser components should fetch.
Choose The Correct Auth Mode
There are two different auth concepts. Pick intentionally:
| Website need | Auth mode | Required behavior |
|---|---|---|
| Higgsfield SDK/model generation, media upload, profile, workspace, credits, feed/history | Higgsfield platform auth | Use /api/user, /__auth/login, /__auth/logout, and server-side https://fnf.internal guards |
| The generated website's own product accounts, for example todos, notes, CRM records, dashboards, memberships | In-app auth | Build app-local auth/session/storage. Do not call fnf.internal/user unless the website also uses Higgsfield SDK features |
| Both generation and product accounts | Both, kept separate | Gate SDK routes with Higgsfield auth and product data with in-app auth. Label UI clearly and never mix identities |
If the prompt mentions Higgsfield, SDK, model generation, Nano Banana, Seedance, image/video generation, media upload, credits, workspaces, or generation history, Higgsfield auth is implicit even if the user did not say "add sign in."
If the prompt only asks for "sign in" for a normal generated product and does not
use Higgsfield generation/profile/credits, default to in-app sign in. Do not use
/__auth/login as the product's account system unless the prompt specifically
asks for Higgsfield account sign-in.
Required /api/user Route
Create a TanStack Start server route, not a separate Hono/Express API:
// app/src/routes/api/user.ts
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/api/user')({
server: {
handlers: {
GET: async () => {
const upstream = await fetch('https://fnf.internal/user')
const body = await upstream.text()
return new Response(body, {
status: upstream.status,
headers: {
'content-type': upstream.headers.get('content-type') ?? 'application/json',
'cache-control': 'no-store',
},
})
},
},
},
})
Notes:
- Preserve upstream status. A
401fromfnf.internal/usermust remain401at/api/user. - Preserve upstream JSON body unchanged. Do not reshape fields in the proxy.
- Add
cache-control: no-store; user identity is per request. - Do not add
Authorization, cookies, service secrets,hf-user-id, or marketplace headers. The platform injects identity on server egress. - Do not expose raw fnf internal URLs to the UI.
Frontend User Loader Pattern
Use /api/user from loaders, queries, or components:
export async function fetchCurrentUser() {
const response = await fetch('/api/user', { credentials: 'include' })
if (response.status === 401)
return null
if (!response.ok)
throw new Error('Failed to load user')
return response.json()
}
For route loaders, return null for signed-out users rather than crashing the
page. For account-required pages, redirect or render a sign-in action.
Login
Login is browser navigation, not an SDK call:
function login(returnPath = window.location.pathname + window.location.search) {
window.location.href = `/__auth/login?return=${encodeURIComponent(returnPath)}`
}
Rules:
- Use the current path as
returnby default. - Encode the return path.
- Keep the return path app-local. Do not pass full external URLs.
- After login completes, the platform redirects back to
return; then/api/usershould succeed.
Logout
Logout is also browser navigation:
function logout(returnPath = '/') {
window.location.href = `/__auth/logout?return=${encodeURIComponent(returnPath)}`
}
Rules:
- Use
/or the current public route as the defaultreturn. - Clear client-side query/cache state after navigation if the website stores user, workspace, feed, or credits data in TanStack Query.
- Do not try to delete platform cookies manually from website code.
- Do not call
fnf.internalfor logout.
Authenticated File Downloads
Apps may run inside a cross-origin authenticated iframe. Native download
navigation may not preserve that embedded authentication context and can return
401 Unauthorized. For a protected app-local /api/... file, NEVER use:
<a href="/api/..." download>window.locationwindow.open
Fetch the file with credentials and download the successful response through a temporary Blob URL instead. This is a browser-only helper: call it from an event handler, never during SSR or module initialization.
export async function downloadAuthenticatedFile(
url: string,
filename: string,
): Promise<void> {
const downloadUrl = new URL(url, window.location.href)
if (
downloadUrl.origin !== window.location.origin
|| !downloadUrl.pathname.startsWith('/api/')
) {
throw new Error('Authenticated downloads require an app-local /api/... URL')
}
const response = await fetch(downloadUrl, {
credentials: 'include',
})
if (!response.ok) {
throw new Error(`Download failed (${response.status})`)
}
const blobUrl = URL.createObjectURL(await response.blob())
const anchor = document.createElement('a')
try {
anchor.href = blobUrl
anchor.download = filename
anchor.style.display = 'none'
document.body.appendChild(anchor)
anchor.click()
}
finally {
anchor.remove()
window.setTimeout(() => URL.revokeObjectURL(blobUrl), 0)
}
}
Call the helper from a real Quanta button. The handler owns loading state and must catch and surface download errors through designed inline feedback or a toast.
<Button
type="button"
disabled={isDownloading}
onClick={handleDownload}
>
{isDownloading ? 'Downloading…' : 'Download'}
</Button>
Requirements:
- The protected server route must re-check the current user and authorize that
specific file. Return
401/403for unauthenticated/forbidden requests; never redirect a download fetch to an HTML login page. Serve the file with a truthfulContent-Typeand, when the route owns the filename, matchingContent-Disposition. - Pass
credentials: 'include'. The helper enforces a same-origin/api/...URL; never weaken that check or pass it an untrusted arbitrary URL. - Check
response.okbefore reading the body. - Show loading and error states.
- Remove every temporary element and revoke every object URL after the click has had a browser task to start the download.
- Do not nest a
<button>inside an<a>. - Raw download links are allowed only for public static files or self-contained signed URLs that require no session.
- For large files, prefer a backend-generated short-lived signed URL with
Content-Disposition: attachmentto avoid buffering the entire file in browser memory. - Test downloads inside the Higgsfield iframe, not only with the app opened directly. Test both newly generated files and previously saved files.
- If a sandboxed host iframe blocks programmatic downloads, the host must grant its download capability. Report that platform constraint; do not work around it by navigating to the protected URL.
For generated-media exports, also apply references/fnf-sdk.md → “Output and
download format contract” so the downloaded bytes, MIME type, extension, and
filename agree. A direct getRawUrl() link is safe only when it is public or a
self-contained signed URL that needs no session; otherwise use the helper.
Auth UI Rules
- Signed-out state should show a clear sign-in action using the website's normal Quanta button style.
- Do not build email/password forms unless the task explicitly asks for a custom
identity UI and the platform supports it. The default is
/__auth/login. - Do not show fake profile data while loading. Use skeletons or neutral loading states.
- Never render emails, names, workspace names, tokens, or raw profile payloads into observability events.
FNF SDK Interaction
For generated websites using the fnf SDK, create SDK adapters server-side with
baseUrl: 'https://fnf.internal'. The auth boundary above is for website UI
identity. SDK calls should still use server functions or server-only modules and
must not be made directly from browser components.
Required Auth For SDK Features
Any website that uses SDK-backed generation must be authenticated. This includes:
- generation submit, poll, cancel, feed/history, and cost preview
- media upload, media resolve, and media reads
- profile, workspace switch, wallet, and credits
- any UI that exposes model settings or writes paid/user-owned outputs
When the user asks for a Higgsfield generation website, model form, image/video
generator, Nano Banana/Seedance/etc. website, or anything that uses the SDK, auth is
implicit even if the user does not say "add sign in." Add sign-in/logout,
/api/user, server-side auth guards, profile/credits/workspace UI, and a
feed/history surface automatically.
The only exception is an offline/mock demo the user EXPLICITLY asked for — memory adapters only, never calling fnf.internal, apps-marketplace, media upload, or profile endpoints. Never choose a mock as the default: a real SDK app is end-to-end, with a real backend and D1 persistence (see references/app-flow.md rule 3a).
Required flow:
- Implement
/api/user. - Browser loads
/api/userbefore rendering SDK-backed controls. - If
/api/userreturns401, show a signed-out state and a Quanta sign-in button that navigates to/__auth/login?return=<current path>. - Disable or hide submit/upload/cost/profile controls while signed out.
- Every server function or server route that performs an SDK operation must
call
https://fnf.internal/userfirst and stop on401.
The UI gate is for experience; the server-side auth check is the actual safety boundary. Do not rely on "button hidden when signed out" as protection.
If a page needs both user state and generation data:
- Browser calls
/api/userfor display/auth gate. - Server function submits/reads jobs through the selected SDK adapter.
- Browser receives only safe website data: user-safe fields, generation ids, statuses, display credits, and sanitized errors.
Server Auth Guard Pattern
Use a small server-only helper for SDK server functions:
// app/src/lib/auth.server.ts
export async function requireCurrentUser() {
const response = await fetch('https://fnf.internal/user')
const body = await response.json().catch(() => null)
if (response.status === 401) {
return {
ok: false as const,
status: 401,
body,
}
}
if (!response.ok) {
return {
ok: false as const,
status: response.status,
body,
}
}
return {
ok: true as const,
user: body,
}
}
Then guard SDK operations:
const auth = await requireCurrentUser()
if (!auth.ok) {
return new Response(JSON.stringify(auth.body), {
status: auth.status,
headers: { 'content-type': 'application/json' },
})
}
// Safe to create SDK adapter/client and submit/read user-owned data here.
For createServerFn, return a typed { ok: false, code: 'unauthorized' }
result or throw/redirect according to the website's route behavior, but still check
auth before the SDK call.
Signed-Out UI Pattern
Use this Quanta pattern for app-shaped generation/tool UIs. Marketing
waitlists and landing pages should use custom sign-in chrome per
references/design-taste-frontend.md instead of Quanta buttons.
import { Button } from '@higgsfield/quanta/button'
function SignInRequired() {
return (
<div className="grid min-h-72 place-items-center rounded-lg border border-q-border-subtle bg-q-background-secondary p-6 text-center">
<div className="grid max-w-sm gap-3">
<h2 className="text-q-title-md-semi-bold">Sign in to generate</h2>
<p className="text-q-body-sm-regular text-q-text-secondary">
Generation, uploads, credits, and history are connected to your account.
</p>
<div>
<Button
onClick={() => {
const returnPath = window.location.pathname + window.location.search
window.location.href = `/__auth/login?return=${encodeURIComponent(returnPath)}`
}}
>
Sign in
</Button>
</div>
</div>
</div>
)
}
Do not render active prompt fields, upload controls, generate buttons, cost buttons, workspace switchers, or job feeds as usable controls while signed out.
Anti-Patterns
await fetch('https://fnf.internal/user') // bad in browser code
await fetch('/api/user') // good in browser code
fetch('https://fnf.internal/user', {
headers: { Authorization: `Bearer ${token}` },
}) // bad; platform attaches identity automatically
window.location.href = '/login' // bad; not the platform auth route
window.location.href = '/__auth/login?return=%2Fdashboard' // good
// bad: anonymous generation surface
await jobs.submit(input)
// good: server-side auth gate first
const auth = await requireCurrentUser()
if (!auth.ok) {
return new Response(JSON.stringify(auth.body), {
status: auth.status,
headers: { 'content-type': 'application/json' },
})
}
await jobs.submit(input)