Security Model (BFF Pattern)
Uxopian-ai is a backend microservice designed to run behind a BFF (Backend for Frontend) or API Gateway. It should never be exposed directly to the public internet. This page explains why, how the Gateway works internally, and how to work without it during development.
Why a BFF Gateway?
The uxopian-ai service does not handle authentication itself — by design. This separation provides several advantages:
- Single responsibility: The AI service focuses on prompt resolution and LLM orchestration. The Gateway focuses on security.
- Pluggable authentication: Your organization can use OAuth2, JWT, SAML, LDAP, or any custom mechanism. The AI service does not need to change.
- Token propagation: The Gateway forwards the original user token (
X-User-Token), which Helpers and integrations can use to call other APIs (FlowerDocs, ARender) on behalf of the authenticated user. - Role-based access control: The Gateway maps roles from your identity provider into the
X-User-Rolesheader. The AI service uses this to gate admin operations. - Tenant isolation: The Gateway extracts the organization/scope from the authenticated session and injects it as
X-User-TenantId, enabling full multi-tenancy.
In a typical deployment, three layers are involved:
- Client Application — The user-facing app (ARender, FlowerDocs, a custom web app).
- BFF / Gateway — Authenticates the user and injects identity headers.
- Uxopian-ai Service — Receives pre-authenticated requests and processes them.
The Gateway is the only component exposed to the network.
The Uxopian Gateway
The Uxopian Gateway is a standalone service, built and deployed independently from the AI service. It is built on Spring Cloud Gateway (reactive) and sits in front of the AI service, handling authentication via a pluggable provider system. The built-in auth providers (DevProvider, FlowerDocsProvider, Fast2Provider) are part of the Gateway service.
Request Processing Pipeline
Every request passes through two filters before reaching the backend:
Client Request
│
▼
┌─────────────────────────────┐
│ 1. DefaultProviderHeaderFilter │ ← Matches the request path to a route
│ (HIGHEST_PRECEDENCE) │ and injects X-Provider-ID header
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 2. AuthFilter │ ← Uses the provider to authenticate,
│ (HIGHEST_PRECEDENCE + 1) │ then enriches the request with
│ │ X-User-Id, X-User-Roles,
│ │ X-User-TenantId, X-User-Token
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 3. Spring Cloud Gateway │ ← Routes to the backend service URI
│ (Route + Path Rewrite) │
└─────────────────────────────┘
│
▼
uxopian-ai Service (port 8080)
-
DefaultProviderHeaderFilter— UsesAntPathMatcherto match the incoming request URL against the configured routes. When a match is found, it injects theX-Provider-IDinternal header with the provider name from the route config (e.g.,FlowerDocsProvider,Fast2Provider,DevProvider). -
AuthFilter— Reads theX-Provider-IDheader, loads the correspondingAuthProviderimplementation, and calls itsauthenticate(request)method. The provider returns anAuthenticatedUserwithid,roles,tenantId, andtoken. The filter then enriches the downstream request with these headers:Enriched Header Source X-User-IdAuthenticatedUser.idX-User-RolesAuthenticatedUser.roles(comma-joined)X-User-TenantIdAuthenticatedUser.tenantIdX-User-TokenAuthenticatedUser.tokenThe filter also creates a Spring Security
UsernamePasswordAuthenticationTokenand places it in the reactiveSecurityContext, enabling role-based path security rules. -
Spring Cloud Gateway — Forwards the enriched request to the backend URI (
http://ai-standalone-service:8080).
Pluggable Auth Providers
The Gateway uses a plugin architecture. Auth providers are loaded dynamically from JAR files at startup:
- At boot, the Gateway scans the
provider/directory (configurable viaauth.provider.path) for provider implementations. - Each provider is a Spring
@Servicewith a name (e.g.,@Service("FlowerDocsProvider")). - The route configuration references that name in the
provider:field.
Built-in providers:
| Provider | Authentication Method |
|---|---|
DevProvider |
Reads identity from raw request headers (for development) |
FlowerDocsProvider |
Validates FlowerDocs JWT tokens (from token header or SESSION cookie) |
Fast2Provider |
Decrypts Fast2 JWT tokens via a remote public key |
To integrate with your organization's identity system (Keycloak, Azure AD, custom SSO), you implement the AuthProvider interface and drop the JAR into the provider/ directory. See Adding a Custom Auth Provider.
Gateway Route Configuration
Routes are defined in the Gateway's application.yml:
app:
gateway:
provider-header: X-Provider-ID
routes:
- id: uxopian-ai
uri: http://ai-standalone-service:8080
prefix: /gui/gateway/uxopian-ai/
path: /gui/gateway/uxopian-ai/**
provider: FlowerDocsProvider # ← Which auth provider to use
security:
- path: /.well-known/**
public: true # ← No auth required
- path: /swagger-ui/**
public: true
- path: /prompt/**
roles: [ "ADMIN" ] # ← Requires ADMIN role
- path: /goal/**
roles: [ "ADMIN" ]
Each route maps a URL pattern to a backend service and specifies which auth provider to use and what security rules apply per path.
Authentication Headers
When deploying in production, your Gateway (Uxopian's or your own) must inject the following headers into every request before it reaches uxopian-ai:
| Header | Description | Required |
|---|---|---|
X-User-TenantId |
Isolates data per tenant (organization). | Yes |
X-User-Id |
Unique identifier for the user. | Yes |
X-User-Roles |
Comma-separated list of roles (e.g., admin,user). |
No |
X-User-Token |
Original user token (if needed for downstream context). | No |
The X-User-Roles header controls access to admin endpoints. Operations under /api/v1/admin/* require the admin role. The X-User-Token is propagated to Helpers and integrations that need to call external APIs (e.g., FlowerDocs, ARender) on behalf of the user.
Production Mode
In production, the Gateway intercepts every incoming request, validates the user's credentials, and injects the X-User-* headers. The uxopian-ai service trusts these headers implicitly.
Flow:
- User sends a request to the Gateway.
- Gateway selects the appropriate
AuthProviderbased on the route. - Provider authenticates (OAuth2, JWT, SAML...) and returns an
AuthenticatedUser. - Gateway enriches the request with
X-User-TenantId,X-User-Id,X-User-Roles,X-User-Token. - Gateway forwards the request to uxopian-ai.
- Uxopian-ai reads the headers and establishes the security context (
AiContext). - All data access (conversations, prompts, stats) is scoped to the tenant.
Never expose uxopian-ai directly
Without a Gateway, anyone can forge X-User-* headers and impersonate any user or tenant. The Gateway is what makes these headers trustworthy.
Development Mode
For local development and testing, there are two levels of dev-mode that work together:
1. The DevProvider (Gateway Side)
The DevProvider is a built-in auth provider that trusts raw request headers without any validation. Instead of decrypting a JWT or calling an OAuth endpoint, it simply reads X-User-Id, X-User-Roles, and X-User-Tenant directly from the incoming request.
@Service("DevProvider")
public class DevProvider implements AuthProvider {
@Override
public Mono<AuthenticatedUser> authenticate(ServerHttpRequest request) {
String userId = request.getHeaders().getFirst("X-User-Id");
String rolesRaw = request.getHeaders().getFirst("X-User-Roles");
String tenantId = request.getHeaders().getFirst("X-User-Tenant");
// ... builds AuthenticatedUser from these headers
}
}
To activate it, configure a route with provider: DevProvider in the Gateway's application.yml. This lets you call the Gateway with plain headers (e.g., from curl or Postman) without needing real credentials.
2. The dev Profile (AI Service Side)
When the uxopian-ai service is started with SPRING_PROFILES_ACTIVE=dev, its internal AuthFilter injects default credentials if the X-User-* headers are missing:
- Default Tenant:
Tenant-development - Default User:
User-development
This means you can call the AI service directly (bypassing the Gateway entirely) and it will still work:
# No X-User-* headers needed — dev profile fills in defaults
curl http://localhost:8085/api/v1/conversations
You can also override specific headers while letting others fall back to defaults:
# Custom tenant, default user
curl -H "X-User-TenantId: my-test-tenant" http://localhost:8085/api/v1/conversations
Starter Kit Uses Dev Mode
The Docker Starter Kit ships with SPRING_PROFILES_ACTIVE=dev and no Gateway service. This is why the Quick Start curl commands work directly against localhost:8085 — the dev profile handles the missing headers automatically.
Never use dev in production
The dev profile disables all authentication enforcement. Any request without headers will be accepted as User-development in Tenant-development. Combined with no Gateway, this means zero access control.
When to Use What
| Scenario | Gateway | AI Service Profile | How Auth Works |
|---|---|---|---|
| Production | Uxopian Gateway + real provider (e.g., FlowerDocsProvider) |
Default (no dev) |
Gateway validates credentials, injects headers. AI service rejects requests without headers. |
| Staging / Integration | Uxopian Gateway + DevProvider |
Default (no dev) |
You pass raw headers through the Gateway. AI service still requires headers. |
| Local development | None | dev |
Call the AI service directly. Missing headers get default values. |
Multi-Tenancy
Every piece of data in uxopian-ai is scoped to a Tenant ID:
- Conversations belong to a specific tenant.
- Prompts and Goals can be global or tenant-specific.
- Statistics are aggregated per tenant.
The X-User-TenantId header determines which tenant's data a request can access. A user from tenant-A cannot see conversations from tenant-B, even if they share the same uxopian-ai instance.
This logical separation is enforced at the data layer (OpenSearch queries always include the tenant filter).
Going Further
- Adding a Custom Auth Provider — Implement your own authentication logic for the Gateway.
- Architecture Overview — See the full component and sequence diagrams.
- Configuration Files — Gateway and AI service configuration reference.