Skip to main content

Authentication API

Authentication is handled by Keycloak. The backend validates JWT tokens for all API requests.

Token Endpoints (Keycloak)​

Get Access Token (Authorization Code)​

Used by the frontend with PKCE flow.

POST /realms/openprime/protocol/openid-connect/token
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=openprime-app
&code=<authorization_code>
&redirect_uri=http://localhost:3000/callback
&code_verifier=<pkce_verifier>

Get Access Token (Client Credentials)​

For service-to-service or CLI access:

POST /realms/openprime/protocol/openid-connect/token
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=openprime-cli
&client_secret=<client_secret>

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "uuid",
"scope": "openid profile email"
}

Refresh Token​

POST /realms/openprime/protocol/openid-connect/token
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&client_id=openprime-app
&refresh_token=<refresh_token>

Introspect Token​

Validate token (server-side):

POST /realms/openprime/protocol/openid-connect/token/introspect
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>

token=<access_token>

Response:

{
"active": true,
"sub": "user-uuid",
"preferred_username": "john.doe",
"email": "john.doe@example.com",
"email_verified": true,
"exp": 1699999999,
"iat": 1699996399,
"client_id": "openprime-app"
}

Logout​

POST /realms/openprime/protocol/openid-connect/logout
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

client_id=openprime-app
&refresh_token=<refresh_token>

User Info​

Get Current User​

Fetch the authenticated user's profile.

GET /api/users/me
Authorization: Bearer <access_token>

Response:

{
"data": {
"id": "uuid",
"keycloakId": "keycloak-uuid",
"username": "john.doe",
"email": "john.doe@example.com",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
}

Update Current User​

PUT /api/users/me
Authorization: Bearer <access_token>
Content-Type: application/json

{
"preferences": {
"theme": "dark",
"defaultProvider": "aws"
}
}

Token Claims​

The access token contains these claims:

{
"exp": 1699999999,
"iat": 1699996399,
"jti": "unique-token-id",
"iss": "http://localhost:8080/realms/openprime",
"aud": "account",
"sub": "user-uuid",
"typ": "Bearer",
"azp": "openprime-app",
"session_state": "session-uuid",
"acr": "1",
"realm_access": {
"roles": ["user", "admin"]
},
"resource_access": {
"openprime-app": {
"roles": ["environment-admin"]
}
},
"scope": "openid profile email",
"sid": "session-id",
"email_verified": true,
"preferred_username": "john.doe",
"given_name": "John",
"family_name": "Doe",
"email": "john.doe@example.com"
}

Error Responses​

401 Unauthorized​

Missing or invalid token:

{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
}

403 Forbidden​

Valid token but insufficient permissions:

{
"error": {
"code": "FORBIDDEN",
"message": "Insufficient permissions"
}
}

Token Expired​

{
"error": {
"code": "TOKEN_EXPIRED",
"message": "Access token has expired"
}
}

CLI Authentication​

For command-line tools, use device authorization flow or client credentials:

Device Authorization​

# 1. Request device code
curl -X POST \
"http://localhost:8080/realms/openprime/protocol/openid-connect/auth/device" \
-d "client_id=openprime-cli"

# Response includes verification_uri and user_code

# 2. User visits URL and enters code

# 3. Poll for token
curl -X POST \
"http://localhost:8080/realms/openprime/protocol/openid-connect/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "client_id=openprime-cli" \
-d "device_code=<device_code>"

Security Best Practices​

  1. Store tokens securely - Never in localStorage for sensitive apps
  2. Use short expiry - Access tokens expire in 5 minutes
  3. Validate on every request - Backend validates JWT signature
  4. Use HTTPS - Always in production
  5. Implement token refresh - Before expiry to avoid interruptions