Skip to content

Service Authentication

Backend services authenticate to Surge using Bearer tokens prefixed with aeg_svc_. Each token carries a set of grants that control which operations the service can perform.

Service token lifecycle

Service tokens authenticate backend services to Surge. Each token carries grants that scope what the service can do. Tokens follow the same security pattern as session tokens: they're shown once at creation and stored as SHA-256 hashes.

Creating tokens

Use the CLI to create a service token:

bash
cargo run -p surge-server -- svc create --name my-service

Output:

Service token: aeg_svc_1a2b3c4d5e6f7g8h9i0j

The plaintext token is displayed once. Copy it immediately — if you lose it, you must revoke and recreate. Service tokens can only be created and revoked via the CLI; there is no HTTP endpoint for managing them.

Listing tokens

bash
cargo run -p surge-server -- svc list

Shows all registered services with their grants, without exposing token values (only SHA-256 hashes are stored).

Revoking tokens

bash
cargo run -p surge-server -- svc revoke my-service

Revoking a token immediately invalidates it. Any service using that token will receive authentication errors on their next request.

Token format

Service tokens are generated the same way as session tokens — 128-bit random, base62-encoded — but with a different prefix:

PropertyValue
Entropy128 bits
EncodingBase62
Prefixaeg_svc_
StorageSHA-256 hash

The aeg_svc_ prefix distinguishes service tokens from session tokens (aeg_s_) and flow IDs (aeg_f_) in logs and traffic.

How services present tokens

Services authenticate by sending the token in the Authorization header:

bash
curl -X POST http://localhost:3000/v1/sessions/verify \
  -H "Authorization: Bearer aeg_svc_1a2b3c4d5e6f7g8h9i0j" \
  -H "Content-Type: application/json" \
  -d '{"token": "aeg_s_..."}'

Every request to the Surge service API is authenticated the same way: the Bearer token is hashed and looked up against stored service tokens, and the request is rejected if the token is missing, malformed, or revoked.

Grants

Grants are fine-grained permissions attached to service tokens. When you create a token, you specify which grants it needs; each endpoint requires one specific grant, and a request without it is rejected.

Available grants

GrantAllows
introspectVerify sessions; look up identity state
identity_readRead identity profiles
identity_writeUpdate identity profiles
direct_authRegister and authenticate identities directly (password auth)
revokeRevoke sessions

Scoping tokens

Create a token with specific grants — not every service needs full access:

bash
# Read-only introspection service
cargo run -p surge-server -- svc create --name auth-gateway --grant introspect

# Identity management service  
cargo run -p surge-server -- svc create --name user-admin --grant identity_read --grant identity_write

Services that attempt an operation without the required grant receive an HTTP 403 Forbidden response.

RemoteProvider client

Rust services can use the RemoteProvider client instead of constructing HTTP requests manually. It wraps the Surge API and includes built-in caching:

rust
use surge::{RemoteConfig, RemoteProvider};
use std::sync::Arc;
use std::time::Duration;

let provider = Arc::new(RemoteProvider::new(RemoteConfig {
    base_url: "https://auth.example.com".parse()?,
    service_token: "aeg_svc_1a2b3c4d5e6f7g8h9i0j".into(),
    cache_ttl: Duration::from_secs(30),
    cache_max_entries: 10_000,
    timeout: Duration::from_secs(5),
})?);

// Verify a session — result is cached via moka
let result = provider.verify_session("aeg_s_...").await?;

The RemoteProvider uses moka for in-memory caching of verification results, reducing load on the auth server for repeated lookups of the same session token. Cache entries have a short TTL aligned with your session verification patterns.

Related: Configuration, API Reference