The KYA SDK is coming soon. The code on this page won’t run until the SDK packages are published.
/.well-known/kya-profile.json, add the nonce and token routes, and answer protected pages with 401 and a KYA-Disclosure-Scope header (see Accepting agents).
The agent also does the same thing in every pattern (see Presenting a Credential):
- It asks for a nonce with
POST /nonces. - It trades its credential for your access token with
POST /oauth/token.
In your backend
Start here for a first integration, or when only one part of your site (such as checkout) accepts agents. Your app server answers both routes:- On
/noncesit callsverifier.issueNonce()and returns{ "nonce": "...", "audience": "shop.example" }. - On
/oauth/tokenit callsverifier.verify(subjectToken, { scope })with the scope from your published profile.
claims.sub, the checks that passed (claims.verification.active_keys) and the shared fields. It applies your rules and returns an access token for the agent, or refuses with the failure code.
Every server that answers these routes needs the same nonce store. See Share nonces across servers.
In your login server
Use this if you already run an OAuth authorization server. Your APIs keep checking your own tokens and never need to know about KYA. Your login server answers both routes. It returns a nonce and its audience on/nonces, and adds a token exchange grant (RFC 8693) on /oauth/token.
That request is form-encoded with these fields:
The login server:
- Calls
verifier.verify()and maps the result into your token:claims.toTokenClaims()givessubandexp. - Responds with
access_token,token_typeandexpires_in.
claims.cnf.jwk (the agent’s public key) and return token_type: "DPoP".
Your APIs receive the same kind of token your other clients send: Authorization: DPoP <token> with a DPoP proof, or Bearer if you issue plain tokens. They check it as they do today.
Set your token’s expiry from claims.exp so it never outlives the credential. When it expires, the agent presents a fresh credential. There are no refresh tokens.
At the edge
Use this when one check should sit in front of several services. A CDN worker in front of your site (for example, Cloudflare Workers) answers both routes and runs the check, so your servers never touch the credential. The worker answers/nonces from a shared nonce store (see Share nonces across servers) and calls verifier.verify() on /oauth/token.
Your origin servers receive the result attached to each request, for example the customer ID and the checks that passed, in request headers you define. They read those headers and apply your rules.
Tips for production
Start in watch-only mode
Check every credential and log the result, but don’t block anyone yet. Once the logs look right, turn on your rules.Decide what happens when Baselayer can’t be reached
The SDK needs Baselayer’s signing keys and revocation list. Both are cached, but if a fetch fails the check returnsISSUER_RESOLUTION_FAILED or STATUS_LIST_RESOLUTION_FAILED. These are the only two retryable failures.
The SDK never accepts a credential it couldn’t check. What you do with the request is up to you:
- Fail open: treat the agent as an unverified visitor and let it continue with whatever access an unverified visitor gets.
- Fail closed: return
503so the agent retries later.
Share nonces across servers
A nonce must work exactly once. If you run more than one server, serverless functions or many edge locations, a nonce issued on one could be spent again on another, and a copied presentation could be replayed.InMemoryNonceStore keeps nonces in the memory of one process (they expire after 10 minutes), so it only protects a single long-lived process.
Every verifier needs a NonceStore, and there is no default. Give every instance the same store, and make sure that store can check and spend a nonce in one atomic step.
Share one verifier per process
The verifier holds the key and revocation list caches. Create it once and reuse it, rather than creating one per request.Returning customers across servers
The verifier keeps no record of which customers it has seen. To know a returning customer, look upclaims.sub in your own database. See Matching customers.