M2M JWT verification with JWKS and OAuth scopes
How JSON Web Key Sets work with Scalekit, how to use the /keys endpoint to verify machine-to-machine tokens, and how OAuth scopes map to JWT claims for authorization.
When you add OAuth 2.0 client credentials for your APIs, callers receive JWT access tokens. Before you trust any claim, you must verify the signature using Scalekit’s public keys (JWKS). After verification, you authorize the request—often by checking OAuth scopes carried in the token.
This cookbook explains how JWKS and scopes fit together for Scalekit M2M flows: where keys live, how verification works at a high level, how scopes are defined and stored, and how to enforce them in your service.
Why JWKS and scopes belong in one place
Section titled “Why JWKS and scopes belong in one place”- JWKS answers “is this token real?” — You use the key identified by
kidin the JWT header to validate the signature (typically RS256). - Scopes answer “what may this client do?” — After the token is valid, you inspect the
scopesclaim (and your routing rules) to allow or deny the operation.
Skipping either step breaks your security model: verified-but-overpowered clients, or unverified tokens.
JWKS and Scalekit keys
Section titled “JWKS and Scalekit keys”A JSON Web Key Set (JWKS) is JSON that lists one or more JWKs—public key material identified by a kid (key ID). Scalekit puts the matching kid in the JWT header so your validator can pick the right key without baking certificates into your app.
Each environment publishes signing keys at:
GET https://<SCALEKIT_ENVIRONMENT_URL>/keysUse the same base URL as /oauth/token (for example https://your-app.scalekit.dev).
Example response shape:
{ "keys": [ { "use": "sig", "kty": "RSA", "kid": "snk_58327480989122566", "alg": "RS256", "n": "…", "e": "AQAB" } ]}For access tokens, use the key where use is sig and alg is RS256.
Verify an access token
Section titled “Verify an access token”At implementation time, your API typically:
- Extracts the bearer token from
Authorization: Bearer <token>. - Decodes the JWT header (base64url, first segment) and reads
kidandalg. Do not trust the payload until the signature checks out. - Resolves the signing key — fetch
https://<SCALEKIT_ENVIRONMENT_URL>/keys, or use a JWKS client (for examplejwks-rsain Node.js) with caching and refresh when you see an unknownkid. - Verifies the signature with your JWT library (RS256 for Scalekit access tokens).
- Validates claims such as
exp,iss(your environment URL), andaudif your API relies on audience restrictions. - Authorizes the operation using application claims—especially
scopes(covered in the next section).
Operational practices
Section titled “Operational practices”- Cache JWKS responses; refetch when verification fails with an unknown
kid(key rotation). - Fail closed on bad signature, wrong issuer, or expired token (
401; use403when the token is valid but not allowed). - Never skip signature verification based on the payload alone.
OAuth scopes for machine clients
Section titled “OAuth scopes for machine clients”Scopes are permission names you attach to an OAuth client. In M2M flows they describe what a client may do—separate from who it is (client_id / sub).
Why scopes matter
Section titled “Why scopes matter”Without scopes, any valid client could hit any endpoint. Scopes let you apply least privilege, document what each integration is for, and enforce rules in your API by reading the scopes array on the JWT.
How scopes work in Scalekit M2M
Section titled “How scopes work in Scalekit M2M”- When you register an API client for an organization, you pass a
scopesarray (REST or SDKs). - Scalekit stores those scopes and includes them on issued access tokens.
- Your API verifies the JWT (steps above), then checks that
scopesincludes what the route requires.
Use a consistent naming pattern such as resource:action (for example deployments:read, deployments:write).
Register scopes on the client
Section titled “Register scopes on the client”Scopes are set at client creation (and when you update the client via the API). Example:
"scopes": [ "deploy:applications", "read:deployments"]The same values appear on the client record and in issued tokens.
Validate scopes on your API
Section titled “Validate scopes on your API”After the token is verified:
-
Read
scopesfrom the payload, for example:scopes in JWT payload (example) "scopes": ["deploy:applications","read:deployments"] -
Compare what the token grants to what the route allows (for example require
deploy:applicationsonPOST /deploy); return403if a required scope is missing. -
Use SDK helpers where they fit your stack to combine signature and scope checks (see the quickstart).
Related
Section titled “Related”- Add OAuth 2.0 to your APIs — client registration, tokens, examples
- API keys — long-lived keys; patterns may differ from OAuth client credentials
- Authenticate customer apps — customer-facing API auth and JWKS examples