Authentication
The AEGIS API uses a two-step authentication flow: exchange email/password credentials for a JWT token, then use the token on subsequent requests. The Go API gateway enforces authentication on every request except health checks and the token endpoint itself.
Authentication Flow
1. Client sends email + password to POST /api/v1/auth/token
2. Auth service verifies credentials (bcrypt), returns JWT
3. Client includes JWT in Authorization header (or aegis_token cookie) on all subsequent requests
4. Gateway validates JWT on each request before proxying to backendObtaining a JWT Token
Exchange email and password credentials for a JWT token by calling the token endpoint. This endpoint does not require prior authentication.
curl -X POST http://localhost:8000/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"email": "admin@aegis.local", "password": "aegis-dev-admin"}'Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | The user’s email address (matched case-insensitively) |
password | string | Yes | The user’s password |
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400,
"user_id": "dev-user",
"roles": ["admin", "operator", "reviewer"]
}| Field | Type | Description |
|---|---|---|
access_token | string | The JWT to use in the Authorization header |
token_type | string | Always "bearer" |
expires_in | integer | Token lifetime in seconds (86400 = 24 hours) |
user_id | string | The authenticated user’s ID |
roles | string[] | Roles granted to this user |
If the email or password is incorrect (or the user is inactive), the endpoint returns 401 Unauthorized with {"detail": "Invalid email or password"}.
Using the JWT Token
Include the JWT in the Authorization header with the Bearer scheme on all subsequent requests:
curl http://localhost:8000/api/v1/conversations \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."For browser clients (SSE/EventSource), the gateway also accepts the JWT from the aegis_token cookie set at login, so no Authorization header is required.
# Step 1: Get a token
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"email": "admin@aegis.local", "password": "aegis-dev-admin"}' | jq -r '.access_token')
# Step 2: Use the token
curl http://localhost:8000/api/v1/conversations \
-H "Authorization: Bearer $TOKEN"Token Details
JWT Claims
The JWT payload contains the following claims:
| Claim | Description |
|---|---|
user_id | The authenticated user’s identifier |
email | The authenticated user’s email address |
roles | Array of role strings (e.g., ["admin", "operator", "reviewer"]) |
iss | Issuer — "aegis-auth" for tokens from the auth service |
iat | Issued-at timestamp (Unix epoch) |
exp | Expiration timestamp (Unix epoch) |
Token Expiry
Tokens expire 24 hours after issuance. When a token expires, the gateway returns 401 Unauthorized. Request a new token by calling POST /api/v1/auth/token again.
Signing Algorithm
Tokens are signed with HS256 (HMAC-SHA256) using a shared secret configured via the JWT_SECRET environment variable.
Token Validation
The gateway validates tokens locally using the shared JWT secret. It does not call the auth service on every request. The auth service also exposes an internal validation endpoint used during initial gateway setup:
curl -X POST http://localhost:8000/api/v1/auth/validate \
-H "Content-Type: application/json" \
-d '"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."'Response:
{
"valid": true,
"user_id": "dev-user",
"roles": ["admin", "operator", "reviewer"]
}Role-Based Access
Some endpoints require specific roles. The gateway checks roles from the JWT claims before proxying the request:
| Endpoint Pattern | Required Role |
|---|---|
/api/v1/admin/* | admin |
/api/v1/detection-rules/* | power_user or admin |
| All other endpoints | Any authenticated user |
If the user’s roles do not include the required role, the gateway returns 403 Forbidden:
{
"error": "admin role required"
}Public Endpoints
The following endpoints do not require authentication:
| Endpoint | Description |
|---|---|
GET /health | Gateway health check |
POST /api/v1/auth/token | Token exchange |
GET /api/v1/entity-types* | Public entity type listings |
GET /api/v1/relationship-rules* | Public relationship rule listings |
GET /api/v1/relationship-types* | Public relationship type listings |
User Provisioning
Accounts are admin-provisioned — there is no self-serve signup. Users live in the users table (see the auth-service page) and are created via a CLI:
cd services/auth-service
poetry run python -m auth_service.create_user \
--email alice@example.com \
--roles admin,operator,reviewerThe command prompts for a password (or reads it from AEGIS_NEW_USER_PASSWORD). Re-running it for an existing email rotates that user’s password.
Bootstrap Admin
On startup the auth service seeds an initial admin from the BOOTSTRAP_ADMIN_EMAIL and BOOTSTRAP_ADMIN_PASSWORD environment variables, if a user with that email does not already exist. For local development these default to admin@aegis.local / aegis-dev-admin.
| Password | Roles | |
|---|---|---|
admin@aegis.local | aegis-dev-admin | admin, operator, reviewer |
The default bootstrap admin credentials must never be used in production. Set strong BOOTSTRAP_ADMIN_EMAIL / BOOTSTRAP_ADMIN_PASSWORD values and a real JWT_SECRET via environment variables before deploying.
Security Notes
- Passwords are hashed with bcrypt before storage; plaintext passwords are never persisted.
- Email matching is case-insensitive (enforced by a
LOWER(email)unique index). - The
JWT_SECRETmust be set via an environment variable in production. - CORS is configured to allow all origins (
*). Restrict this in production deployments.