Skip to main content
The KYA SDK is coming soon. The code on this page won’t run until the SDK packages are published.
The SDK runs wherever your code runs. The TypeScript SDK works in Node 20+, Auth0 Actions, Cloudflare Workers and other edge runtimes. The Python SDK works in Python 3.11+. The setup is the same in every pattern: install the SDK, publish your /.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):
  1. It asks for a nonce with POST /nonces.
  2. It trades its credential for your access token with POST /oauth/token.
The agent side of the SDK uses those two paths by default. The only thing that changes is which of your systems answers them.

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 /nonces it calls verifier.issueNonce() and returns { "nonce": "...", "audience": "shop.example" }.
  • On /oauth/token it calls verifier.verify(subjectToken, { scope }) with the scope from your published profile.
Your app code receives the result directly: 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:
  1. Calls verifier.verify() and maps the result into your token: claims.toTokenClaims() gives sub and exp.
  2. Responds with access_token, token_type and expires_in.
To tie your token to the agent’s key, use 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.
Strip those headers from all incoming traffic at the worker. Otherwise an agent can set them itself and skip the check.

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 returns ISSUER_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 503 so 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.
A NonceStore has a ttlSeconds property and three async methods: issue, status and spend.spend is what stops replays. It must check and spend the nonce in one atomic step and return the status from before the change.
Then pass your store in when you create the verifier:

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 up claims.sub in your own database. See Matching customers.