NIST IR 8587 Token Protection: A Keycloak Control Map

Guilliano Molaire Guilliano Molaire 14 min read

Last updated: September 2026

NIST finalized Interagency Report 8587, “Protecting Tokens and Assertions from Forgery, Theft, and Misuse,” on 15 September 2026, and most of the control outcomes its announcement highlights are already a switch in Keycloak rather than a project. The report is written for federal agencies and the cloud service providers they buy from, but the control set is the ordinary token hygiene any identity platform should be able to evidence: short lifetimes, sender-constrained tokens, strict audience and issuer validation, disciplined signing-key management, working revocation, and enough logging to reconstruct an incident. Keycloak 26.x covers those directly. DPoP has been supported rather than preview since 26.4, mutual-TLS certificate binding is a per-client toggle, and refresh rotation is one realm setting. Two things it does not cover are the risk-signal side of fine-grained conditional access, where Keycloak gives you conditional flows rather than a device and behaviour engine, and shared signals, which is still experimental.

Two things worth stating up front. This post maps control outcomes to Keycloak configuration; it is not a compliance attestation, and if you are in scope for IR 8587 your assessor will want the actual document. And the IR 8587 material here comes from the NIST and CISA announcements of the final report rather than from the full text, so treat the control list below as the publicly summarised outcomes rather than a section-by-section map. The report also carries material on AI and post-quantum migration that is outside what this post covers.

What does NIST IR 8587 ask of identity providers?

In 2026, NIST finalized IR 8587 in coordination with CISA, aimed at federal agencies and cloud service providers, with the stated purpose of protecting identity tokens and assertions from forgery, theft and misuse (NIST, “NIST Finalizes Guidelines on Protecting Online Identity and Access Tokens From Misuse,” 15 September 2026). The announcement describes guidance that builds on NIST SP 800-53 and sets out principles for both providers and consuming agencies, with architectural considerations for identity providers and authorization servers.

The recommendations named in the NIST and CISA announcements group into six buckets, and they map onto things a Keycloak realm either does or does not do:

  1. Sender-constrained tokens, with mutual TLS and demonstrating proof of possession named specifically
  2. Explicit audience restriction, so a token issued for one service is not accepted by another
  3. Fine-grained conditional-access decisions informed by device, network, geolocation and behavioural context
  4. Key management, token verification and lifecycle controls
  5. Revocation, with global revocation and shared signals listed among emerging standards that may support these outcomes
  6. Documentation of the full token architecture: protocols, lifetimes, validation paths, key management, session controls, revocation, logging and incident response

Bucket three is the one where Keycloak is least able to answer on its own, and it is worth saying so before working through the rest. Keycloak gives you conditional authentication flows keyed on role, client scope, user attribute, credential type and level of authentication, plus step-up. It does not ship a risk engine that scores device posture, network reputation, geolocation or behaviour. Those signals have to come from something else and be fed in, through a custom authenticator or an identity provider that already holds them.

Bucket six is the one teams underestimate. Every control below is configurable in an afternoon, and writing down which ones you chose and why is the part that makes a configured realm auditable.

Token theft, forgery and misuse: why does the distinction matter?

The three words in the report’s title describe three different failures with three different fixes, which is exactly why “we have MFA” does not answer any of them.

Theft means a valid token ends up in the wrong hands, whether lifted from a browser session, a log file, or an adversary-in-the-middle proxy. The token is genuine, so signature validation passes. The controls that help are short lifetimes and sender constraints, because a bearer token is only useful to a thief for as long as it lives and only if possession alone is sufficient. Token theft is the failure mode that survives MFA, which is why it gets its own guidance rather than being folded into authentication advice.

Forgery means an attacker mints a token the identity provider never issued, or alters one it did. This is a key management and verification problem: private key protection, algorithm pinning, and consumers that actually verify the signature against the published JWKS rather than decoding and trusting.

Misuse means a genuine token used outside its intended scope, audience or context. A token minted for an internal reporting service and replayed against the payments API is misuse, and neither a short lifetime nor a signature check catches it, because audience restriction is the control that does.

MFA sits before all three. It governs how a token gets issued, not what happens to it afterward, which is the point the announcements open on: a stolen token can bypass both the password and the second factor.

How do you get short lifetimes and rotation right in Keycloak?

Access token lifespan and refresh rotation live in Realm settings, then Tokens, and they are the two dials that most directly shrink the theft window.

Access Token Lifespan controls how long an issued access token stays valid. Keycloak’s defaults here are already conservative, which is worth knowing before you go looking for a problem that is not there: the bootstrapped master realm is set to 60 seconds in the source, and a new realm lands at 5 minutes. Values in the five to fifteen minute range are typical for a workforce realm, and the constraint is how often your clients can tolerate a refresh round trip rather than how short you can push the number. The setting that more often needs attention is the one below it.

Revoke Refresh Token is the setting that turns refresh rotation on. With it enabled, Keycloak revokes the presented refresh token and issues a new one on every refresh, so a stolen refresh token stops working the moment the legitimate client refreshes (Keycloak documentation, “Session and token timeouts,” release 26.7). The companion Refresh Token Max Reuse value should stay at zero unless you have a specific client that cannot handle rotation, because any nonzero value reopens the replay window you just closed.

Session timeouts are the third dial, and they are separate from token lifetimes. SSO Session Idle and SSO Session Max bound the user session itself, and Client Session Idle and Client Session Max bound each client session within it. On installs without persistent user sessions, Keycloak adds a two-minute grace window to idle timeouts, so a 30-minute idle timeout expires at 32. Persistent user sessions are a default feature on the 26.x line, so most current deployments will not see this.

Offline tokens deserve their own paragraph, because they are the standing-access problem hiding behind good token hygiene. An offline token survives logout by design. If your realm hands them out, set Offline Session Max Limited so they expire on a clock rather than only on idle, and treat every client with offline_access in its scope as something to justify. The full token lifecycle guide covers the interaction between these settings in more detail, and refresh token rotation in Keycloak covers the client-side handling rotation requires.

Can Keycloak issue sender-constrained tokens?

It can, two ways, and both are production features in Keycloak 26.x rather than preview flags. A sender-constrained token is bound to a key the client holds, so an attacker who steals the token still cannot use it without the key. That is the property the announcements point at when they name mutual TLS and demonstrating proof of possession.

DPoP (RFC 9449) binds the token to a key pair the client proves possession of on every request. DPoP was a preview feature from Keycloak 23 and became supported in 26.4; in the 26.7 source it is registered as a default-type feature, meaning it needs no --features flag (Keycloak source, Profile.Feature, release 26.7 branch). Turn it on per client with the dpop.bound.access.tokens attribute, or the Require DPoP bound tokens switch in the client’s Capability config block on the Settings tab.

Mutual TLS certificate-bound access tokens (RFC 8705) bind the token to the client certificate presented on the TLS connection. The client attribute is tls.client.certificate.bound.access.tokens, exposed in the admin console as OAuth 2.0 Mutual TLS Certificate Bound Access Tokens Enabled, and unlike the DPoP switch this one does live in the client’s Advanced settings.

Choosing between them is mostly an infrastructure question. Certificate binding needs client certificates to survive to the Keycloak instance and to your resource servers, which is easy inside a service mesh and painful through a load balancer that terminates TLS and forgets. DPoP works at the application layer and survives proxies, which makes it the usual answer for public clients, mobile apps and anything that reaches you over the internet. Neither is free: both require every resource server to validate the binding, and a resource server that ignores the confirmation claim turns a sender-constrained token straight back into a bearer token.

Start where the tokens are most valuable. Administrative APIs and anything with write access to customer data earn the binding first. The DPoP walkthrough against the Keycloak Admin API is a concrete example of what the client side looks like.

How do you enforce audience and issuer validation?

Audience restriction is the control that separates a token from being a skeleton key, and in Keycloak it has two halves that teams routinely implement only one of.

On the issuing side, use audience mappers so the aud claim names the specific resource servers a token is meant for, rather than letting it default to something broad. Keycloak’s audience resolve mapper populates aud from the client roles in the token, which is convenient and also means a client granted roles on three services gets a token valid at all three, which is sometimes what you want and frequently is not.

On the consuming side, every resource server must verify iss against your realm’s issuer URL, verify aud contains its own identifier, verify the signature against the realm JWKS, and verify exp. Skipping the audience check is the common failure, because the token validates cryptographically and the code works.

# The two claims that carry the restriction
"iss": "https://sso.example.com/realms/production",
"aud": ["payments-api"],

The guide to verifying Keycloak access tokens on the backend covers the validation order, and token introspection versus local validation covers the trade-off between validating locally with JWKS and calling the introspection endpoint. For checking what a specific token actually carries, the JWT token analyzer decodes claims in the browser without sending the token anywhere. That trade-off matters for IR 8587 specifically: local validation is fast and cannot see a revocation, while introspection sees revocation immediately and puts your identity provider in the hot path of every request. Most architectures end up mixing them, with short-lived local validation for ordinary reads and introspection for privileged operations.

SAML assertions carry the same requirements in different vocabulary. AudienceRestriction is the audience check, NotBefore and NotOnOrAfter are the lifetime, and assertion signing plus signature validation at the service provider is the forgery control. The Keycloak SAML service provider guide covers the settings; the point worth carrying over is that a SAML deployment needs the same evidence as an OIDC one.

What about signing keys and JWKS hygiene?

Key management is where forgery is prevented, and Keycloak’s model makes the easy path mostly correct. Each realm holds its own keys, generated per realm, published at the realm’s JWKS endpoint, and used to sign tokens and assertions.

Four practices cover most of what IR 8587’s key management and verification recommendations imply:

  • Rotate by adding, not replacing. Add a new key provider with a higher priority, let Keycloak sign new tokens with it, and keep the old key in the JWKS as passive until every token signed with it has expired. Deleting the old key immediately invalidates live sessions.
  • Pin the algorithm. Set the realm’s Default Signature Algorithm deliberately and make resource servers reject anything else. A validator that accepts whatever the token header claims is a forgery vector.
  • Cache the JWKS with a bounded TTL. Resource servers that fetch JWKS on every request will take your identity provider down under load, and ones that cache forever will reject valid tokens after a rotation. A TTL in minutes rather than hours, with a refresh triggered on an unknown kid, covers both.
  • Keep the private keys where you can attest to them. Realm keys in the database are the default. An HSM or external vault through a custom key provider is the step up, and it is the step an assessor will ask about.

The JWKS verifier is a quick way to confirm that a realm is publishing the keys you think it is, and that a given token’s kid resolves against them.

Where does Keycloak fall short of what IR 8587 wants?

Three places, and they are worth naming plainly rather than papering over.

Risk-based conditional access. As above, Keycloak gives you conditional flows and step-up, not device posture or behavioural scoring. If your control narrative depends on bucket three, that signal source is a separate build or a separate product.

Shared signals. The announcements list global revocation and shared signals among emerging standards that might support these outcomes, which is the territory the Shared Signals Framework and CAEP occupy. Keycloak registers SSF as an experimental feature in the 26.7 line (Keycloak source, Profile.Feature, release 26.7 branch), and what ships is a transmitter rather than a receiver, so Keycloak can emit CAEP and RISC events but does not consume anyone else’s. The workable approximation today is short token lifetimes plus introspection on privileged paths plus event-driven revocation through the admin API, which gets you continuous-ish evaluation at the cost of writing the plumbing yourself. Keycloak CAEP and shared signals covers where that stands and what the approximation looks like.

Operational evidence. Everything above is a setting, and settings drift. These controls are not hard to turn on; they are hard to keep on across a year of realm imports, client copies, version upgrades and staff changes. Our reading of why the announcements stress documenting the token architecture is that documentation is the only part of this that survives staff turnover, and no product feature supplies it.

That last point is also the case for a managed deployment rather than a self-hosted one. Skycloak runs upstream Keycloak, so every control here behaves as upstream documents it, and what changes is who owns patch cadence, key rotation and the evidence trail. The self-hosting cost analysis works through that trade-off without assuming the answer, and zero trust authentication with Keycloak covers the broader architecture these controls sit inside.

A working checklist

For a platform team that wants the short version, in the order we would actually do it:

Control Where Target
Access token lifespan Realm settings, Tokens 5 to 15 minutes
Refresh rotation Realm settings, Tokens Revoke Refresh Token on, Max Reuse 0
Offline token bound Realm settings, Sessions Offline Session Max Limited on
Audience restriction Client scopes, mappers aud names specific resource servers
Consumer validation Every resource server iss, aud, exp, signature, algorithm
Sender constraint Client: DPoP in Capability config, mTLS in Advanced settings Binding on privileged clients
Signing key rotation Realm settings, Keys Add-then-retire, never delete-in-place
Revocation path Admin API, revoke endpoint Tested, scripted, in the runbook
Event logging Realm settings, Events tab User and admin events on, forwarded off-box
Session bounds Realm settings, Sessions Idle and max set, session management reviewed

The last row is the one most often left for later. Keycloak auditing and event logging covers what each event type contains, and it is worth turning on before an incident rather than during one.

Frequently asked questions

Does NIST IR 8587 apply to private companies?

It does not apply as a requirement to anyone. An interagency report is guidance rather than a mandate even for agencies, whose obligations flow from SP 800-53 controls that IR 8587 expands on. The practices it describes are ordinary token security, so private organizations commonly adopt it as a reference, particularly those selling into federal or regulated buyers who will ask about it.

Is DPoP supported in Keycloak or still preview?

It is supported, and has been since Keycloak 26.4 after several releases in preview. In the 26.7 source, DPoP is registered as a default-type feature, which means it ships enabled without a --features flag. Enabling it for a given client is a per-client setting through the dpop.bound.access.tokens attribute or the Require DPoP bound tokens switch in the client’s Capability config.

What is the difference between DPoP and mTLS token binding?

Both bind a token to a key the client holds, so a stolen token is unusable alone. Mutual TLS binds to the client certificate on the TLS connection, which requires that certificate to survive to your servers. DPoP binds at the application layer with a per-request proof, so it works through proxies and load balancers that terminate TLS.

How short should a Keycloak access token lifespan be?

Short enough that a stolen token expires before it is useful, long enough that refresh traffic does not become your bottleneck. Five to fifteen minutes suits most workforce and customer realms. The number matters less than pairing it with refresh rotation, because a long-lived refresh token undoes a short access token.

Can Keycloak revoke a token immediately?

It can revoke the token at the identity provider through the revocation endpoint or by ending the session, and introspection will report it inactive straight away. Resource servers doing local JWKS validation will keep accepting it until it expires, because they never ask. That gap is why short lifetimes and introspection on privileged paths are complementary rather than alternatives.

Does Keycloak support the Shared Signals Framework?

Only as an experimental feature in the 26.7 line, and only as a transmitter, so Keycloak can emit CAEP and RISC events but does not consume signals from other providers. That makes it something to evaluate rather than build production compliance on. Teams needing continuous evaluation today usually combine short token lifetimes, introspection on sensitive operations, and admin-API-driven session revocation triggered by their own security event pipeline.

Sources

The IR 8587 references below are cited from the NIST and CISA announcements of the final report. We have not read the full publication, so nothing in this post should be taken as a summary of its text beyond what those announcements state.

  • NIST, “NIST Finalizes Guidelines on Protecting Online Identity and Access Tokens From Misuse,” published 15 September 2026, https://www.nist.gov/news-events/news/2026/09/nist-finalizes-guidelines-protecting-online-identity-and-access-tokens
  • NIST, “NISTIR 8587: Protecting Tokens and Assertions from Forgery, Theft, and Misuse: Implementation Recommendations for Agencies and Cloud Service Providers,” final, published 15 September 2026, https://csrc.nist.gov/pubs/ir/8587/final
  • CISA, “Protecting Tokens and Assertions from Forgery, Theft, and Misuse,” https://www.cisa.gov/resources-tools/resources/protecting-tokens-and-assertions-forgery-theft-and-misuse
  • Keycloak, “Session and token timeouts,” server administration guide, release 26.7, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/docs/documentation/server_admin/topics/sessions/timeouts.adoc
  • Keycloak, Profile.Feature source, DPoP as a default feature and Shared Signals Framework as experimental, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/common/src/main/java/org/keycloak/common/Profile.java
  • Keycloak, OIDCConfigAttributes source, dpop.bound.access.tokens and tls.client.certificate.bound.access.tokens client attributes, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/server-spi-private/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java
  • Keycloak, ApplianceBootstrap source, master realm access token lifespan of 60 seconds, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
  • IETF, “RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP),” https://datatracker.ietf.org/doc/html/rfc9449
  • IETF, “RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens,” https://datatracker.ietf.org/doc/html/rfc8705

Patched on the day, not on your next maintenance window

Keycloak 26.7.2 fixed CVE-2026-18963, an account takeover through password reset. Skycloak had it available for auto-upgrade and told customers the same day upstream shipped it. Self-hosted teams schedule that work themselves.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales

NIST IR 8587 Token Protection: A Keycloak Control Map

Guilliano Molaire Guilliano Molaire 14 min read

Last updated: September 2026

NIST finalized Interagency Report 8587, “Protecting Tokens and Assertions from Forgery, Theft, and Misuse,” on 15 September 2026, and most of the control outcomes its announcement highlights are already a switch in Keycloak rather than a project. The report is written for federal agencies and the cloud service providers they buy from, but the control set is the ordinary token hygiene any identity platform should be able to evidence: short lifetimes, sender-constrained tokens, strict audience and issuer validation, disciplined signing-key management, working revocation, and enough logging to reconstruct an incident. Keycloak 26.x covers those directly. DPoP has been supported rather than preview since 26.4, mutual-TLS certificate binding is a per-client toggle, and refresh rotation is one realm setting. Two things it does not cover are the risk-signal side of fine-grained conditional access, where Keycloak gives you conditional flows rather than a device and behaviour engine, and shared signals, which is still experimental.

Two things worth stating up front. This post maps control outcomes to Keycloak configuration; it is not a compliance attestation, and if you are in scope for IR 8587 your assessor will want the actual document. And the IR 8587 material here comes from the NIST and CISA announcements of the final report rather than from the full text, so treat the control list below as the publicly summarised outcomes rather than a section-by-section map. The report also carries material on AI and post-quantum migration that is outside what this post covers.

What does NIST IR 8587 ask of identity providers?

In 2026, NIST finalized IR 8587 in coordination with CISA, aimed at federal agencies and cloud service providers, with the stated purpose of protecting identity tokens and assertions from forgery, theft and misuse (NIST, “NIST Finalizes Guidelines on Protecting Online Identity and Access Tokens From Misuse,” 15 September 2026). The announcement describes guidance that builds on NIST SP 800-53 and sets out principles for both providers and consuming agencies, with architectural considerations for identity providers and authorization servers.

The recommendations named in the NIST and CISA announcements group into six buckets, and they map onto things a Keycloak realm either does or does not do:

  1. Sender-constrained tokens, with mutual TLS and demonstrating proof of possession named specifically
  2. Explicit audience restriction, so a token issued for one service is not accepted by another
  3. Fine-grained conditional-access decisions informed by device, network, geolocation and behavioural context
  4. Key management, token verification and lifecycle controls
  5. Revocation, with global revocation and shared signals listed among emerging standards that may support these outcomes
  6. Documentation of the full token architecture: protocols, lifetimes, validation paths, key management, session controls, revocation, logging and incident response

Bucket three is the one where Keycloak is least able to answer on its own, and it is worth saying so before working through the rest. Keycloak gives you conditional authentication flows keyed on role, client scope, user attribute, credential type and level of authentication, plus step-up. It does not ship a risk engine that scores device posture, network reputation, geolocation or behaviour. Those signals have to come from something else and be fed in, through a custom authenticator or an identity provider that already holds them.

Bucket six is the one teams underestimate. Every control below is configurable in an afternoon, and writing down which ones you chose and why is the part that makes a configured realm auditable.

Token theft, forgery and misuse: why does the distinction matter?

The three words in the report’s title describe three different failures with three different fixes, which is exactly why “we have MFA” does not answer any of them.

Theft means a valid token ends up in the wrong hands, whether lifted from a browser session, a log file, or an adversary-in-the-middle proxy. The token is genuine, so signature validation passes. The controls that help are short lifetimes and sender constraints, because a bearer token is only useful to a thief for as long as it lives and only if possession alone is sufficient. Token theft is the failure mode that survives MFA, which is why it gets its own guidance rather than being folded into authentication advice.

Forgery means an attacker mints a token the identity provider never issued, or alters one it did. This is a key management and verification problem: private key protection, algorithm pinning, and consumers that actually verify the signature against the published JWKS rather than decoding and trusting.

Misuse means a genuine token used outside its intended scope, audience or context. A token minted for an internal reporting service and replayed against the payments API is misuse, and neither a short lifetime nor a signature check catches it, because audience restriction is the control that does.

MFA sits before all three. It governs how a token gets issued, not what happens to it afterward, which is the point the announcements open on: a stolen token can bypass both the password and the second factor.

How do you get short lifetimes and rotation right in Keycloak?

Access token lifespan and refresh rotation live in Realm settings, then Tokens, and they are the two dials that most directly shrink the theft window.

Access Token Lifespan controls how long an issued access token stays valid. Keycloak’s defaults here are already conservative, which is worth knowing before you go looking for a problem that is not there: the bootstrapped master realm is set to 60 seconds in the source, and a new realm lands at 5 minutes. Values in the five to fifteen minute range are typical for a workforce realm, and the constraint is how often your clients can tolerate a refresh round trip rather than how short you can push the number. The setting that more often needs attention is the one below it.

Revoke Refresh Token is the setting that turns refresh rotation on. With it enabled, Keycloak revokes the presented refresh token and issues a new one on every refresh, so a stolen refresh token stops working the moment the legitimate client refreshes (Keycloak documentation, “Session and token timeouts,” release 26.7). The companion Refresh Token Max Reuse value should stay at zero unless you have a specific client that cannot handle rotation, because any nonzero value reopens the replay window you just closed.

Session timeouts are the third dial, and they are separate from token lifetimes. SSO Session Idle and SSO Session Max bound the user session itself, and Client Session Idle and Client Session Max bound each client session within it. On installs without persistent user sessions, Keycloak adds a two-minute grace window to idle timeouts, so a 30-minute idle timeout expires at 32. Persistent user sessions are a default feature on the 26.x line, so most current deployments will not see this.

Offline tokens deserve their own paragraph, because they are the standing-access problem hiding behind good token hygiene. An offline token survives logout by design. If your realm hands them out, set Offline Session Max Limited so they expire on a clock rather than only on idle, and treat every client with offline_access in its scope as something to justify. The full token lifecycle guide covers the interaction between these settings in more detail, and refresh token rotation in Keycloak covers the client-side handling rotation requires.

Can Keycloak issue sender-constrained tokens?

It can, two ways, and both are production features in Keycloak 26.x rather than preview flags. A sender-constrained token is bound to a key the client holds, so an attacker who steals the token still cannot use it without the key. That is the property the announcements point at when they name mutual TLS and demonstrating proof of possession.

DPoP (RFC 9449) binds the token to a key pair the client proves possession of on every request. DPoP was a preview feature from Keycloak 23 and became supported in 26.4; in the 26.7 source it is registered as a default-type feature, meaning it needs no --features flag (Keycloak source, Profile.Feature, release 26.7 branch). Turn it on per client with the dpop.bound.access.tokens attribute, or the Require DPoP bound tokens switch in the client’s Capability config block on the Settings tab.

Mutual TLS certificate-bound access tokens (RFC 8705) bind the token to the client certificate presented on the TLS connection. The client attribute is tls.client.certificate.bound.access.tokens, exposed in the admin console as OAuth 2.0 Mutual TLS Certificate Bound Access Tokens Enabled, and unlike the DPoP switch this one does live in the client’s Advanced settings.

Choosing between them is mostly an infrastructure question. Certificate binding needs client certificates to survive to the Keycloak instance and to your resource servers, which is easy inside a service mesh and painful through a load balancer that terminates TLS and forgets. DPoP works at the application layer and survives proxies, which makes it the usual answer for public clients, mobile apps and anything that reaches you over the internet. Neither is free: both require every resource server to validate the binding, and a resource server that ignores the confirmation claim turns a sender-constrained token straight back into a bearer token.

Start where the tokens are most valuable. Administrative APIs and anything with write access to customer data earn the binding first. The DPoP walkthrough against the Keycloak Admin API is a concrete example of what the client side looks like.

How do you enforce audience and issuer validation?

Audience restriction is the control that separates a token from being a skeleton key, and in Keycloak it has two halves that teams routinely implement only one of.

On the issuing side, use audience mappers so the aud claim names the specific resource servers a token is meant for, rather than letting it default to something broad. Keycloak’s audience resolve mapper populates aud from the client roles in the token, which is convenient and also means a client granted roles on three services gets a token valid at all three, which is sometimes what you want and frequently is not.

On the consuming side, every resource server must verify iss against your realm’s issuer URL, verify aud contains its own identifier, verify the signature against the realm JWKS, and verify exp. Skipping the audience check is the common failure, because the token validates cryptographically and the code works.

# The two claims that carry the restriction
"iss": "https://sso.example.com/realms/production",
"aud": ["payments-api"],

The guide to verifying Keycloak access tokens on the backend covers the validation order, and token introspection versus local validation covers the trade-off between validating locally with JWKS and calling the introspection endpoint. For checking what a specific token actually carries, the JWT token analyzer decodes claims in the browser without sending the token anywhere. That trade-off matters for IR 8587 specifically: local validation is fast and cannot see a revocation, while introspection sees revocation immediately and puts your identity provider in the hot path of every request. Most architectures end up mixing them, with short-lived local validation for ordinary reads and introspection for privileged operations.

SAML assertions carry the same requirements in different vocabulary. AudienceRestriction is the audience check, NotBefore and NotOnOrAfter are the lifetime, and assertion signing plus signature validation at the service provider is the forgery control. The Keycloak SAML service provider guide covers the settings; the point worth carrying over is that a SAML deployment needs the same evidence as an OIDC one.

What about signing keys and JWKS hygiene?

Key management is where forgery is prevented, and Keycloak’s model makes the easy path mostly correct. Each realm holds its own keys, generated per realm, published at the realm’s JWKS endpoint, and used to sign tokens and assertions.

Four practices cover most of what IR 8587’s key management and verification recommendations imply:

  • Rotate by adding, not replacing. Add a new key provider with a higher priority, let Keycloak sign new tokens with it, and keep the old key in the JWKS as passive until every token signed with it has expired. Deleting the old key immediately invalidates live sessions.
  • Pin the algorithm. Set the realm’s Default Signature Algorithm deliberately and make resource servers reject anything else. A validator that accepts whatever the token header claims is a forgery vector.
  • Cache the JWKS with a bounded TTL. Resource servers that fetch JWKS on every request will take your identity provider down under load, and ones that cache forever will reject valid tokens after a rotation. A TTL in minutes rather than hours, with a refresh triggered on an unknown kid, covers both.
  • Keep the private keys where you can attest to them. Realm keys in the database are the default. An HSM or external vault through a custom key provider is the step up, and it is the step an assessor will ask about.

The JWKS verifier is a quick way to confirm that a realm is publishing the keys you think it is, and that a given token’s kid resolves against them.

Where does Keycloak fall short of what IR 8587 wants?

Three places, and they are worth naming plainly rather than papering over.

Risk-based conditional access. As above, Keycloak gives you conditional flows and step-up, not device posture or behavioural scoring. If your control narrative depends on bucket three, that signal source is a separate build or a separate product.

Shared signals. The announcements list global revocation and shared signals among emerging standards that might support these outcomes, which is the territory the Shared Signals Framework and CAEP occupy. Keycloak registers SSF as an experimental feature in the 26.7 line (Keycloak source, Profile.Feature, release 26.7 branch), and what ships is a transmitter rather than a receiver, so Keycloak can emit CAEP and RISC events but does not consume anyone else’s. The workable approximation today is short token lifetimes plus introspection on privileged paths plus event-driven revocation through the admin API, which gets you continuous-ish evaluation at the cost of writing the plumbing yourself. Keycloak CAEP and shared signals covers where that stands and what the approximation looks like.

Operational evidence. Everything above is a setting, and settings drift. These controls are not hard to turn on; they are hard to keep on across a year of realm imports, client copies, version upgrades and staff changes. Our reading of why the announcements stress documenting the token architecture is that documentation is the only part of this that survives staff turnover, and no product feature supplies it.

That last point is also the case for a managed deployment rather than a self-hosted one. Skycloak runs upstream Keycloak, so every control here behaves as upstream documents it, and what changes is who owns patch cadence, key rotation and the evidence trail. The self-hosting cost analysis works through that trade-off without assuming the answer, and zero trust authentication with Keycloak covers the broader architecture these controls sit inside.

A working checklist

For a platform team that wants the short version, in the order we would actually do it:

Control Where Target
Access token lifespan Realm settings, Tokens 5 to 15 minutes
Refresh rotation Realm settings, Tokens Revoke Refresh Token on, Max Reuse 0
Offline token bound Realm settings, Sessions Offline Session Max Limited on
Audience restriction Client scopes, mappers aud names specific resource servers
Consumer validation Every resource server iss, aud, exp, signature, algorithm
Sender constraint Client: DPoP in Capability config, mTLS in Advanced settings Binding on privileged clients
Signing key rotation Realm settings, Keys Add-then-retire, never delete-in-place
Revocation path Admin API, revoke endpoint Tested, scripted, in the runbook
Event logging Realm settings, Events tab User and admin events on, forwarded off-box
Session bounds Realm settings, Sessions Idle and max set, session management reviewed

The last row is the one most often left for later. Keycloak auditing and event logging covers what each event type contains, and it is worth turning on before an incident rather than during one.

Frequently asked questions

Does NIST IR 8587 apply to private companies?

It does not apply as a requirement to anyone. An interagency report is guidance rather than a mandate even for agencies, whose obligations flow from SP 800-53 controls that IR 8587 expands on. The practices it describes are ordinary token security, so private organizations commonly adopt it as a reference, particularly those selling into federal or regulated buyers who will ask about it.

Is DPoP supported in Keycloak or still preview?

It is supported, and has been since Keycloak 26.4 after several releases in preview. In the 26.7 source, DPoP is registered as a default-type feature, which means it ships enabled without a --features flag. Enabling it for a given client is a per-client setting through the dpop.bound.access.tokens attribute or the Require DPoP bound tokens switch in the client’s Capability config.

What is the difference between DPoP and mTLS token binding?

Both bind a token to a key the client holds, so a stolen token is unusable alone. Mutual TLS binds to the client certificate on the TLS connection, which requires that certificate to survive to your servers. DPoP binds at the application layer with a per-request proof, so it works through proxies and load balancers that terminate TLS.

How short should a Keycloak access token lifespan be?

Short enough that a stolen token expires before it is useful, long enough that refresh traffic does not become your bottleneck. Five to fifteen minutes suits most workforce and customer realms. The number matters less than pairing it with refresh rotation, because a long-lived refresh token undoes a short access token.

Can Keycloak revoke a token immediately?

It can revoke the token at the identity provider through the revocation endpoint or by ending the session, and introspection will report it inactive straight away. Resource servers doing local JWKS validation will keep accepting it until it expires, because they never ask. That gap is why short lifetimes and introspection on privileged paths are complementary rather than alternatives.

Does Keycloak support the Shared Signals Framework?

Only as an experimental feature in the 26.7 line, and only as a transmitter, so Keycloak can emit CAEP and RISC events but does not consume signals from other providers. That makes it something to evaluate rather than build production compliance on. Teams needing continuous evaluation today usually combine short token lifetimes, introspection on sensitive operations, and admin-API-driven session revocation triggered by their own security event pipeline.

Sources

The IR 8587 references below are cited from the NIST and CISA announcements of the final report. We have not read the full publication, so nothing in this post should be taken as a summary of its text beyond what those announcements state.

  • NIST, “NIST Finalizes Guidelines on Protecting Online Identity and Access Tokens From Misuse,” published 15 September 2026, https://www.nist.gov/news-events/news/2026/09/nist-finalizes-guidelines-protecting-online-identity-and-access-tokens
  • NIST, “NISTIR 8587: Protecting Tokens and Assertions from Forgery, Theft, and Misuse: Implementation Recommendations for Agencies and Cloud Service Providers,” final, published 15 September 2026, https://csrc.nist.gov/pubs/ir/8587/final
  • CISA, “Protecting Tokens and Assertions from Forgery, Theft, and Misuse,” https://www.cisa.gov/resources-tools/resources/protecting-tokens-and-assertions-forgery-theft-and-misuse
  • Keycloak, “Session and token timeouts,” server administration guide, release 26.7, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/docs/documentation/server_admin/topics/sessions/timeouts.adoc
  • Keycloak, Profile.Feature source, DPoP as a default feature and Shared Signals Framework as experimental, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/common/src/main/java/org/keycloak/common/Profile.java
  • Keycloak, OIDCConfigAttributes source, dpop.bound.access.tokens and tls.client.certificate.bound.access.tokens client attributes, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/server-spi-private/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java
  • Keycloak, ApplianceBootstrap source, master realm access token lifespan of 60 seconds, release 26.7 branch, retrieved 2026-09-16, https://github.com/keycloak/keycloak/blob/release/26.7/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
  • IETF, “RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP),” https://datatracker.ietf.org/doc/html/rfc9449
  • IETF, “RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens,” https://datatracker.ietf.org/doc/html/rfc8705

Patched on the day, not on your next maintenance window

Keycloak 26.7.2 fixed CVE-2026-18963, an account takeover through password reset. Skycloak had it available for auto-upgrade and told customers the same day upstream shipped it. Self-hosted teams schedule that work themselves.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman