veylant/web/src/pages/docs/api-reference/AuthenticationPage.tsx
2026-02-27 23:33:07 +01:00

134 lines
4.8 KiB
XML

import { CodeBlock } from "../components/CodeBlock";
import { Callout } from "../components/Callout";
export function AuthenticationPage() {
return (
<div>
<h1 id="authentication">Authentication</h1>
<p>
All <code>/v1/*</code> endpoints require a Bearer JWT in the{" "}
<code>Authorization</code> header. Veylant IA validates the token against Keycloak (OIDC)
or uses a mock verifier in development mode.
</p>
<h2 id="bearer-token">Bearer Token</h2>
<CodeBlock
language="bash"
code={`curl http://localhost:8090/v1/chat/completions \\
-H "Authorization: Bearer <your-access-token>" \\
-H "Content-Type: application/json" \\
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hi"}]}'`}
/>
<h2 id="dev-mode">Development Mode</h2>
<Callout type="info" title="Development mode auth bypass">
When <code>server.env=development</code> and Keycloak is unreachable, the proxy uses a{" "}
<code>MockVerifier</code>. Any non-empty Bearer token is accepted. The authenticated user
is injected as <code>admin@veylant.dev</code> with <code>admin</code> role and tenant ID{" "}
<code>dev-tenant</code>.
</Callout>
<CodeBlock
language="bash"
code={`# Any string works as the token in dev mode
curl http://localhost:8090/v1/chat/completions \\
-H "Authorization: Bearer dev-token" \\
...`}
/>
<h2 id="keycloak-flow">Production: Keycloak OIDC Flow</h2>
<p>In production, clients obtain a token via the standard OIDC Authorization Code flow:</p>
<ol>
<li>Redirect user to Keycloak login page</li>
<li>User authenticates; Keycloak redirects back with an authorization code</li>
<li>Exchange code for tokens at the token endpoint</li>
<li>Use the <code>access_token</code> as the Bearer token</li>
</ol>
<CodeBlock
language="bash"
code={`# Token endpoint (replace values)
curl -X POST \\
http://localhost:8080/realms/veylant/protocol/openid-connect/token \\
-d "grant_type=password" \\
-d "client_id=veylant-proxy" \\
-d "username=admin@veylant.dev" \\
-d "password=admin123"
# Response includes:
{
"access_token": "eyJhbGci...",
"expires_in": 300,
"refresh_token": "eyJhbGci...",
"token_type": "Bearer"
}`}
/>
<h2 id="jwt-claims">JWT Claims</h2>
<p>The proxy extracts the following claims from the JWT:</p>
<div className="overflow-x-auto my-4">
<table className="w-full text-sm border rounded-lg overflow-hidden">
<thead>
<tr className="bg-muted/50 border-b">
<th className="text-left px-4 py-2.5 font-semibold">Claim</th>
<th className="text-left px-4 py-2.5 font-semibold">Source</th>
<th className="text-left px-4 py-2.5 font-semibold">Description</th>
</tr>
</thead>
<tbody>
{[
{ claim: "sub", source: "Standard JWT", desc: "User ID (UUID)" },
{ claim: "email", source: "Standard JWT", desc: "User email" },
{ claim: "realm_access.roles", source: "Keycloak extension", desc: "RBAC roles: admin, manager, user, auditor" },
{ claim: "veylant_tenant_id", source: "Keycloak mapper", desc: "Tenant UUID" },
{ claim: "department", source: "Keycloak user attribute", desc: "Department name for routing rules" },
].map((row) => (
<tr key={row.claim} className="border-b last:border-0">
<td className="px-4 py-2.5 font-mono text-xs">{row.claim}</td>
<td className="px-4 py-2.5 text-muted-foreground text-xs">{row.source}</td>
<td className="px-4 py-2.5 text-muted-foreground text-xs">{row.desc}</td>
</tr>
))}
</tbody>
</table>
</div>
<h2 id="test-users">Pre-configured Test Users</h2>
<p>The Keycloak realm export includes these users for testing:</p>
<CodeBlock
language="bash"
code={`# Admin user (full access)
username: admin@veylant.dev
password: admin123
roles: admin
# Regular user (restricted to allowed models)
username: user@veylant.dev
password: user123
roles: user`}
/>
<h2 id="error-responses">Auth Error Responses</h2>
<p>Authentication errors always return OpenAI-format JSON:</p>
<CodeBlock
language="json"
code={`// 401 Missing or invalid token
{
"error": {
"type": "authentication_error",
"message": "invalid or missing authorization token",
"code": "invalid_api_key"
}
}
// 403 Valid token, insufficient role
{
"error": {
"type": "permission_error",
"message": "role 'user' does not have access to model 'gpt-4o'. Allowed: gpt-4o-mini",
"code": "permission_denied"
}
}`}
/>
</div>
);
}