COG-3: Authentication
Status: Draft
Version: 2.0
Created: 2025-09-24
Updated: 2026-01-28
Authors: Mike Anderson
Purpose
Authentication ensures the integrity, security, and controlled access to assets and operations on the grid. This document specifies authentication mechanisms for venues, enabling secure and flexible access control while maintaining venue operators' autonomy over access policies.
Authentication is managed at a per-venue level, allowing venue operators to define and enforce access policies tailored to their specific requirements.
Authentication mechanisms are built on commonly available web standards and, where appropriate, make use of decentralised facilities supported by Convex lattice technology.
Principles
- Venue autonomy — Each venue operator decides which authentication mechanisms to support and what privileges to grant.
- Layered security — Multiple mechanisms MAY be supported simultaneously, allowing clients to use whichever is appropriate.
- Standards-based — All mechanisms use established web and cryptographic standards.
- Decentralisation-ready — Ed25519 key-based authentication ties into the broader Convex identity model without requiring centralised credential stores.
Specification
1. Public Access (No Authentication)
Venues MAY provide open, public access to grid operations and assets, enabling public services or sharing open-source capabilities without requiring prior authorisation.
Public access is the simplest mode and is appropriate for open data, demo services, and community resources.
Venues SHOULD NOT allow unauthorised users to consume excessive resources.
Venues SHOULD implement rate limiting on public endpoints, e.g. via a reverse proxy or application-level throttling.
2. Bearer Token (API Key)
Venues MAY offer authorisation using bearer tokens (commonly referred to as API keys).
The token MUST be passed by the client with every HTTPS request using the standard Authorization header:
Authorization: Bearer <token>
If the venue offers bearer-token authorisation, it has complete control over what privileges it grants to clients using any particular token. Token provisioning is out of scope for this specification and is managed by the venue operator (e.g. via a management portal, CLI, or out-of-band exchange).
Security considerations
- Tokens MUST be treated as secrets and MUST NOT be logged, committed to version control, or transmitted over unencrypted channels.
- Venues SHOULD support token revocation.
- Venues SHOULD enforce token expiry where appropriate.
3. HTTP Basic Authentication
Venues MAY support HTTP Basic authentication as defined in RFC 7617.
Credentials are transmitted as a Base64-encoded username:password pair in the Authorization header:
Authorization: Basic <base64(username:password)>
Basic authentication MUST only be used over HTTPS to protect credentials in transit.
This mechanism is suitable for simple setups, internal tooling, or development environments where a lightweight credential model is sufficient.
4. OAuth 2.0
Venues MAY allow authentication using OAuth 2.0 with PKCE (RFC 7636).
OAuth enables delegated authorisation, allowing users to authenticate via an external identity provider (e.g. Google, GitHub, or a corporate IdP) without sharing their credentials with the venue.
Flow overview
- Client initiates an authorisation request to the venue's OAuth endpoint.
- Venue redirects the client to the identity provider for authentication.
- On successful authentication, the identity provider returns an authorisation code to the venue callback.
- The venue exchanges the code for an access token.
- The client uses the access token as a bearer token for subsequent API requests.
Requirements
- Venues implementing OAuth MUST use PKCE to mitigate authorisation code interception attacks.
- Access tokens SHOULD be short-lived; refresh tokens MAY be issued for long-lived sessions.
- Venues MUST validate tokens on every request.
5. Ed25519 Self-Issued JWT
Venues MAY authenticate clients using self-issued JWTs signed with Ed25519 keys. This mechanism aligns with the Convex identity model, where accounts are identified by Ed25519 public keys, and with the W3C did:key method for self-sovereign identity.
The client's identity is a did:key DID derived from their Ed25519 public key. No pre-registration is required — any entity with an Ed25519 key pair can authenticate. The venue verifies the JWT signature by extracting the public key from the did:key in the token's iss claim.
Client identity: did:key
The client's DID is computed from the Ed25519 public key using the did:key method:
- Take the 32-byte Ed25519 public key
- Prepend the multicodec prefix
0xed01(Ed25519 public key) - Encode with multibase base58btc (prefix
z) - Result:
did:key:z6Mk...
This DID is self-certifying — the public key is embedded in the identifier itself, requiring no external resolution.
JWT structure
The client constructs a JWT with the following structure:
Header:
{
"alg": "EdDSA",
"typ": "JWT",
"kid": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}
Payload:
{
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"sub": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:web:venue.example.com",
"iat": 1706367600,
"exp": 1706367900
}
| Claim | Required | Description |
|---|---|---|
iss | REQUIRED | Client's did:key DID |
sub | REQUIRED | Same as iss (self-issued token) |
aud | RECOMMENDED | Venue DID or URL — prevents replay at a different venue |
iat | REQUIRED | Issued-at timestamp (Unix seconds) |
exp | REQUIRED | Expiry timestamp (Unix seconds) |
jti | OPTIONAL | Unique token ID for replay detection |
The kid header parameter MUST match the iss claim and identifies the verification key.
Transport
The JWT is sent using the standard Authorization: Bearer header:
Authorization: Bearer eyJhbGciOiJFZERTQSIs...
This reuses the same header as bearer tokens and API keys. The venue distinguishes a self-issued JWT from an opaque token by decoding the JWT and checking for alg: EdDSA with a did:key issuer.
Flow overview
Client Venue
│ │
│ 1. Generate Ed25519 key pair (once) │
│ did:key:z6Mk... = encode(pubkey) │
│ │
│ 2. Create JWT: │
│ iss: did:key:z6Mk... │
│ aud: did:web:venue.example.com │
│ exp: <now + 5min> │
│ Sign with Ed25519 private key │
│ │
│ Authorization: Bearer <JWT> │
│ POST /api/v1/invoke │
│ ───────────────────────────────────────────▶ │
│ │
│ 3. Decode JWT, extract did:key from iss
│ 4. Derive Ed25519 pubkey from did:key
│ 5. Verify JWT signature
│ 6. Validate exp, aud, iat
│ 7. (Optional) Resolve Convex account
│ │
│ 200 OK │
│ ◀─────────────────────────────────────────── │
Venue verification
Venues MUST perform the following validation steps:
- Decode the JWT and verify
algisEdDSA - Extract the
did:keyfrom theissclaim - Decode the Ed25519 public key from the
did:key(reverse multicodec + multibase) - Verify the JWT signature using the extracted public key
- Validate
exp > now(reject expired tokens) - Validate
iat <= now + clock_skew(reject tokens issued in the future) - If
audis present, validate it matches the venue's own DID or URL - Validate
exp - iat <= max_lifetime(reject tokens with excessively long validity)
Recommended validation parameters:
| Parameter | Recommended value |
|---|---|
| Clock skew tolerance | 30 seconds |
Maximum token age (now - iat) | 10 minutes |
Maximum token lifetime (exp - iat) | 5 minutes |
Token lifetime
Tokens SHOULD be short-lived (recommended 5 minutes). Since the client holds the private key, generating a new JWT is effectively free (Ed25519 signing takes microseconds). Refresh tokens are unnecessary — when a token expires, the client simply signs a new one.
Optional Convex account resolution
After verifying the JWT signature, a venue MAY look up the Ed25519 public key on the Convex network to resolve a Convex account:
- Extract the 32-byte Ed25519 public key from the
did:key - Query the Convex network for accounts whose controller key matches
- If found — associate the request with the Convex account (e.g.
#1337) - If not found — the client is authenticated by
did:keyalone
This resolution is a venue-side decision. The client does not need to be aware of it. A venue not connected to the Convex network simply authenticates by did:key without account resolution.
Security considerations
- Private keys MUST NOT leave the client and MUST NOT be transmitted over the network.
- The
expandaudclaims provide replay protection without server-side state. - Venues SHOULD reject tokens where
exp - iatexceeds the maximum lifetime, preventing clients from creating excessively long-lived tokens. - For high-security environments, venues MAY track
jtivalues within the token's validity window to detect replay of the exact same token. - This mechanism integrates naturally with Convex account keys, enabling on-chain identity verification.
Client SDK Support
The Covia SDKs provide a generic authentication interface that supports all mechanisms described above.
| Mechanism | Python SDK | Java SDK |
|---|---|---|
| Public (no auth) | Default | Default |
| Bearer token | BearerAuth(token) | Supported |
| HTTP Basic | BasicAuth(user, pass) | Supported |
| OAuth 2.0 | Planned | Supported |
| Ed25519 JWT | Ed25519Auth | Planned |
Python SDK example
from covia import Grid
from covia.auth import BearerAuth, BasicAuth
# Public access (no auth)
venue = Grid.connect("https://venue.covia.ai")
# Bearer token
venue = Grid.connect("https://venue.covia.ai", auth=BearerAuth("my-token"))
# HTTP Basic
venue = Grid.connect("https://venue.covia.ai", auth=BasicAuth("user", "pass"))
# Ed25519 self-issued JWT (requires: pip install covia[signing])
from covia.auth import Ed25519Auth
# Generate a new identity — audience is auto-set from Grid.connect()
auth = Ed25519Auth.generate()
print(auth.did) # did:key:z6Mk...
venue = Grid.connect("did:web:venue.covia.ai", auth=auth)
# Or from a known seed (deterministic key)
auth = Ed25519Auth.from_seed(seed_bytes)
venue = Grid.connect("did:web:venue.covia.ai", auth=auth)
Custom authentication providers can be implemented by subclassing covia.auth.Auth:
from covia.auth import Auth
class ApiKeyAuth(Auth):
def __init__(self, key: str) -> None:
self._key = key
def apply(self, headers: dict[str, str]) -> None:
headers["X-Api-Key"] = self._key
Venue Discovery
Venues SHOULD advertise their supported authentication mechanisms via the A2A agent card (/.well-known/agent-card.json) using the securityScheme field. This allows clients to discover the required authentication method before making API calls.