Security17 min

API security for the CTO: identity, gateway and tokens, without trusting blindly

By Dorian Chávez · founder of Hábil and integration architect ·

When to revalidate the token behind the gateway, how APIs call each other, introspection or local validation, logout and how long a token should last.

A frequent API security architecture today has three pieces: an identity provider that issues the tokens —Keycloak, Microsoft Entra ID, Okta or another—, a gateway that receives all the traffic —APISIX, Kong, Apigee, Azure API Management or another— and OAuth 2.0 as the language in which permissions are requested and presented. It is a good architecture. The problem shows up in the questions nobody wrote down when designing it: does the service behind the gateway check the token again? What happens when one API calls another? If the user logs out, does their token stop working? How long should a token last?

This article answers those questions for whoever has to sign off on the architecture: the CTO of a bank, a fintech, an insurer, a retail chain or any regulated company. Each answer comes with its condition, because in security there is almost never an "always."

Three types of route, three levels of trust

Before talking about tokens, it helps to classify each route of the API:

Three types of route, three levels of trust
Route typeWhat it is forWhat it provesWhat it doesn't prove
PublicCatalogs, general information, service healthNothingWho is calling
With API keyIdentifying a consumer, measuring and limiting their usageThat the caller has the keyWho the person is, what they consented to, or whether the key was stolen
With OAuth tokenAccess with permissions, on behalf of a system or a personWith an OAuth access token configured for this API: lets you verify the attributes the profile and the provider issue —for example, issuer, audience, validity and permissions— through JWT validation or introspectionThat the person is still authorized right now, if the token is long-lived

An API key identifies, but doesn't authorize well: it carries no fine-grained permissions, no indication of who it is addressed to, no standard expiry, no delegation [1][2]. It works for simple consumption, metering and quotas. For an integration between companies (B2B) with sensitive data, OAuth 2.0 with client credentials is the better fit: the partner system obtains, via API, a short-lived token with narrow permissions, addressed to your API [3][4]. This route applies when the system acts on its own behalf or under a prior agreement; it does not replace a person's consent. If you keep API keys: one per consumer and per environment, stored as a secret, rotated and never in the URL, because URLs end up written in the logs of the gateway, the proxies and the monitoring tools, where anyone with access can read them [2].

Should the service behind the gateway check the token?

It is the question that generates the most arguments, and the short answer is: being behind the gateway does not grant trust by itself. The NIST zero trust framework says so: network location does not imply trust, and the implicit trust zone must be as small as possible [5].

The full answer separates two things that are often confused:

  • Validating the token (is it authentic, did my provider issue it, is it meant for me, is it still valid?).
  • Authorizing the operation (can this person or this system read this account, this policy, this order?).

The second, fine-grained authorization on the object, must be evaluated where the business rule and the relationship to the object are available: normally in the service or in a policy component integrated with it. The gateway can complement it with coarse-grained controls, but it doesn't replace that rule. A generic permission like "read payments" doesn't decide whether someone can read another customer's payment. That mistake has a name and is the first on OWASP's list of API risks [6][7].

The first can be delegated to the gateway, but only if all of these conditions are met [5][8][9]:

  1. The service cannot be reached by any path other than the gateway: not from another network, not from a neighboring service, not through an administrative route.
  2. The channel between the gateway and the service is authenticated, for example with mutual TLS.
  3. The gateway removes any identity header that arrives from outside and sets its own, signed (for example, an internal token) or over an authenticated channel, so that the service can verify it comes from the gateway.
  4. The route configuration is inventoried, tested and fails closed: a route without a rule is not left open.

If a condition is not met, document another policy enforcement point that verifiably validates the caller's identity and context; if none exists, the service must do it. And there are cases where it should validate it even if all four are met: when it handles money or personal data, when it decides based on data in the token, or when it serves several customers or domains.

A configuration detail worth its weight in gold: in some gateways, the plugins that validate API keys or JWTs forward the original credential to the service behind them unless configured otherwise, and the OpenID Connect one, with bearer_only set to false, does not strictly require a bearer token [10][11]. These are the values documented in APISIX 3.19 [vendor]; confirm the installed version and the effective configuration. Reviewing those default values is part of the architecture, not a detail.

When one API calls another

Inside the architecture, a service needs to call another. There are five common patterns, and each serves a different purpose [12][13][14]:

When one API calls another
Is there a user behind it?PatternUse it whenRisk if misused
YesForward the user's tokenThe target service is declared as a recipient of the tokenThe intermediary holds a token that works for other services
YesExchange the token (token exchange, RFC 8693)The target needs to know who the user is, but with its own token, reduced and addressed to itThe exchange allows requesting any permission for any target
NoThe service's own credentialsTechnical work, batch processes, events, reconciliation: there is no user behind itAttributing to a user an action the system performed
YesShort-lived internal tokenThe gateway swaps the external token for an internal one, valid only insideUnintentionally creating a second, ungoverned identity provider
NoOnly mutual TLS between servicesIt is enough to know which service is callingThe certificate proves the service, not the user's permission

A rule that reduces the risk of improper reuse: each service verifies that the token was addressed to it. The field that says so is called the audience (aud), and if the service is not in it, the token is rejected [15][16]; to restrict the destination when requesting it, there are resource indicators (RFC 8707) [30]. This avoids the mistake of "blind forwarding" (token passthrough) and its best-known consequence, the confused deputy: a service that, with a token that isn't its own, does something the user never authorized. For example, a reporting service that receives a token issued for the payments service and, with it, ends up able to execute payments.

For the chain "A calls B on behalf of the user," the recommended pattern is token exchange: A swaps the token it received for a new one, addressed to B and with fewer permissions [12]. In Keycloak, the standard V2 feature is enabled by default, but the requesting client must be confidential and have Standard token exchange enabled; standard exchange operates between clients of the same realm [17]. And NIST proposes something similar for microservices: swapping the external token at the entry point for a short-lived internal one, and verifying it at every hop [8].

Trust the token or ask the provider?

There are two ways to know whether a token is good:

Trust the token or ask the provider?
MethodFits whenAdvantageLimit
Local validation of the JWT (signature with the provider's public keys, issuer, audience, expiry)High volume, low latencyDoesn't depend on the provider being available on every requestDoesn't find out if the token was revoked after being issued
Introspection (RFC 7662): asking the providerOpaque tokens, or actions where current state mattersAnswers whether the token is still active nowAdds latency and dependence on the provider
MixedHigh-volume regulated APIsLocal validation for the common case, introspection for the criticalYou have to decide and document what is "critical"

Validating locally is not just "the signature passed": you have to fix the expected issuer, accept only the intended algorithms, check audience, expiry and permissions, and obtain the keys from a trusted place, because the provider rotates them: the service caches the public keys and, when a token arrives signed with a key it doesn't know, fetches them again instead of rejecting it or accepting it blindly [18][19]. And a cost the CTO signs off on: with introspection on every request, the identity provider sits on the critical path of every operation; if it goes down or slows down, everything goes down or slows down. And if the result of an introspection is cached, the cache time cannot be longer than the revocation window the business accepts: a five-minute cache contradicts a promise of "immediate" revocation.

Logging out doesn't turn off tokens already issued

An operationally relevant effect, one that often surprises a security committee: when a user logs out, the access token they already had remains valid until it expires, for any service that validates it locally. OpenID Connect's logout mechanisms end the session and notify the applications, but they don't invalidate the access tokens already in circulation [20][21]; Keycloak's documentation warns about it explicitly [17].

That is why token lifetime is designed in layers:

  1. Short access tokens, so that the exposure window is small.
  2. Revocable refresh tokens, bound to the client and, in public applications, with rotation [3].
  3. Introspection in the operations where current state matters: transfers, beneficiary changes, bulk downloads, administrative actions.
  4. Revocation (RFC 7009) when the provider supports it [22].
  5. Revocation events between systems (OpenID CAEP), which are already a final standard; provider support is still uneven [23].

How long should a token last?

There is no "standard" duration in any specification. OAuth leaves token lifetime to the provider [4]; the bearer token standard recommends one hour or less [24]; and the open banking security profile (FAPI 2.0) calls for short tokens without setting a figure [25]. What there are, are initial design ranges —an editorial criterion, not a standard—; adjust them to exposure, revocation window, re-authentication and provider availability:

How long should a token last?
TokenStarting pointComment
A person's access token5 minutes; 1 to 5 for payments and administrationOnly if the refresh token is rotated and bound to the client; otherwise a somewhat longer lifetime is advisable, compensating with introspection on the critical
Access token between companies (client credentials)5 to 15 minutesRenewed by authenticating the system again
Refresh tokenAs long as the session lasts: inactivity and maximumDon't silently renew an inactive session
"Offline" (long-lived) tokenOnly as an approved exceptionWith an explicit maximum and revocation

Keycloak's out-of-the-box values [vendor] are along those lines. We verified them directly in its source code (main branch consulted on 6 October 2026), because the documentation doesn't publish them in full [26]. They are vendor figures and may change between versions:

How long should a token last?
Keycloak settingDefault value
Access token lifespan5 minutes (1 minute in the "master" administrative realm)
Session: idle30 minutes
Session: maximum10 hours
Offline session: idle30 days (its maximum is disabled by default)

In Keycloak, the refresh token of a normal session lives as long as the session allows. Before relying on these numbers, check those of your installation: per-client settings and imports change them.

When do you need OpenID Connect?

  • OAuth 2.0 is enough when what is being protected is the access of an application or a system to an API: integrations between companies, internal processes, permissions by scope [4].
  • OpenID Connect fits when the application needs interoperable authenticated identity, authentication claims or single sign-on (also authentication level, logout and re-authentication for sensitive actions) [27]. OAuth alone does not standardize that identity: OpenID Connect is the identity layer on top of OAuth.
  • A frequent mistake: using the OpenID Connect ID token to call an API. The ID token is for the application that started the login; the access token is what gets sent to the API [27].

In open banking, the bar goes up

If your API falls into the world of open banking or financial data, the FAPI 2.0 profile (final standard) raises the bar: tokens bound to whoever uses them —with mutual TLS or DPoP—, strong customer authentication and protected request parameters [25][28][29]. It is not mandatory for all APIs, nor is it a Mexican obligation by itself: it can be a useful technical reference for high-value APIs when the applicable framework, a contract or the risk policy adopts it, and it does not replace the assessment of applicable regulatory obligations.

Where to start

  1. Classify your routes as public, with API key and with token, and check that each one is where it should be.
  2. Decide, service by service, who validates the token, with the four conditions above in writing.
  3. Verify the audience in each service and eliminate blind token forwarding.
  4. Review the default values of your identity provider and your gateway.
  5. Define your revocation window by type of operation, and adjust lifetimes and introspection to it.

How many of your routes would withstand that review today? The first assessment conversation is at no cost: you leave with the map of your routes by trust level and the gaps to address first. Write to us on WhatsApp.

References

  1. OWASP, REST Security Cheat Sheet (API keys). https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
  2. OWASP, OAuth2 Cheat Sheet. https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html
  3. IETF, RFC 9700, Best Current Practice for OAuth 2.0 Security (2025). https://www.rfc-editor.org/rfc/rfc9700.html
  4. IETF, RFC 6749, The OAuth 2.0 Authorization Framework, §4.4 client credentials. https://www.rfc-editor.org/rfc/rfc6749.html
  5. NIST, SP 800-207, Zero Trust Architecture. https://csrc.nist.gov/pubs/sp/800/207/final
  6. OWASP, API Security Top 10 2023 (API1: Broken Object Level Authorization). https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/
  7. OWASP, Authorization Cheat Sheet. https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
  8. NIST, SP 800-207A, A Zero Trust Architecture Model for Access Control in Cloud-Native Applications. https://csrc.nist.gov/pubs/sp/800/207/a/final
  9. NIST, SP 800-204, Security Strategies for Microservices-based Application Systems. https://csrc.nist.gov/pubs/sp/800/204/final
  10. Apache APISIX, key-auth and jwt-auth plugins (hide_credentials). https://apisix.apache.org/docs/apisix/plugins/key-auth/ · https://apisix.apache.org/docs/apisix/plugins/jwt-auth/
  11. Apache APISIX, openid-connect plugin (bearer_only, introspection, JWKS). https://apisix.apache.org/docs/apisix/plugins/openid-connect/
  12. IETF, RFC 8693, OAuth 2.0 Token Exchange. https://www.rfc-editor.org/rfc/rfc8693.html
  13. OWASP, Identity Propagation Patterns Cheat Sheet. https://cheatsheetseries.owasp.org/cheatsheets/Identity_Propagation_Patterns_Cheat_Sheet.html
  14. IETF, RFC 8705, OAuth 2.0 Mutual-TLS Client Authentication. https://www.rfc-editor.org/rfc/rfc8705.html
  15. IETF, RFC 7519, JSON Web Token, §4.1.3 (aud). https://www.rfc-editor.org/rfc/rfc7519.html
  16. IETF, RFC 9068, JWT Profile for OAuth 2.0 Access Tokens. https://www.rfc-editor.org/rfc/rfc9068.html
  17. Keycloak, Server Administration Guide and Securing Applications Guide (audience, token exchange, sessions). https://www.keycloak.org/securing-apps/token-exchange · https://www.keycloak.org/documentation
  18. OWASP, JSON Web Token Cheat Sheet. https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html
  19. IETF, RFC 7662, OAuth 2.0 Token Introspection. https://www.rfc-editor.org/rfc/rfc7662.html
  20. OpenID, Back-Channel Logout 1.0. https://openid.net/specs/openid-connect-backchannel-1_0.html
  21. OpenID, RP-Initiated Logout 1.0. https://openid.net/specs/openid-connect-rpinitiated-1_0.html
  22. IETF, RFC 7009, OAuth 2.0 Token Revocation. https://www.rfc-editor.org/rfc/rfc7009.html
  23. OpenID, CAEP 1.0 (Continuous Access Evaluation Profile). https://openid.net/specs/openid-caep-1_0-final.html
  24. IETF, RFC 6750, Bearer Token Usage, §5.3. https://www.rfc-editor.org/rfc/rfc6750.html
  25. OpenID, FAPI 2.0 Security Profile (final). https://openid.net/specs/fapi-security-profile-2_0-final.html
  26. Keycloak [vendor], source code: Constants.java and ApplianceBootstrap.java (default values), main branch consulted on 6 October 2026; links to the latest commit of each file as of that date. https://github.com/keycloak/keycloak/blob/55e0a796c7d397027c766efcad1ba47706ba8f72/server-spi-private/src/main/java/org/keycloak/models/Constants.java · https://github.com/keycloak/keycloak/blob/3575af2b5bc19fa16d3f6caf39ad1fc869c3fdda/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
  27. OpenID, OpenID Connect Core 1.0. https://openid.net/specs/openid-connect-core-1_0.html
  28. IETF, RFC 9449, OAuth 2.0 Demonstrating Proof of Possession (DPoP). https://www.rfc-editor.org/rfc/rfc9449.html
  29. OpenID, FAPI 2.0 Message Signing (final). https://openid.net/specs/fapi-message-signing-2_0-final.html
  30. IETF, RFC 8707, Resource Indicators for OAuth 2.0. https://www.rfc-editor.org/rfc/rfc8707.html