COG-10: Authentication
Status: Draft (Work in Progress)
Version: 0.1
Created: 2026-01-29
Authors: Mike Anderson
This specification is under active development. Structure and details may change significantly based on implementation experience and community feedback.
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 Token: A JWT presented in the
Authorization: Bearer <token>HTTP header - 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:...)
│
├── 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.
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.
Requirements:
- The JWT MUST use the EdDSA algorithm
- The
kidheader MUST contain the signer's public key encoded as a Multikey string - The
subclaim MUST be adid:keyURI whose public key matches thekidheader - The JWT MUST include
iat(issued at) andexp(expiration) claims - The venue verifies the signature using the public key from the
kidheader, then confirms that thesubclaim'sdid:keyencodes the same key
Example claims:
{
"sub": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1706500000,
"exp": 1706586400
}
This mechanism requires no prior registration or shared secrets. Any entity with an Ed25519 key pair can authenticate to any venue. The did:key in the sub claim serves as the caller's decentralised identifier.
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:covia:local: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:covia:local:u:alice_gmail_com",
"iss": "did:covia:local",
"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.
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 authorization page - After consent, the provider redirects to
/auth/{provider}/callbackwith an authorization 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-scoped DID:
{venue-did}:u:{user-id}
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 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 },
// 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