Authentication & Authorization

N/A /api/v1/auth

Authentication & Authorization

Overview

The Prismatic API uses a layered authentication and authorization system built on Bearer tokens with role-based access control (RBAC) and fine-grained scope enforcement. Every API request (except the Health Check and OpenAPI Specification endpoints) must include a valid Bearer token that identifies the caller and determines what operations they are authorized to perform.

The authentication system is implemented as a Phoenix plug (PrismaticWeb.Plugs.APIAuth) that executes before the Generic Dispatch controller. It validates the token, resolves the associated identity, checks scope requirements for the requested endpoint, and attaches the authorization context to the connection. If any check fails, the request is rejected before reaching the dispatch layer.

Token management follows the principle of least privilege: tokens are issued with specific scopes that limit access to particular API domains. A token with perimeter:read scope can query security ratings but cannot modify agent configurations. This granularity allows organizations to issue purpose-specific tokens for different integration scenarios.

The No Mercy doctrine applies to authentication: there are no grace periods, no soft failures, and no anonymous access to protected endpoints. Invalid tokens are rejected immediately with a clear error response.

Authentication Methods

Bearer Token Authentication

The primary authentication method. Include the token in the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Tokens are issued through the token management endpoints or through the platform's identity system.

Token Structure

Tokens are JSON Web Tokens (JWT) containing:

{
  "sub": "user_abc123",
  "iss": "prismatic-platform",
  "iat": 1739347200,
  "exp": 1739433600,
  "scopes": ["perimeter:read", "agents:read"],
  "roles": ["analyst"],
  "metadata": {
    "name": "API Integration Token",
    "created_by": "admin@example.com"
  }
}
ClaimTypeDescription
substringSubject identifier (user or service account ID)
issstringToken issuer (always prismatic-platform)
iatintegerIssued-at timestamp (Unix epoch)
expintegerExpiration timestamp (Unix epoch)
scopesarrayAuthorized API scopes
rolesarrayRBAC role assignments
metadataobjectOptional token metadata

Token Lifecycle

Creating a Token

POST /api/v1/auth/tokens
{
  "name": "EASM Integration",
  "scopes": ["perimeter:read", "perimeter:write"],
  "expires_in": 86400,
  "metadata": {
    "purpose": "CI/CD pipeline integration",
    "environment": "staging"
  }
}

Response (201 Created):

{
  "ok": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "token_id": "tok_abc123",
    "name": "EASM Integration",
    "scopes": ["perimeter:read", "perimeter:write"],
    "created_at": "2026-02-12T10:00:00.000Z",
    "expires_at": "2026-02-13T10:00:00.000Z"
  }
}

Important: The full token value is only returned once at creation time. Store it securely. Subsequent queries return only the token ID and metadata, never the token itself.

Listing Tokens

GET /api/v1/auth/tokens

Returns all tokens for the authenticated user (without token values):

{
  "ok": true,
  "data": {
    "tokens": [
      {
        "token_id": "tok_abc123",
        "name": "EASM Integration",
        "scopes": ["perimeter:read", "perimeter:write"],
        "created_at": "2026-02-12T10:00:00.000Z",
        "expires_at": "2026-02-13T10:00:00.000Z",
        "last_used_at": "2026-02-12T10:30:00.000Z",
        "usage_count": 47
      }
    ]
  }
}

Revoking a Token

DELETE /api/v1/auth/tokens/:token_id

Immediately invalidates the token. All subsequent requests using this token will receive a 401 response.

Response (200 OK):

{
  "ok": true,
  "data": {
    "token_id": "tok_abc123",
    "revoked_at": "2026-02-12T11:00:00.000Z"
  }
}

Refreshing a Token

POST /api/v1/auth/tokens/:token_id/refresh

Issues a new token with the same scopes and a fresh expiration. The old token is revoked.

{
  "expires_in": 86400
}

Scopes

Scopes follow a domain:permission pattern. Each scope grants access to a specific set of API operations.

Available Scopes

ScopeDescriptionEndpoints
perimeter:readRead attack surface datadiscover, rating, compliance
perimeter:writeModify perimeter configurationsasset management, scan scheduling
agents:readRead agent registry and statuslist, status
agents:writeManage agent configurationsactivate, deactivate, configure
quality:readRead quality metricsquality DNA, floor guardian
quality:writeModify quality thresholdsgate configuration
webhooks:readRead webhook configurationslist, status
webhooks:writeManage webhookscreate, update, delete
admin:readRead administrative dataaudit logs, system config
admin:writeModify system configurationsettings, user management

Scope Inheritance

Some scopes include others implicitly:

  • admin:write includes admin:read
  • perimeter:write includes perimeter:read
  • agents:write includes agents:read

Roles

Roles are predefined bundles of scopes that simplify permission management.

RoleIncluded ScopesDescription
viewer*:readRead-only access to all domains
analystperimeter:read, agents:read, quality:readSecurity analysis access
operatorperimeter:*, agents:*, quality:readOperational management
admin*:*Full administrative access

Code Examples

curl

# Create a token
curl -s -X POST \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI Pipeline", "scopes": ["perimeter:read"], "expires_in": 3600}' \
  http://localhost:4004/api/v1/auth/tokens | jq '.data.token'

# Use the token
export API_TOKEN="eyJhbGciOiJIUzI1NiIs..."
curl -s -H "Authorization: Bearer $API_TOKEN" \
  http://localhost:4004/api/v1/perimeter/rating?domain=example.com | jq .

# List tokens
curl -s -H "Authorization: Bearer $ADMIN_TOKEN" \
  http://localhost:4004/api/v1/auth/tokens | jq '.data.tokens[] | {id: .token_id, name, scopes}'

# Revoke a token
curl -s -X DELETE \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  http://localhost:4004/api/v1/auth/tokens/tok_abc123

# Refresh a token
curl -s -X POST \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expires_in": 86400}' \
  http://localhost:4004/api/v1/auth/tokens/tok_abc123/refresh | jq '.data.token'

Elixir

# Create a token programmatically
{:ok, token_data} = PrismaticApi.Auth.create_token(%{
  name: "Internal Service",
  scopes: ["perimeter:read", "agents:read"],
  expires_in: 86400
})

# Validate a token
case PrismaticApi.Auth.validate_token(token_string) do
  {:ok, claims} -> IO.puts("Valid for: #{inspect(claims.scopes)}")
  {:error, :expired} -> IO.puts("Token expired")
  {:error, :invalid} -> IO.puts("Invalid token")
end

# Check authorization for a specific scope
case PrismaticApi.Auth.authorize(conn, "perimeter:read") do
  :ok -> proceed_with_request(conn)
  {:error, :unauthorized} -> send_401(conn)
  {:error, :forbidden} -> send_403(conn)
end

Python

import requests
import os

# Create a token
admin_token = os.environ["ADMIN_TOKEN"]
response = requests.post(
    "http://localhost:4004/api/v1/auth/tokens",
    headers={"Authorization": f"Bearer {admin_token}", "Content-Type": "application/json"},
    json={"name": "Python Client", "scopes": ["perimeter:read"], "expires_in": 3600}
)

api_token = response.json()["data"]["token"]

# Use the token for API calls
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {api_token}"})

rating = session.get(
    "http://localhost:4004/api/v1/perimeter/rating",
    params={"domain": "example.com"}
).json()

print(f"Rating: {rating['data']['rating']['grade']}")

Error Responses

Status CodeError CodeDescription
401token_missingNo Authorization header provided
401token_invalidToken signature verification failed
401token_expiredToken has passed its expiration time
401token_revokedToken has been explicitly revoked
403insufficient_scopeToken does not include the required scope
403insufficient_roleToken's role does not permit the operation
422invalid_scopeRequested scope does not exist

See Error Handling for the standard error response format.

Rate Limits

Authentication endpoints have their own rate limits to prevent brute-force attacks:

EndpointLimitWindow
POST /auth/tokens10 requests1 minute
DELETE /auth/tokens/:id30 requests1 minute
POST /auth/tokens/:id/refresh10 requests1 minute
Failed authentication attempts20 attempts5 minutes

After 20 failed authentication attempts from a single IP within 5 minutes, the IP is temporarily blocked for 15 minutes.

Security Best Practices

PracticeDescription
Short-lived tokensUse tokens with the shortest practical expiration
Minimum scopesRequest only the scopes needed for the integration
Token rotationRefresh tokens regularly, do not reuse expired tokens
Secure storageStore tokens in environment variables or secret managers, never in code
Revoke on compromiseImmediately revoke tokens if they may have been exposed
Audit regularlyReview token usage via the listing endpoint

The authentication system enforces the Color Teams isolation model. Tokens associated with Red Team operations cannot access Blue Team data, and vice versa. The Trinity Gate verification validates that scope definitions are internally consistent and that no privilege escalation paths exist in the role hierarchy.


Connect & Contribute

Created by Tomáš Korcak (korczis) | Open Source under GHL