COG-10: Venue Authentication & Access Control
Status: Draft (Work in Progress)
Version: 0.1
Created: 2026-01-29
Authors: Mike Anderson
:::caution Work in Progress This specification is under active development. Structure and details may change significantly based on implementation experience and community feedback. :::
:::note Related specifications This document specifies how a venue implements authentication and access control — the caller classes, the middleware verification order, venue-signed tokens, and OAuth provider configuration. For the underlying catalogue of mechanisms (including the full Ed25519 self-issued JWT specification, which is not repeated here), see COG-3: Authentication Mechanisms. For capability-based authorisation, see COG-13: Agent Capabilities. :::
This standard specifies the authentication and access control mechanisms for Covia Grid venues. It defines how callers identify themselves, how venues verify identity, and how access control decisions are made.
Purpose
Venues on the Covia Grid expose API endpoints that accept requests from users, agents, and other venues. Authentication determines who is making a request; access control determines whether that request is permitted.
The Covia authentication model supports three classes of caller:
- Anonymous: No identity presented. Permitted when public access is enabled.
- Self-identified: The caller presents a self-issued JWT proving control of a cryptographic key pair. No external authority is needed.
- Authority-backed: The caller presents a JWT issued by the venue (after OAuth login) or by a trusted external provider.
Terminology
See COG-1: Architecture for definitions of Grid terminology. Additional terms used in this specification:
- Bearer JWT: A JWT presented in the
Authorization: Bearer <token>HTTP header (distinct from the opaque bearer tokens / API keys of COG-3 §2) - Caller DID: The Decentralised Identifier associated with the authenticated caller
- Venue Key: The Ed25519 key pair owned by the venue, used for signing venue-issued JWTs
- JWKS: JSON Web Key Set, a standard format for publishing public keys used to verify JWTs
Specification
Authentication Flow
All API requests (/api/*), A2A requests (/a2a), and MCP requests (/mcp) pass through the authentication middleware. The middleware extracts caller identity from the Authorization header.
Request arrives
│
├── No Authorization header
│ ├── Public access enabled → Allow (anonymous)
│ └── Public access disabled → 401 Authentication required
│
└── Authorization: Bearer <token>
│
├── Try self-issued EdDSA verification
│ └── Success → Caller DID = sub claim (did:key or venue-managed named DID)
│
├── Try venue-signed EdDSA verification
│ └── Success → Caller DID = sub claim (venue user DID)
│
├── Try external provider RS256 verification
│ └── Success → Caller identity = email or sub claim
│
└── All verification paths fail
├── Public access enabled → Allow (anonymous)
└── Public access disabled → 401 Invalid or expired token
The middleware attempts verification in order: self-issued, venue-signed, then external provider. The first successful verification determines the caller identity.
Every accepted token yields the caller identity from its sub claim. What distinguishes the classes is whose authority the token rests on — expressed by the signing key together with a per-class constraint on iss, which keeps the classes disjoint:
| Class | Authority (iss) | Caller (sub) | Signature key |
|---|---|---|---|
| Self-issued | The subject itself — iss, when present, MUST equal sub | did:key, or a venue-managed named DID | The kid key, which MUST be authorised for sub (COG-3 §5) |
| Venue-signed | This venue — iss MUST equal the venue's DID | Venue-scoped user DID | The venue's own key |
| External provider | The provider — iss MUST match the configured issuer | Provider-scoped subject | A provider JWKS key matching kid |
A venue-signed token is an attestation by the venue about its user, so iss ≠ sub is expected there. A self-issued token rests on the subject's own authority, so a token naming any other issuer MUST NOT be accepted as self-issued.
Token Types
Self-Issued EdDSA JWT
A client with an Ed25519 key pair MAY create a self-issued JWT to authenticate. This is the primary authentication method for agents and automated clients.
The full token specification lives in COG-3 §5 and is not repeated here. In summary:
- The JWT MUST use the EdDSA algorithm
- The
kidheader identifies the signing key — canonically its bare Multikey string; thedid:key:<multikey>and<sub>#<multikey>forms are also accepted - The
subclaim is the caller's DID: adid:keywhose embedded key equals thekidkey, or a venue-managed named-user DID for which thekidkey is an active registered authentication key - The
issclaim, when present, MUST equalsub; it is REQUIRED for venue-managed subjects - The JWT MUST include
iatandexp;audis RECOMMENDED and enforced per the venue's audience policy - The venue verifies the signature with the
kidkey, then confirms that key is authorised forsub
Example claims:
{
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"sub": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:web:venue.example.com",
"iat": 1706500000,
"exp": 1706500300
}
For did:key subjects this mechanism requires no prior registration or shared secrets — any entity with an Ed25519 key pair can authenticate to any venue. A venue-managed subject can authenticate only at its home venue, using a key registered for it there.
Venue-Signed EdDSA JWT
After a successful OAuth login, the venue issues a JWT signed with its own Ed25519 key pair. This binds the OAuth identity to a venue-scoped user DID.
Requirements:
- The JWT MUST use the EdDSA algorithm
- The JWT MUST be signed with the venue's key pair
- The
subclaim MUST contain the user's venue-scoped DID (e.g.did:web:venue.example.com:u:alice_gmail_com) - The
issclaim MUST contain the venue's DID - The
iatandexpclaims MUST be present - Expiry is controlled by the venue's
auth.tokenExpiryconfiguration (default: 86400 seconds / 24 hours)
Example claims:
{
"sub": "did:web:venue.example.com:u:alice_gmail_com",
"iss": "did:web:venue.example.com",
"iat": 1706500000,
"exp": 1706586400
}
The venue verifies these tokens using its own public key. No external key material is needed.
External Provider RS256 JWT
Clients that already hold a valid ID token from a configured OAuth provider MAY present it directly as a bearer token. The venue verifies it against the provider's JWKS endpoint.
Requirements:
- The JWT MUST use the RS256 algorithm
- The JWT
kidheader MUST match a key in the provider's JWKS endpoint - The JWT MUST pass claim validation (issuer, audience, expiry)
- The venue extracts the
emailclaim (preferred) orsubclaim as the caller identity
Verification process:
- Parse the JWT and check the algorithm is RS256
- Extract the
kidfrom the header - For each configured OAuth provider with a JWKS endpoint:
a. Fetch the provider's JWKS and find the key matching
kidb. Verify the RS256 signature c. Validate claims: issuer matches provider, audience matchesclientId, token not expired - If verification succeeds, extract the caller identity from
emailorsub
Anonymous Access
When auth.public.enabled is true (the default), requests without a valid bearer token are allowed through without a caller identity. The caller DID is null for anonymous requests.
Anonymous access is suitable for:
- Development and testing
- Public read-only APIs
- Venues that implement their own access control at the application layer
When auth.public.enabled is false, all requests MUST include a valid bearer token. Requests without a token or with an invalid token receive a 401 response.
Public Capability Ceiling
Anonymous requests do not run unconstrained: the venue assigns the anonymous caller class a capability scope — the public capability ceiling — evaluated by the ordinary capability machinery (COG-13). The ceiling is configured with auth.public.caps:
auth.public.caps | Effect |
|---|---|
| (absent — the default) | A secure read-only profile: crud/read scoped to the venue's public identity (<venueDID>:public) plus unscoped asset/read. No invoke, agent, or mutating ability is granted, so every mutating operation — including operation invocation — is denied to anonymous callers. |
"unrestricted" | No ceiling — anonymous callers are unscoped (the legacy permissive behaviour). |
| An explicit capability array | Exactly that scope. |
A malformed value MUST fail closed to the default read-only profile. The ceiling applies only to anonymous callers: any authenticated identity — including a self-issued did:key caller or a relayed identity-token caller (COG-3 §6) — has the standing of its own capabilities, and the public ceiling does not apply.
OAuth Login
Venues MAY configure OAuth 2.0 Authorization Code flow providers to enable browser-based login. The following providers are supported:
Google
| Setting | Value |
|---|---|
| Authorization endpoint | https://accounts.google.com/o/oauth2/v2/auth |
| Token endpoint | https://oauth2.googleapis.com/token |
| JWKS URI | https://www.googleapis.com/oauth2/v3/certs |
| Issuer | https://accounts.google.com |
| Scopes | openid email profile |
Google returns an OpenID Connect ID token. The venue verifies it via JWKS and extracts user identity.
Microsoft
| Setting | Value |
|---|---|
| Authorization endpoint | https://login.microsoftonline.com/common/oauth2/v2.0/authorize |
| Token endpoint | https://login.microsoftonline.com/common/oauth2/v2.0/token |
| JWKS URI | https://login.microsoftonline.com/common/discovery/v2.0/keys |
| Issuer | Varies by tenant |
| Scopes | openid email profile |
Microsoft uses the common (multi-tenant) endpoint. The issuer varies by Azure AD tenant, so issuer validation is skipped.
GitHub
| Setting | Value |
|---|---|
| Authorization endpoint | https://github.com/login/oauth/authorize |
| Token endpoint | https://github.com/login/oauth/access_token |
| User info endpoint | https://api.github.com/user |
| JWKS URI | N/A (GitHub does not issue JWTs) |
| Scopes | user:email read:user |
GitHub uses standard OAuth 2.0 (not OpenID Connect). After token exchange, the venue fetches user information from GitHub's API.
OAuth Callback Flow
- The user visits
/auth/{provider}and is redirected to the provider's authorisation page - After consent, the provider redirects to
/auth/{provider}/callbackwith an authorisation code - The venue exchanges the code for tokens at the provider's token endpoint
- The venue extracts user identity (email, name, subject) from the ID token or user info endpoint
- The venue creates or updates the user record in its user database
- The venue issues a venue-signed JWT and returns it to the client
Redirect URIs
OAuth redirect URIs are constructed from the venue's base URL:
{baseUrl}/auth/{provider}/callback
The base URL is derived from the venue's hostname and port configuration, or can be set explicitly with the baseUrl venue configuration key. The redirect URI MUST be registered in the provider's developer console.
User Identity
After OAuth login, users are assigned a venue-managed DID formed from the venue's did:web identity (COG-2):
did:web:{venue-host}:u:{user-id}
This is a standard did:web DID — the colon-separated segments are the method's path encoding — and it resolves to a DID document the venue serves at https://{venue-host}/u/{user-id}/did.json. The venue is the controller of the DID and signs assertions about the user with its own key pair. A public venue hostname is required.
The user ID is derived from the email address by replacing . and @ with _ (e.g. alice@gmail.com becomes alice_gmail_com). When no email is available, the OAuth sub claim is used directly. User IDs MUST match [A-Za-z0-9._-]+.
User records are stored in the venue's user database with fields including did, email, name, provider, and updated timestamp.
Configuration
Authentication is configured in the venue configuration file under the auth key:
"auth": {
// Allow unauthenticated access (default: true)
"public": { "enabled": true }, // add "caps": [...] to widen the public capability ceiling
// JWT token expiry in seconds (default: 86400 = 24 hours)
"tokenExpiry": 86400,
// OAuth login providers
"oauth": {
"google": { "clientId": "...", "clientSecret": "..." },
"microsoft": { "clientId": "...", "clientSecret": "..." },
"github": { "clientId": "...", "clientSecret": "..." }
}
}
All fields are optional. A venue with no auth configuration allows anonymous access and has no login providers.
Security Considerations
Token Security
- Bearer tokens SHOULD be transmitted only over HTTPS in production
- Venue-signed JWTs SHOULD use short expiry times appropriate to the deployment context
- Client secrets for OAuth providers MUST NOT be committed to version control or exposed in client-side code
Self-Issued Token Trust
Self-issued EdDSA tokens prove that the caller controls a key pair, but do not establish any real-world identity. Venues that require identity assurance SHOULD use OAuth login and restrict access to venue-signed tokens.
JWKS Caching
The venue caches JWKS responses from external providers to avoid excessive network requests. Cached keys are refreshed periodically or when a kid is not found in the cache.
Anonymous Access
When public access is enabled, the venue MUST NOT assume that anonymous requests are trustworthy. Application-level access control SHOULD be applied to restrict what anonymous callers can do.
Related Specifications
- COG-1: Architecture - Overall Grid architecture and terminology
- COG-2: Decentralised ID - DID scheme and resolution
- COG-9: Agent Messaging - Message authentication requirements
- RFC 7519 - JSON Web Token (JWT)
- RFC 7517 - JSON Web Key (JWK)
- RFC 6749 - OAuth 2.0 Authorization Framework
- W3C DID Core - Decentralized Identifiers