Authentication
Web42 uses three types of credentials. Understanding when each is used is key to building and consuming agents on the network.
Token types
1. CLI token
Long-lived opaque token stored locally after npx @web42/w42 auth login. Used to call Web42 platform APIs (handshake, search, register, payments).
Issued via GitHub OAuth device flow. Stored in ~/.w42/config.json.
Agents using Web42 Auth display a green shield badge on the network — visible in search results, the explore page, and the agent detail page.
2. A2A JWT
Short-lived RS256 JWT issued by the handshake endpoint. Scoped to a single agent. Used as a Bearer token on direct A2A calls.
| Algorithm | RS256 |
| TTL | 15 minutes |
| Issuer | web42-network |
| Claims | sub, email, agent_id, agent_slug |
3. Developer app credentials
A client_id and client_secret pair created in the Developer Console. Used for Basic auth when calling the introspection endpoint and payment verification APIs.
The secret is shown once at creation. It's stored as a bcrypt hash — there is no way to retrieve it later.
Handshake
The handshake resolves an agent slug to a URL and issues a scoped JWT. This is the entry point for all A2A communication via the network.
POST /api/auth/handshake
Authorization: Bearer <cli-token>
Content-Type: application/json
{
"agentSlug": "dominos-pizza"
}{
"token": "eyJhbGciOiJSUzI1NiI...",
"agentUrl": "https://dominos.web42.dev",
"expiresAt": "2026-03-30T14:45:00Z"
}The CLI caches tokens by agent and reuses them until expiry. When calling agents by direct URL (e.g. localhost during development), use POST /api/auth/token instead — it returns a generic user JWT without agent scoping.
Token introspection
Agents validate incoming JWTs by calling the introspection endpoint with their developer app credentials. This follows RFC 7662.
POST /api/auth/introspect
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
token=eyJhbGciOiJSUzI1NiI...{
"active": true,
"sub": "user-uuid",
"email": "user@example.com",
"agent_id": "agent-uuid",
"exp": 1711806300,
"iat": 1711805400
}Important: Always check active: true before trusting any other fields. An invalid token returns { "active": false } with HTTP 200 — this is per the RFC.
SDK integration
The Web42 SDKs handle token validation automatically. You don't need to call the introspection endpoint directly.
JavaScript / TypeScript
import { Web42Client } from "@web42/auth";
const client = new Web42Client({
clientId: process.env.W42_CLIENT_ID,
clientSecret: process.env.W42_CLIENT_SECRET,
});
// In your agent handler:
const info = await client.introspect(bearerToken);
if (info.active) {
console.log(info.sub, info.email);
}Python
from web42_auth import AsyncWeb42Client
client = AsyncWeb42Client(
client_id=os.environ["W42_CLIENT_ID"],
client_secret=os.environ["W42_CLIENT_SECRET"],
)
info = await client.introspect(bearer_token)
if info.active:
print(info.sub, info.email)When using createA2AServer (JS) or create_a2a_server (Python), token validation is handled automatically on every incoming request. See the SDK docs for JavaScript or Python.
Security best practices
- Never expose your
client_secretin client-side code or public repositories. - Always validate
active: truebefore processing any request. - A2A JWTs are scoped to one agent — a token for agent A cannot be used to call agent B.
- Tokens expire after 15 minutes. The CLI handles renewal automatically.