Skip to content

Latest commit

 

History

History
316 lines (227 loc) · 10.5 KB

File metadata and controls

316 lines (227 loc) · 10.5 KB

Authentication

This document describes authentication mechanisms for the HyperFleet API.

Overview

HyperFleet API supports the following authentication modes:

  1. Development Mode (No Auth): For local development and testing without authentication
  2. Development with JWT (Google Cloud): Local development with real JWT validation using Google identity tokens
  3. Production Mode (JWT Auth): JWT-based authentication with configurable issuer

Development Mode (No Auth)

For local development and testing, authentication can be disabled.

Usage

# Start service without authentication
make run-no-auth

# Access API without tokens
curl http://localhost:8000/api/hyperfleet/v1/clusters | jq

Configuration

export HYPERFLEET_SERVER_JWT_ENABLED=false
./bin/hyperfleet-api serve

Caller identity in development mode

When JWT is disabled, caller identity resolution is inactive. Audit fields (created_by, updated_by, deleted_by) fall back to system@hyperfleet.local.

When JWT is enabled, mutating requests (POST, PATCH, PUT, DELETE) that cannot resolve a caller identity are rejected with 401 Unauthorized. Read requests (GET, LIST) are allowed without identity.

The identity_header field is per-issuer and only takes effect when JWT is enabled. When configured, a trusted gateway can override the JWT claim by setting this header:

# Mutating request with identity header (JWT must still be valid)
curl -X POST http://localhost:8000/api/hyperfleet/v1/clusters \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-HyperFleet-Identity: dev-user@local" \
  -H "Content-Type: application/json" \
  -d '{"kind":"Cluster","name":"my-cluster","spec":{}}'

# Read requests work without identity
curl http://localhost:8000/api/hyperfleet/v1/clusters | jq

See Caller identity for audit below for full details.

Important: Never disable authentication in production environments.

Development with JWT (Google Cloud)

For local development with real JWT validation, you can use Google Cloud identity tokens. This gives you proper authentication and caller identity without deploying a dedicated identity provider.

Prerequisites

Start the server

Configure via YAML (per-issuer JWT config, no CLI flags for configs):

server:
  jwt:
    enabled: true
    configs:
      - issuer_url: "https://accounts.google.com"
        jwk_cert_url: "https://www.googleapis.com/oauth2/v3/certs"
        header: Authorization
        audience: "32555940559.apps.googleusercontent.com"
        identity_claim: "email"
        identity_claim_pattern: ""
        identity_header: "X-HyperFleet-Identity"
./bin/hyperfleet-api serve --config config.yaml \
  --db-host localhost --db-port 5432 --db-name hyperfleet --db-username hyperfleet

The audience 32555940559.apps.googleusercontent.com is the default gcloud CLI OAuth client ID. It matches the aud claim in tokens generated by gcloud auth print-identity-token.

Generate a token and make requests

# Generate an identity token (valid for ~1 hour)
TOKEN=$(gcloud auth print-identity-token)

# List clusters
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/api/hyperfleet/v1/clusters | jq

# Create a cluster (created_by will be your Google email)
curl -X POST http://localhost:8000/api/hyperfleet/v1/clusters \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"Cluster","name":"my-cluster","spec":{}}'

# Override identity via header (header takes precedence over JWT)
curl -X POST http://localhost:8000/api/hyperfleet/v1/clusters \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-HyperFleet-Identity: gateway-user@corp.com" \
  -d '{"kind":"Cluster","name":"my-cluster-2","spec":{}}'

How it works

Google identity tokens are standard OIDC JWTs signed by Google's keys. The server validates them like any other JWT:

  1. Fetches Google's public keys from the configured jwk_cert_url in the issuer config
  2. Verifies the RS256 signature
  3. Checks iss matches https://accounts.google.com
  4. Checks aud matches the configured audience
  5. Extracts the email claim for caller identity

You can inspect your token with:

gcloud auth print-identity-token | cut -d. -f2 | base64 -d 2>/dev/null | jq

Production Mode (JWT Auth)

Production deployments use JWT-based authentication with a configurable issuer.

Usage

# Start service with authentication
make run

# Access API with a valid JWT
curl -H "Authorization: Bearer ${TOKEN}" \
  http://localhost:8000/api/hyperfleet/v1/clusters

JWT Authentication

HyperFleet API validates JWT tokens using RS256 signature verification.

Token validation checks:

  1. Signature - Token signed by trusted issuer
  2. Issuer - Matches one of the configured issuer_url values in server.jwt.configs
  3. Audience - Matches the audience configured for that issuer
  4. Expiration - Token not expired
  5. Claims - Required claims present

Token format:

Authorization: Bearer <jwt-token>

Example request:

curl -H "Authorization: Bearer ${TOKEN}" \
  http://localhost:8000/api/hyperfleet/v1/clusters

Issuer configuration reference

Each entry in server.jwt.configs supports the following fields:

Field Required Default Description
issuer_url Yes Expected iss claim for this issuer
jwk_cert_url One of jwk_cert_url / jwk_cert_file JWKS endpoint URL for this issuer's public keys
jwk_cert_file One of jwk_cert_url / jwk_cert_file Path to a local JWKS file
header No Authorization HTTP header to read the JWT from
audience No "" (any) Expected aud claim; skipped if empty
identity_claim No email JWT claim used as audit identity
identity_claim_pattern No "" (none) Regex the identity value must match; non-matching requests get 401
identity_header No "" (disabled) HTTP header that overrides the JWT claim for audit identity (gateway-set only)

Complete example with all fields:

server:
  jwt:
    enabled: true
    configs:
      - issuer_url: https://idp.example.com/realms/hyperfleet
        jwk_cert_url: https://idp.example.com/realms/hyperfleet/protocol/openid-connect/certs
        jwk_cert_file: ""
        header: Authorization
        audience: ""
        identity_claim: email
        identity_claim_pattern: ""
        identity_header: ""

Caller identity for audit

Authentication (JWT validation) and caller identity (audit attribution) are separate concerns. When JWT is enabled, identity resolution is always active because identity_claim defaults to email. Audit fields (created_by, updated_by, deleted_by) are populated from the matched issuer's identity settings. When JWT is disabled, audit fields fall back to system@hyperfleet.local.

Layer Component Responsibility
Outer JWTHandler Validates Authorization: Bearer token
Inner ResolveCallerIdentity middleware Resolves who is recorded as the actor

The resolved identity is written to created_by on create, updated_by on update, and deleted_by on delete. Precedence: per-issuer identity header > JWT claim.

When identity resolution is configured, mutating requests (POST, PATCH, PUT, DELETE) that cannot resolve a caller identity are rejected with 401 Unauthorized. Read requests (GET, LIST) are allowed without identity.

JWT claim (per-issuer)

Configure which JWT claim is used as the caller identity for each issuer:

server:
  jwt:
    configs:
      - issuer_url: https://idp.example.com
        jwk_cert_url: https://idp.example.com/certs
        header: Authorization
        audience: ""
        identity_claim: email   # or preferred_username, sub, etc.
        identity_claim_pattern: ""
        identity_header: ""

HTTP identity header (per-issuer, optional)

When identity_header is set on an issuer config, a trusted gateway can set the caller identity via HTTP header. If the header is present and non-empty, it overrides the JWT claim for audit fields. JWT validation is still required.

server:
  jwt:
    configs:
      - issuer_url: https://idp.example.com
        jwk_cert_url: https://idp.example.com/certs
        header: Authorization
        audience: ""
        identity_claim: email
        identity_claim_pattern: ""
        identity_header: X-HyperFleet-Identity

Security: Clients must not be able to set this header directly. Configure your ingress/gateway to strip the header from external requests and set it from the authenticated upstream user.

Identity values from both sources are validated: trimmed of whitespace, limited to 256 characters, and rejected if they contain control characters.

Configuration

Environment Variables

# Development (no auth)
export HYPERFLEET_SERVER_JWT_ENABLED=false

# Production (with auth) - configure issuers in config.yaml
export HYPERFLEET_SERVER_JWT_ENABLED=true
# Issuer configs must be set via YAML config file (server.jwt.configs list)

See Deployment for complete configuration options.

Kubernetes Deployment

Configure via Helm values:

# values.yaml — see "Issuer configuration reference" above for all fields
config:
  server:
    jwt:
      enabled: true
      configs:
        - issuer_url: https://your-idp.example.com/auth/realms/your-realm
          jwk_cert_url: https://your-idp.example.com/auth/realms/your-realm/protocol/openid-connect/certs
          audience: https://your-api.example.com

Deploy:

helm install hyperfleet-api oci://quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api-chart:<tag> --values values.yaml

Note: You may also choose to install from the ./charts folder, if you've cloned this repository locally.

Troubleshooting

Common Issues

401 Unauthorized

  • Check token is valid and not expired
  • Verify issuer_url and audience in server.jwt.configs match token claims
  • Ensure Authorization header is correctly formatted

Token debugging

# Decode JWT token (header and payload only, not verified)
echo $TOKEN | cut -d. -f2 | base64 -d | jq

# Check token expiration
echo $TOKEN | cut -d. -f2 | base64 -d | jq '.exp | todate'

Related Documentation

  • Deployment - Authentication configuration and Kubernetes setup