| name | frontmcp-auth-ui |
| description | Use when customizing, branding, or replacing the built-in FrontMCP OAuth pages (the login, consent, federated-select, incremental-authorization, and error pages) with your own React components. Covers the auth.ui slot-to-file map and auth.extras name-to-handler map on the auth config (no decorator, no class); the @frontmcp/ui/auth React hooks, the AuthPageWrapper component, and mountAuthPage (client-rendered via an esm.sh import-map plus a per-file server-side transform, with no bundling and no SSR); and the framework-owned CSRF and CSP. Triggers: custom login page, brand the consent screen, replace the OAuth UI, custom authorization UI, style the auth pages, multi-step login. The skill for CUSTOM AUTHORIZATION UI (distinct from auth config in frontmcp-config and permissions in frontmcp-authorities). |
| tags | ["auth","auth-ui","login","consent","custom-ui","react","oauth","guide"] |
| category | config |
| targets | ["all"] |
| bundle | ["full"] |
| priority | 5 |
| visibility | both |
| license | Apache-2.0 |
| metadata | {"docs":"https://docs.agentfront.dev/frontmcp/authentication/custom-ui"} |
FrontMCP Custom Authorization UI (auth.ui)
Entry point for replacing FrontMCP's built-in OAuth pages (login, consent, federated, incremental, error) with your own React components. Custom UI is a simple slot→file map (auth.ui) plus an extras name→handler map (auth.extras) on the auth config — there is no decorator and no class. The references/custom-auth-ui.md reference has the full API; the examples/ show a single login slot and a multi-step extras form.
When to Use This Skill
Must Use
- Branding or fully replacing the
local/remote mode login page with a custom React component
- Building a custom consent screen, federated provider picker, incremental authorization, or error page
- Adding a server-validated multi-step field to an authorization page (e.g. "add another item") via
auth.extras
Recommended
- Understanding which half is the server (
auth.ui / auth.extras in @frontmcp/sdk) and which is the client (@frontmcp/ui/auth)
- Looking up the
AuthFlowState fields a slot component receives, or the /oauth/ui/extra route
- Confirming that the framework (not your component) owns CSRF + CSP
Skip When
- You only need to add/rename fields on the built-in login page or run a custom verifier — use the declarative
login / authenticate config in frontmcp-config → configure-auth instead (no React, no build step)
- You are customizing a tool widget (not an auth page) — use
create-tool → ui-widgets
- You don't need a custom page at all — configuring no
auth.ui keeps the built-in pages
Decision: Use this skill when you want to render your OWN component for an auth slot. Use configure-auth's declarative login config when tweaking the built-in page's fields is enough.
Prerequisites
- A FrontMCP server in
local or remote auth mode (see frontmcp-config → configure-auth)
@frontmcp/ui, react, and react-dom installed (react/react-dom are peer deps of @frontmcp/ui)
Steps
- Write a React component (default export) for the slot, reading the injected state via
@frontmcp/ui/auth hooks (see references/custom-auth-ui.md)
- Map the slot to its
.tsx path under auth.ui — a RELATIVE path auto-anchored to the config file's directory (no fileURLToPath)
- (Optional) Add an
auth.extras[name] handler function for any mid-flow validated field
- Declare both under
auth: @FrontMcp({ auth: { mode: 'local', ui: { login: './…' }, extras: { … } } }) (per-app under splitByApp — put them on the @App({ auth }) that owns the pages)
- Verify using the Verification Checklist below
Scenario Routing Table
| Scenario | Reference | Description |
|---|
| Replace any built-in auth page with a React component | custom-auth-ui | auth.ui: { slot: './file.tsx' }, the hooks, <AuthPageWrapper>, mountAuthPage |
| Add a server-validated mid-flow field (multi-step form) | custom-auth-ui | auth.extras: { name: handler }, the accumulator, useExtraField / useAddedItems |
| Look up the flow-state fields a component receives | custom-auth-ui | AuthFlowState field table (no PII) |
| Understand the served route and the framework-owned CSRF + CSP | custom-auth-ui | /oauth/ui/extra, the esm.sh import-map + inline module, security ownership |
The map form
Custom UI is a slot→file map on the auth config. Point each slot at a sibling .tsx/.jsx source (default export); the SDK transpiles it once server-side and inlines it as an ES module, with deps loaded from esm.sh via an import-map and mountAuthPage appended for you — exactly as @Tool({ ui }) leads with the FileSource { file } form:
import { App } from '@frontmcp/sdk';
@App({
auth: {
mode: 'local',
ui: { login: './auth/login.tsx' },
extras: { 'envs:add': async (input, ctx) => ({ ok: true, addedItems: [{ key: String(input.key) }] }) },
},
})
export default class Server {}
A relative auth.ui path resolves against the directory of the file that declares the @App/@FrontMcp config — captured automatically at decoration time. No fileURLToPath needed. Absolute paths pass through; on capture failure the framework falls back to process.cwd() with a warning.
Common Patterns
| Pattern | Correct | Incorrect | Why |
|---|
| Slot registration | auth: { ui: { login: './login.tsx' } } | auth: { ui: [LoginAuthUi] } (array of classes) | Custom UI is a slot→file MAP now — no decorator, no class |
| Path anchoring | Relative './login.tsx' (auto-anchored to the config file) | fileURLToPath(new URL('./login.tsx', import.meta.url)) | The framework captures the config file's dir for you — manual anchoring is no longer needed |
| Extra registration | auth: { extras: { 'envs:add': handlerFn } } | auth: { extras: [AddEnvExtra] } (annotated class) | Extras are an extra-name → handler-function MAP now |
| CSRF | Let the hooks round-trip csrfToken | Generating / checking a token in your component | The server mints + verifies CSRF; your component must not |
| User identity | User-typed fields live in your own <form> inputs | Putting email/name into AuthFlowState | AuthFlowState is PII-free by contract — it carries OAuth client ids + control fields only |
| Wrapper form | Wrap UI in <AuthPageWrapper> (renders the finish <form> + hidden fields) | Hand-rolling pending_auth_id / csrf hidden inputs | The wrapper injects the control fields so a no-JS submit still works |
Verification Checklist
Troubleshooting
| Problem | Cause | Solution |
|---|
| Built-in page still shows | Slot not in auth.ui, or the .tsx failed to transpile | Confirm the slot is in auth: { ui: { … } } on the scope that owns the pages; check logs — a transpile error falls back to the built-in page |
Blank page / @frontmcp/ui/auth 404s in the browser | @frontmcp/ui/auth isn't on esm.sh (unpublished monorepo) | Publish @frontmcp/ui, OR map it to a locally-served ESM URL via @FrontMcp({ ui: { cdnOverrides } }) (see references/custom-auth-ui.md → Local dev / offline) |
ENOENT / component not found at runtime | Path doesn't resolve from the config file's dir | Use a path relative to the config file (auto-anchored), or an absolute path |
| Hooks throw "must be used inside …" | Component rendered without <AuthPageWrapper> | Mount via mountAuthPage(Component) (it wraps for you) or wrap manually in <AuthPageWrapper> |
| extras POST returns 400 (csrf) | The submitted csrf ≠ the server-minted token | Let the hooks send it — useExtraField / submitExtra attach pending_auth_id + csrf automatically |
Custom page can't import @frontmcp/ui/auth | Package (or react/react-dom) not installed | npm install @frontmcp/ui react react-dom |
Examples
Each reference has matching examples under examples/<reference>/:
custom-auth-ui
| Example | Level | Description |
|---|
login-slot | Intermediate | Replace the built-in login page with a custom React component via auth.ui: { login: './login.tsx' } and useAuthFlow. |
multi-step-auth-extra | Advanced | Add a server-validated multi-step field with auth.extras: { 'envs:add': fn }, useExtraField, and useAddedItems. |
Accessing This Skill
Skills are distributed as plain SKILL.md files plus a sibling references/
and examples/ tree, so consumers can pick whichever access mode fits:
| Mode | How it works |
|---|
| Filesystem | Read libs/skills/catalog/frontmcp-auth-ui/ directly from a clone of the catalog repo, or from a published @frontmcp/skills install. SKILL.md is the entry point. |
frontmcp CLI | frontmcp skills list, frontmcp skills read frontmcp-auth-ui, frontmcp skills read frontmcp-auth-ui:references/<file>.md, frontmcp skills install frontmcp-auth-ui — no server required. |
MCP skill:// | When a developer mounts this skill into their own FrontMCP server (@FrontMcp({ skills: [...] })), the SDK exposes it via SEP-2640 resources: skill://frontmcp-auth-ui/SKILL.md, skill://frontmcp-auth-ui/references/{file}.md, etc. The server's skill://index.json returns the SEP-2640 discovery document for everything mounted on it. |
The catalog itself is not an MCP server. The skill:// URIs only resolve
when a server has been configured to host this skill.
Reference