Skip to content

Authentication

techdox edited this page Jul 14, 2026 · 3 revisions

Authentication

Trove's default mode is still simple: if OIDC is not configured, the dashboard and read APIs are open. In that mode, keep the server on a trusted LAN, VPN, tailnet, or behind an authenticating reverse proxy.

For native login, Trove supports OpenID Connect (OIDC). This protects the dashboard and read-only APIs without changing the agent ingest path.

What is protected

When OIDC is enabled, these routes require a valid OIDC browser session or the optional API bearer token:

Method Path Notes
GET / Dashboard. Browser users are redirected to the IdP.
GET /api/v1/services Service inventory.
GET /api/v1/agents Agent heartbeat/status data.
GET /api/v1/events State-change event feed.
GET /api/v1/me Current auth state used by the dashboard user chip.
GET /metrics Prometheus metrics.

When OIDC is enabled, these routes are registered but stay unauthenticated:

Method Path Why
POST /api/v1/report Agent ingest uses each agent's own bearer token.
GET /healthz Health checks need to work before auth.
GET /oauth2/login Starts login.
GET /oauth2/callback Receives the IdP callback.
POST /oauth2/logout Clears the local session and redirects through IdP logout when available.

Server environment variables

Variable Required Purpose
TROVE_OIDC_ISSUER Yes OIDC issuer/discovery URL, for example https://auth.example.com/application/o/trove/.
TROVE_OIDC_CLIENT_ID Yes OAuth2/OIDC client ID.
TROVE_OIDC_CLIENT_SECRET Yes OAuth2/OIDC client secret.
TROVE_OIDC_REDIRECT_URL Yes Callback URL, for example https://trove.example.com/oauth2/callback.
TROVE_API_TOKEN No Static bearer token for scripts that cannot use browser OIDC.
TROVE_OIDC_SESSION_MAX_AGE No Dashboard session lifetime. Default is 8h. Uses Go duration syntax.

Example:

TROVE_OIDC_ISSUER=https://auth.example.com/application/o/trove/
TROVE_OIDC_CLIENT_ID=trove
TROVE_OIDC_CLIENT_SECRET=OIDC_CLIENT_SECRET_VALUE
TROVE_OIDC_REDIRECT_URL=https://trove.example.com/oauth2/callback
TROVE_API_TOKEN=TROVE_API_TOKEN_VALUE
TROVE_OIDC_SESSION_MAX_AGE=8h

The session cookie is named trove_session. It is signed, HttpOnly, SameSite=Lax, and marked Secure when the configured redirect URL uses HTTPS.

Authentik setup

Create an OAuth2/OpenID provider for Trove.

Recommended settings:

Setting Value
Provider type OAuth2/OpenID Provider
Client type Confidential
Redirect URI https://trove.example.com/oauth2/callback
Scopes openid, profile, email

Also allow the dashboard root as a post-logout redirect/return URI:

https://trove.example.com/oauth2/callback
https://trove.example.com/

That second URL matters for logout. Without it, the provider may reject Trove's post_logout_redirect_uri or leave the browser stranded after IdP logout.

Login flow

  1. A browser requests the dashboard or a read API.
  2. Trove sees no valid trove_session cookie and redirects to /oauth2/login.
  3. Trove redirects to the provider authorization endpoint.
  4. The provider redirects back to /oauth2/callback.
  5. Trove validates the state cookie, exchanges the code, verifies the ID token, and sets a signed session cookie.
  6. The browser returns to the requested path.

API-style requests get 401 JSON instead of a browser redirect when they send an Authorization header or do not request text/html.

Logout flow

The dashboard logout button submits a real POST form to /oauth2/logout.

On logout, Trove:

  1. clears the local trove_session cookie;
  2. reads the provider's discovered OIDC end_session_endpoint;
  3. redirects the browser there with client_id and post_logout_redirect_uri;
  4. falls back to the dashboard root if the provider does not publish a valid logout endpoint.

With Authentik, that redirect usually looks like this:

https://auth.example.com/application/o/trove/end-session/?client_id=trove&post_logout_redirect_uri=https%3A%2F%2Ftrove.example.com%2F

This prevents the old "logout boomerang" where Trove cleared its cookie, redirected home, then Authentik silently logged the user straight back in because the upstream SSO session still existed.

Programmatic read API access

Set TROVE_API_TOKEN and pass it as a bearer token:

TROVE_API_TOKEN=TROVE_API_TOKEN_VALUE \
  curl --oauth2-bearer "$TROVE_API_TOKEN" \
  https://trove.example.com/api/v1/services

This token only bypasses OIDC for dashboard/read APIs. Agents still use their own per-agent tokens for POST /api/v1/report.

Verification

Unauthenticated browser request should redirect:

curl -I -H 'Accept: text/html' https://trove.example.com/

Unauthenticated API request should return 401:

curl -i https://trove.example.com/api/v1/services

Bearer-token API request should return JSON:

TROVE_API_TOKEN=TROVE_API_TOKEN_VALUE \
  curl --oauth2-bearer "$TROVE_API_TOKEN" \
  https://trove.example.com/api/v1/services

Logout should redirect to the provider end-session endpoint:

curl -i -X POST https://trove.example.com/oauth2/logout

Look for 303 See Other and a Location header pointing at the provider logout endpoint.

Troubleshooting

Login callback fails

Make sure TROVE_OIDC_REDIRECT_URL exactly matches the provider's redirect URI. Scheme, host, path, and trailing slash behaviour all count.

Logout signs straight back in

Check that the provider publishes end_session_endpoint in discovery metadata and allows the dashboard root as a post-logout redirect URI.

curl https://auth.example.com/application/o/trove/.well-known/openid-configuration

API scripts are redirected to login

Send an Authorization header with TROVE_API_TOKEN, or at least send Accept: application/json so Trove treats the request as an API request.

Health checks fail

Point health checks at /healthz. It is intentionally unauthenticated.

Server fails during OIDC startup

Check all four required variables together. Trove validates provider discovery at startup, so an unreachable issuer, wrong issuer URL, or invalid discovery document prevents the server from starting with half-working authentication. Check docker compose logs server or journalctl -u trove-server -e for the exact discovery error.

Clone this wiki locally