Skip to main content
Noorle issues JWTs from three places. They look alike on the wire and are not interchangeable. Knowing which one you are holding answers most questions about why a token was rejected.

Why only one publishes a JWKS

RS256 is asymmetric: the boundary signs with a private key and publishes the public half, so anyone can verify an id_token without holding a secret. That is what makes it usable as an identity assertion across services. HS256 is symmetric — the signing key and the verifying key are the same secret. Publishing a JWKS for a platform or gateway token would mean publishing the signing secret, so those surfaces deliberately advertise jwks_uri: null. Verification happens inside Noorle. Do not write a client that expects to verify a platform or gateway token itself. It cannot, by design.

The identity id_token

Issued by the boundary at the end of a sign-in, exchanged back-channel with client_secret_basic.
principal_type is Noorle’s one custom claim: human, service, or agent. sub is the principal id, and it is the same id the platform uses for that identity — there is no mapping table between the two databases. Additional claims may be added over time. A validator must not reject a token for carrying a claim it does not recognize.
The boundary’s /token response also carries an access_token. It is an inert random placeholder with no meaning and no accepted use. The id_token is the only carrier of identity in that response.

Key rotation

The boundary can hold several signing keys at once. The first signs; all of them publish in the JWKS, and each key’s kid is derived from its own material. Rotation is therefore a three-step sequence — add the new key second, promote it to first, then drop the old — so no token is ever in flight signed by a key that is not published.

The platform access token

Issued by the CLI device flow, signed HS256. Claims include iss (the management-API host that served it), sub (the approving user), aud, exp, iat, scope, account_id, and a token type. An agent’s outbound token additionally carries a delegation chain, where the agent is the immediate actor and whoever triggered the run is preserved as a nested claim. Scope is account:manage:{account_id}. It names an account, not a permission set — authority still comes from the principal’s role and grants. There are no read / write / admin / offline_access scopes on Noorle. Lifetime is one hour.

Gateway tokens

Each MCP gateway is its own authorization server. A token it mints is admitted only on that gateway, and only if it carries the mcp scope, matches the gateway’s account, and lists that gateway’s resource URN in its audience. There is no cross-gateway fallback: one gateway’s token is simply not a credential anywhere else. A gateway advertises authorization_code, refresh_token, and client_credentials, with PKCE S256 only.

Delegation chains

When an agent makes an outbound call, the token records both who is acting and who caused it. Every subject in the chain must resolve to a live principal in the same account, and the chain is capped at 5 levels. The effective caller — the outermost delegator, or the subject if there is none — is what authorization and audit attribute the call to. This is why a denial can match an identity you did not expect: a deny that names the human who started a run also applies to the sub-agent acting on their behalf.

Practical guidance

  • Do not decode a token and trust the payload. Anyone can write claims; only the signature makes them true.
  • Do not try to verify a Noorle HS256 token client-side. You do not have the key, and you should not.
  • Send tokens only over HTTPS, and keep access tokens out of URLs, logs, and browser local storage.
  • Watch exp and re-authenticate before it passes, rather than discovering it through a 401 mid-operation.

Troubleshooting


Next: Connected Apps.