Authentication
Infracast uses JWT (JSON Web Tokens) for API authentication. All API requests (except health checks) require a valid token.
Getting a Token
Login Flow
Exchange username/password for an access token:
curl -X POST https://api.infracast.io/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"username": "your-username",
"password": "your-password"
}'
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2024-03-16T10:00:00Z",
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
"user": {
"id": "user-123",
"username": "your-username",
"email": "you@company.com",
"roles": ["admin"]
}
}
Token Lifetime
| Token Type | Lifetime | Use |
|---|---|---|
| Access Token | 1 hour | API requests |
| Refresh Token | 7 days | Get new access token |
Using Tokens
Include the token in the Authorization header:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://api.infracast.io/api/v1/tenants/my-tenant/nodes
Python Example
import requests
# Get token
auth_response = requests.post(
"https://api.infracast.io/api/v1/auth/token",
json={"username": "admin", "password": "secret"}
)
token = auth_response.json()["token"]
# Use token
headers = {"Authorization": f"Bearer {token}"}
nodes = requests.get(
"https://api.infracast.io/api/v1/tenants/my-tenant/nodes",
headers=headers
)
JavaScript Example
// Get token
const authResponse = await fetch('https://api.infracast.io/api/v1/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'secret' })
});
const { token } = await authResponse.json();
// Use token
const nodes = await fetch('https://api.infracast.io/api/v1/tenants/my-tenant/nodes', {
headers: { 'Authorization': `Bearer ${token}` }
});
Refreshing Tokens
When your access token expires, use the refresh token to get a new one:
curl -X POST https://api.infracast.io/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}'
Response (new access token):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2024-03-16T11:00:00Z"
}
Refresh your token proactively before it expires (e.g., when 5 minutes remain). This prevents request failures due to expired tokens.
API Keys (Service Accounts)
For server-to-server integrations, use API keys instead of user tokens:
Create an API Key
curl -X POST https://api.infracast.io/api/v1/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-cd-integration",
"tenants": ["my-tenant"],
"permissions": ["nodes:read", "findings:read"],
"expires_at": "2025-03-16T00:00:00Z"
}'
Response:
{
"id": "key-abc123",
"name": "ci-cd-integration",
"key": "ic_live_abc123xyz789...",
"created_at": "2024-03-16T00:00:00Z",
"expires_at": "2025-03-16T00:00:00Z"
}
The API key is only shown once. Store it securely (e.g., in a secrets manager). It cannot be retrieved later.
Using API Keys
API keys are used the same way as tokens:
curl -H "Authorization: Bearer ic_live_abc123xyz789..." \
https://api.infracast.io/api/v1/tenants/my-tenant/nodes
Key Permissions
| Permission | Description |
|---|---|
nodes:read | List and get nodes |
nodes:write | Create/update/delete nodes |
findings:read | List and get findings |
findings:write | Accept risk, resolve findings |
jobs:read | List and get jobs |
jobs:write | Create, start, cancel jobs |
reports:read | Generate and download reports |
admin:* | Full admin access |
SSO / OAuth 2.0
Infracast supports enterprise SSO providers:
Okta
# Configure Okta integration
POST /api/v1/admin/sso/okta
{
"domain": "your-company.okta.com",
"client_id": "0oaxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
Users can then authenticate via Okta:
GET /api/v1/auth/sso/okta/login
→ Redirects to Okta login
→ Returns with authorization code
→ Exchange code for Infracast token
Azure AD
POST /api/v1/admin/sso/azure
{
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
SAML 2.0
For SAML providers:
POST /api/v1/admin/sso/saml
{
"entity_id": "https://idp.company.com",
"sso_url": "https://idp.company.com/saml/sso",
"certificate": "-----BEGIN CERTIFICATE-----\n..."
}
Multi-Tenant Access
Users can have different roles across tenants:
{
"user": {
"id": "user-123",
"tenants": [
{ "id": "prod", "role": "admin" },
{ "id": "staging", "role": "viewer" },
{ "id": "dev", "role": "editor" }
]
}
}
Token permissions are scoped to the requested tenant.
Security Best Practices
Token Storage
| Environment | Recommendation |
|---|---|
| Browser | HttpOnly cookie or secure storage |
| Mobile App | Secure keychain/keystore |
| Server | Environment variable or secrets manager |
| CI/CD | Secret variables (GitHub Secrets, etc.) |
Token Rotation
- Rotate API keys periodically (90 days recommended)
- Use short-lived access tokens (1 hour)
- Revoke tokens when users leave
IP Allowlisting
Restrict API key usage by IP:
POST /api/v1/api-keys
{
"name": "restricted-key",
"allowed_ips": ["10.0.0.0/8", "192.168.1.100"]
}
Troubleshooting
"Invalid token" Error
{"error": {"code": "UNAUTHORIZED", "message": "Invalid or expired token"}}
Causes:
- Token has expired (check
expires_at) - Token was revoked
- Wrong environment (dev token on prod)
Solutions:
- Refresh the token
- Re-authenticate
- Check you're using the correct API URL
"Forbidden" Error
{"error": {"code": "FORBIDDEN", "message": "Insufficient permissions"}}
Causes:
- User doesn't have access to this tenant
- API key missing required permission
- Role doesn't allow this action
Solutions:
- Check user/key permissions
- Request elevated access from admin
- Use a different API key with correct scopes