Identity and Registry
World discovery, player authentication, JWT tokens, RBAC permissions, OAuth2 login, and profile management in Aether.
The identity and registry system is the central authority for player authentication, profile management, permissions, and world discovery in Aether. It provides JWT-based authentication with Ed25519 signing, OAuth2 social login, RBAC/ABAC authorization, and token validation for federated world servers.
Key Concepts
- JWT authentication -- Short-lived access tokens (15 min) and long-lived refresh tokens (30 days) signed with Ed25519.
- OAuth2 social login -- Google, Apple, Discord, and Steam as identity providers.
- RBAC/ABAC permissions -- Hierarchical roles with inherited permissions.
- Token validation -- JWKS endpoint for federated world servers to verify tokens locally.
- Profile management -- CRUD operations for player profiles with search capability.
- Audit logging -- Every authentication event is recorded for security and compliance.
Architecture
The identity service is a Go-based microservice backed by PostgreSQL, Redis, and NATS:
Client -> API Gateway -> Identity Service
|
+---------------+---------------+
| | |
PostgreSQL Redis NATS
(users, (sessions, (auth events)
roles, token cache)
audit)
| Component | Technology | Purpose |
|---|---|---|
| HTTP Framework | net/http + chi | Lightweight, stdlib-compatible routing |
| Database | PostgreSQL 16 | User records, roles, audit logs |
| Cache | Redis 7 | Session storage, token blacklist |
| Message Bus | NATS JetStream | Auth event publishing |
| JWT Signing | go-jose/v4 | Ed25519 token signatures |
| WebAuthn | go-webauthn | FIDO2/passkey support |
Authentication Flow
Player login follows a secure multi-step flow:
- Client sends credentials to the API gateway.
- Gateway forwards to the identity service.
- Service queries the user by email from PostgreSQL.
- Password is verified using Argon2id.
- A new session is created in the database and cached in Redis.
- A login event is published to NATS for audit subscribers.
- Access token and refresh token are returned to the client.
Token refresh rotates both tokens:
POST /api/v1/auth/refresh { refresh_token }
-> Validate refresh token against session
-> Generate new access + refresh token pair
-> Rotate refresh token hash in database
-> Update Redis cache
-> Return new token pair
The old refresh token is invalidated on each rotation, preventing replay attacks.
JWT Token Design
Access token (short-lived, 15 minutes):
{
"sub": "user-uuid",
"iss": "aether-identity",
"aud": ["aether-api", "aether-world"],
"exp": 1709890800,
"iat": 1709889900,
"jti": "unique-token-id",
"role": "creator",
"permissions": ["world:create", "avatar:upload"]
}
Refresh token (long-lived, 30 days):
- Stored as SHA-256 hash in the
sessionstable (never stored in plaintext). - Rotated on each refresh; the old token is invalidated.
- Bound to IP address and user-agent for anomaly detection.
Signing: Ed25519 is used for fast signature generation with small token sizes. Ed25519 keys are provided via the IDENTITY_JWT_PRIVATE_KEY environment variable.
World Server Token Validation
Federated world servers validate player tokens without per-request calls to the identity service:
Option 1 -- JWKS (preferred): World servers fetch the JWKS endpoint periodically (every 5 minutes) and validate tokens locally using the cached Ed25519 public keys.
GET /api/v1/auth/.well-known/jwks.json
-> Returns Ed25519 public keys for local JWT validation
Option 2 -- Validation endpoint: For cases requiring extra claims or revocation checking:
POST /api/v1/auth/validate { token }
-> Returns { valid: true, claims: { ... } }
RBAC/ABAC Permission Model
Roles are hierarchical -- higher-level roles inherit all permissions from lower levels:
| Role | Level | Permissions |
|---|---|---|
| Player | 0 | profile:read, profile:write, world:join, avatar:equip |
| Creator | 10 | world:create, world:edit, avatar:upload, asset:upload |
| Moderator | 50 | user:warn, user:mute, user:kick, report:review |
| Admin | 100 | user:ban, role:assign, role:revoke, system:configure |
Permission checks evaluate the role hierarchy:
Admin inherits -> Moderator inherits -> Creator inherits -> Player
A Creator can do everything a Player can, plus creator-specific actions. A Moderator inherits both Creator and Player permissions.
OAuth2 Social Login
The identity service supports four OAuth2 providers:
| Provider | Flow | Notes |
|---|---|---|
| Authorization Code | Standard OIDC flow | |
| Apple | Authorization Code | Sign in with Apple |
| Discord | Authorization Code | Discord OAuth2 |
| Steam | OpenID 2.0 | Steam Web API |
OAuth2 endpoints:
GET /api/v1/auth/oauth/{provider} -- Initiate OAuth2 redirect
GET /api/v1/auth/oauth/{provider}/callback -- Handle OAuth2 callback
On first OAuth login, a new user account is created and linked to the provider. Subsequent logins match the provider user ID to the existing account.
WebAuthn / Passkey Support
The identity service supports FIDO2/WebAuthn for passwordless authentication:
POST /api/v1/auth/webauthn/register/begin -- Begin passkey registration
POST /api/v1/auth/webauthn/register/finish -- Complete registration
POST /api/v1/auth/webauthn/login/begin -- Begin passkey login
POST /api/v1/auth/webauthn/login/finish -- Complete login
WebAuthn credentials are stored in the webauthn_credentials table with the credential ID, public key, AAGUID, and sign count for replay detection.
Profile Management
Player profiles support CRUD operations with search:
GET /api/v1/profiles/me -- Get current user's profile
PUT /api/v1/profiles/me -- Update current user's profile
GET /api/v1/profiles/{id} -- Get profile by ID
GET /api/v1/profiles -- Search profiles
Profile fields include display name, bio, avatar URL, and user-defined settings stored as JSONB.
Audit Logging
Every authentication event is recorded in the audit_logs table:
| Event Type | Trigger |
|---|---|
login | Successful login |
logout | Session revocation |
token_refresh | Refresh token rotation |
permission_change | Role assignment or revocation |
profile_update | Profile field modification |
Each audit record captures the user ID, IP address, user-agent, timestamp, and event-specific metadata as JSONB.
Configuration
All settings are driven by environment variables:
| Variable | Default | Description |
|---|---|---|
IDENTITY_PORT | 8080 | HTTP listen port |
IDENTITY_DB_URL | required | PostgreSQL connection string |
IDENTITY_REDIS_URL | required | Redis connection string |
IDENTITY_NATS_URL | required | NATS connection string |
IDENTITY_JWT_PRIVATE_KEY | required | Ed25519 private key (PEM) |
IDENTITY_JWT_ACCESS_TTL | 15m | Access token TTL |
IDENTITY_JWT_REFRESH_TTL | 720h | Refresh token TTL |
IDENTITY_RATE_LIMIT_LOGIN | 10 | Login attempts per minute |
IDENTITY_RATE_LIMIT_REGISTER | 5 | Registration attempts per minute |
IDENTITY_ARGON2_MEMORY | 65536 | Argon2id memory in KB |
IDENTITY_ARGON2_ITERATIONS | 3 | Argon2id iterations |
OAuth provider credentials are optional and enable social login when configured:
| Variable | Description |
|---|---|
IDENTITY_OAUTH_GOOGLE_ID | Google OAuth client ID |
IDENTITY_OAUTH_GOOGLE_SECRET | Google OAuth client secret |
IDENTITY_OAUTH_APPLE_ID | Apple OAuth client ID |
IDENTITY_OAUTH_DISCORD_ID | Discord OAuth client ID |
IDENTITY_OAUTH_STEAM_KEY | Steam API key |