Skip to main content

Overview

The Service Principals API provides endpoints for creating and managing service principals - non-human identities for machine-to-machine authentication. Service principals enable automated processes, batch jobs, and integrations to authenticate securely without requiring user credentials.
v2.8.0 adds comprehensive service principal management with permission inheritance and OpenFGA integration.

Use Cases

  • Batch Jobs: ETL processes, data synchronization, scheduled tasks
  • CI/CD Pipelines: Automated deployments, testing, infrastructure provisioning
  • Microservices: Service-to-service authentication
  • Integrations: Third-party system integrations, webhooks

Authentication Modes

  • Client Credentials
  • Service Account User
Standard OAuth2 client credentials flow
  • Service authenticates with service_id + client_secret
  • Receives JWT token for API access
  • No user association required
POST /auth/token
{
  "grant_type": "client_credentials",
  "client_id": "batch-etl-job",
  "client_secret": "secret_abc123..."
}

Base URL

https://api.yourdomain.com/api/v1/service-principals

Authentication

All endpoints require user authentication:
  • Authorization: Bearer {token}
  • Users can only manage service principals they own

Endpoints

POST /

Create a new service principal. Creates a service principal with the specified authentication mode. The calling user becomes the owner of the service principal. Request Body:
name
string
required
Human-readable name for the service (e.g., “Batch ETL Job”)
description
string
required
Purpose/description of the service
authentication_mode
string
default:"client_credentials"
Authentication mode: client_credentials or service_account_user
associated_user_id
string
User ID to act as for permission inheritance (e.g., user:alice). Required if authentication_mode is service_account_user.
inherit_permissions
boolean
default:false
Whether to inherit permissions from associated user
Request Example:
curl -X POST https://api.yourdomain.com/api/v1/service-principals/ \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Batch ETL Job",
    "description": "Nightly data processing pipeline",
    "authentication_mode": "client_credentials",
    "associated_user_id": "user:alice",
    "inherit_permissions": true
  }'
```bash
```python Python
import httpx

response = httpx.post(
    "https://api.yourdomain.com/api/v1/service-principals/",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "name": "Batch ETL Job",
        "description": "Nightly data processing pipeline",
        "authentication_mode": "client_credentials",
        "associated_user_id": "user:alice",
        "inherit_permissions": True
    }
)

data = response.json()
print(f"Service ID: {data['service_id']}")
print(f"Client Secret: {data['client_secret']}")
# IMPORTANT: Save the client_secret securely!
Response:
{
  "service_id": "batch-etl-job",
  "name": "Batch ETL Job",
  "description": "Nightly data processing pipeline",
  "authentication_mode": "client_credentials",
  "associated_user_id": "user:alice",
  "owner_user_id": "user:bob",
  "inherit_permissions": true,
  "enabled": true,
  "created_at": "2025-10-29T10:00:00Z",
  "client_secret": "sp_secret_abc123def456...",
  "message": "Service principal created successfully. Save the client_secret securely."
}
Save the client_secret securely - it will not be shown again! Store it in a secret manager (e.g., HashiCorp Vault, AWS Secrets Manager, Azure Key Vault).
Status Codes:
201
Created
Service principal created successfully
400
Bad Request
Invalid authentication mode or missing required fields
{
  "error": "validation_error",
  "message": "Invalid authentication_mode. Must be 'client_credentials' or 'service_account_user'"
}
401
Unauthorized
Not authenticated
409
Conflict
Service principal with this ID already exists
{
  "error": "conflict",
  "message": "Service principal with ID 'batch-etl-job' already exists"
}

GET /

List service principals owned by the current user. Returns all service principals where the current user is the owner. Does not include client secrets. Request Example:
curl https://api.yourdomain.com/api/v1/service-principals/ \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response:
[
  {
    "service_id": "batch-etl-job",
    "name": "Batch ETL Job",
    "description": "Nightly data processing pipeline",
    "authentication_mode": "client_credentials",
    "associated_user_id": "user:alice",
    "owner_user_id": "user:bob",
    "inherit_permissions": true,
    "enabled": true,
    "created_at": "2025-10-29T10:00:00Z"
  },
  {
    "service_id": "ci-cd-pipeline",
    "name": "CI/CD Pipeline",
    "description": "Automated deployment service",
    "authentication_mode": "service_account_user",
    "associated_user_id": "user:bob",
    "owner_user_id": "user:bob",
    "inherit_permissions": true,
    "enabled": true,
    "created_at": "2025-10-28T15:00:00Z"
  }
]
Status Codes:
200
Success
Service principals retrieved successfully
401
Unauthorized
Not authenticated

GET /

Get details of a specific service principal. Returns service principal details if the current user is the owner. Path Parameters:
service_id
string
required
Unique identifier of the service principal
Request Example:
curl https://api.yourdomain.com/api/v1/service-principals/batch-etl-job \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response:
{
  "service_id": "batch-etl-job",
  "name": "Batch ETL Job",
  "description": "Nightly data processing pipeline",
  "authentication_mode": "client_credentials",
  "associated_user_id": "user:alice",
  "owner_user_id": "user:bob",
  "inherit_permissions": true,
  "enabled": true,
  "created_at": "2025-10-29T10:00:00Z"
}
Status Codes:
200
Success
Service principal details retrieved
401
Unauthorized
Not authenticated
403
Forbidden
Not the owner of this service principal
{
  "error": "forbidden",
  "message": "You do not have permission to view this service principal"
}
404
Not Found
Service principal not found
{
  "error": "not_found",
  "message": "Service principal 'batch-etl-job' not found"
}

POST //rotate-secret

Rotate service principal secret. Generates a new client secret for the service principal. The old secret is invalidated immediately. Path Parameters:
service_id
string
required
Unique identifier of the service principal
Request Example:
curl -X POST https://api.yourdomain.com/api/v1/service-principals/batch-etl-job/rotate-secret \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
```bash
```python Python
import httpx

response = httpx.post(
    "https://api.yourdomain.com/api/v1/service-principals/batch-etl-job/rotate-secret",
    headers={"Authorization": f"Bearer {token}"}
)

data = response.json()
print(f"New Client Secret: {data['client_secret']}")
# IMPORTANT: Update your service configuration immediately!
Response:
{
  "service_id": "batch-etl-job",
  "client_secret": "sp_secret_xyz789new...",
  "message": "Secret rotated successfully. Update your service configuration."
}
Update your service configuration immediately! The old secret is invalidated and will no longer work. Save the new client_secret securely.
Status Codes:
200
Success
Secret rotated successfully
401
Unauthorized
Not authenticated
403
Forbidden
Not the owner of this service principal
404
Not Found
Service principal not found

DELETE /

Delete a service principal. Permanently deletes the service principal from Keycloak and OpenFGA. This action cannot be undone. Path Parameters:
service_id
string
required
Unique identifier of the service principal
Request Example:
curl -X DELETE https://api.yourdomain.com/api/v1/service-principals/batch-etl-job \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response: No content (HTTP 204) Status Codes:
204
No Content
Service principal deleted successfully
401
Unauthorized
Not authenticated
403
Forbidden
Not the owner of this service principal
404
Not Found
Service principal not found

POST //associate-user

Associate service principal with a user for permission inheritance. Links a service principal to a user, optionally enabling permission inheritance. When inherit_permissions is true, the service principal can act on behalf of the user and inherit all their permissions. Path Parameters:
service_id
string
required
Unique identifier of the service principal
Query Parameters:
user_id
string
required
User ID to associate (e.g., user:alice)
inherit_permissions
boolean
default:true
Whether to inherit permissions from the user
Request Example:
curl -X POST 'https://api.yourdomain.com/api/v1/service-principals/batch-etl-job/associate-user?user_id=user:alice&inherit_permissions=true' \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response:
{
  "service_id": "batch-etl-job",
  "name": "Batch ETL Job",
  "description": "Nightly data processing pipeline",
  "authentication_mode": "client_credentials",
  "associated_user_id": "user:alice",
  "owner_user_id": "user:bob",
  "inherit_permissions": true,
  "enabled": true,
  "created_at": "2025-10-29T10:00:00Z"
}
Status Codes:
200
Success
User association updated successfully
401
Unauthorized
Not authenticated
403
Forbidden
Not the owner of this service principal
404
Not Found
Service principal not found

Using Service Principals

1. Create Service Principal

# Create service principal
curl -X POST https://api.yourdomain.com/api/v1/service-principals/ \
  -H "Authorization: Bearer $USER_TOKEN" \
  -d '{
    "name": "My Batch Job",
    "description": "Data processing service",
    "authentication_mode": "client_credentials"
  }'

# Save the response (contains client_secret)

2. Authenticate as Service Principal

# Exchange credentials for JWT token
curl -X POST https://api.yourdomain.com/auth/token \
  -d grant_type=client_credentials \
  -d client_id=my-batch-job \
  -d client_secret=$CLIENT_SECRET

# Response:
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

3. Make API Requests

# Use the access token for API requests
curl https://api.yourdomain.com/message \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{"query": "Process data"}'

Security Best Practices

Never hardcode secrets in code or configuration files
  • Use secret managers: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault
  • Inject secrets at runtime via environment variables
  • Rotate secrets regularly (quarterly or after security incidents)
  • Use separate service principals for different environments
Grant minimum required permissions
  • Create service principals with specific, limited scopes
  • Use permission inheritance only when necessary
  • Associate with users who have minimal required permissions
  • Regularly audit service principal permissions
Implement zero-downtime secret rotation
  1. Generate new secret using rotate-secret endpoint
  2. Update service configuration with new secret
  3. Restart service to use new credentials
  4. Old secret is invalidated immediately
Automate rotation with tools like cert-manager or custom automation.
Track service principal activity
  • Enable audit logging for all service principal operations
  • Monitor authentication attempts and failures
  • Alert on unusual activity patterns
  • Review service principal usage quarterly
  • Disable unused service principals


Production Ready: Service principals support enterprise authentication patterns with Keycloak and OpenFGA!