COG-3: Authentication Mechanisms
Status: Draft
Version: 2.0
Created: 2025-09-24
Updated: 2026-01-28
Authors: Mike Anderson
:::note Related specifications This document specifies the catalogue of authentication mechanisms a venue may offer (public access, bearer tokens, HTTP Basic, OAuth 2.0, Ed25519 self-issued JWTs, UCAN identity tokens for relayed requests) and how clients use them. For how a specific venue implements identity verification and access control — the middleware verification order, venue-signed tokens, and OAuth provider configuration — see COG-10: Venue Authentication & Access Control. For capability-based authorisation (what an authenticated caller may do), see COG-13: Agent Capabilities. :::
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 token names a principal in the sub claim and a signing key in the kid header. The venue verifies the signature with the kid key, then checks that the key is authorised for the principal. In the common self-certifying case the principal is a did:key and the authorised key is exactly the key embedded in it — no pre-registration is required, and any entity with an Ed25519 key pair can authenticate. A venue MAY additionally accept subjects it manages itself (named-user DIDs), for which the signing key must be a registered authentication key — see Subjects and authorised keys.
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.
Subjects and authorised keys
The sub claim names the principal being authenticated. Two subject forms are supported:
- Self-certifying (
did:key) — the DID embeds the public key. The signing key is authorised if and only if it is the key encoded insub. Works at any venue, with no registration. - Venue-managed subject — the DID of a named user managed by the receiving venue. The signing key is authorised if it is one of the subject's active registered authentication keys. Such a subject can authenticate only at the venue that manages it: venues MUST NOT resolve foreign subjects (e.g. another venue's users) through this mechanism — cross-venue caller identity uses the UCAN identity token (§6).
Where a venue maintains an authentication-key registry, the following invariants apply:
- A subject MAY have multiple active keys (e.g. one per device); keys MAY be added and revoked over time.
- A key MUST be bound to at most one subject per venue — otherwise a single key could authenticate as two principals.
- A revoked key MUST NOT be silently reactivated; re-authorising a key requires an explicit new binding.
JWT structure
The client constructs a JWT with the following structure:
Header:
{
"alg": "EdDSA",
"typ": "JWT",
"kid": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}
Payload:
{
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"sub": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:web:venue.example.com",
"iat": 1706367600,
"exp": 1706367900
}
| Claim | Required | Description |
|---|---|---|
sub | REQUIRED | The principal: a did:key, or a venue-managed subject DID for which the signing key is registered |
iss | RECOMMENDED | The principal's DID — MUST equal sub when present (a token naming a different issuer is not self-issued and MUST be rejected by this mechanism); REQUIRED for venue-managed subjects |
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 identifies the signing key. Its canonical form is the key's bare Multikey string (z6Mk...); verifiers SHOULD also accept the did:key:<multikey> and <sub>#<multikey> forms (in the latter, the prefix MUST equal the token's sub). The kid key MUST be authorised for the sub principal as defined in Subjects and authorised keys. Note that kid identifies the key, not the principal — for a venue-managed subject it names a registered key that is distinct from the DID in sub.
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 kid that decodes to an Ed25519 Multikey.
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, resolve signing key from kid
│ 4. Verify JWT signature with that key
│ 5. Check key is authorised for sub
│ 6. Validate iss = sub, 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 - Resolve the signing key from the
kidheader (bare Multikey,did:key:<multikey>, or<sub>#<multikey>with the prefix equal tosub) - Verify the JWT signature using that key
- Verify the key is authorised for the
subprincipal — for adid:keysubject it MUST equal the key encoded insub; for a venue-managed subject it MUST be an active registered authentication key of that subject - If
issis present, validate that it equalssub(REQUIRED for venue-managed subjects) - 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; venues MAY requireaud(audience policy) - Validate
exp - iat <= max_lifetime(reject tokens with excessively long validity)
Recommended validation parameters:
| Parameter | Recommended value |
|---|---|
| Clock skew tolerance | 30 seconds |
Maximum token lifetime (exp - iat) | 5 minutes |
Token lifetime
Tokens SHOULD be short-lived (recommended 5 minutes; the reference SDK's default is 300 seconds). 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.
6. UCAN Identity Token (Relayed Requests)
Venues MAY accept a caller identity from the request's ucans proof channel when the transport itself is unauthenticated. This is the identity mechanism for relayed cross-venue requests: an intermediary venue forwards the caller's tokens, and the receiving venue verifies the caller's own signature — placing no trust in the relay.
The proof channel
UCAN tokens accompany a request in the proof channel, separate from operation input — input is data and is persisted into job records (COG-8), so a credential there would leak into durable history. The channel has three carriers, all verified identically at ingress:
| Carrier | Form | Use |
|---|---|---|
Request body ucans field | JSON array of UCAN JWT strings | Requests with a body (e.g. POST /api/v1/invoke) |
X-Covia-Ucans header | The same JWT strings, comma-separated | Body-less requests (e.g. job observation GETs) |
Authorization: Bearer | A single UCAN JWT | When the bearer slot is not carrying another token type |
Encoding rules for the header:
- Entries are separated by commas; whitespace around entries MUST be tolerated (each entry is trimmed) and empty entries ignored. A JWT cannot contain a comma, so the separation is unambiguous.
- Senders SHOULD emit a single header line. (An intermediary that combines repeated lines with commas per HTTP field semantics yields a parseable value, but receivers read one line.)
- Ordering carries no meaning: each token is self-contained — delegation chains travel nested in the token's
prffield, so a single leaf JWT is a complete chain. - Tokens that fail verification are discarded at ingress; a discarded token never fails the request by itself (the absence of required authority does).
- Venues MAY bound the number and size of tokens they accept.
Senders attach tokens explicitly from their own proof state — nothing relies on passive header propagation across hops (COG-15).
Identity tokens
An identity token is a UCAN with:
| Field | Value |
|---|---|
iss | The caller's DID (signs the token) |
aud | The receiving venue's DID |
att | Empty — the token grants nothing; it is pure proof of identity |
exp | Short-lived expiry |
The empty attenuation list makes the token unambiguous: it cannot be dual-used as a grant. The audience binding makes it unusable at any other venue.
Verification rules:
- The token MUST pass standard UCAN verification (signature, temporal bounds).
- The token's
audMUST equal the receiving venue's DID. - The token's
attMUST be empty. - The mechanism applies only when the transport carries no
Authorizationidentity — an authenticated transport peer (e.g. a relaying venue acting as itself) is the caller regardless of forwarded tokens. - Two identity tokens with different issuers are ambiguous: the request MUST be rejected.
On success the caller is the token's iss, with the same standing as any authenticated identity (capability proofs audienced to it apply; the public-access ceiling does not).
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 | Supported |
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 securitySchemes field. This allows clients to discover the required authentication method before making API calls.