OAuth Revoke Flow
Access tokens and refresh tokens expire at some point in time. Access tokens tend to expire quickly, whereas refresh tokens last longer. Their validity period is set when created. The period for which they are valid depends on factors such as:
- How the user authenticated and when
- The client application that was used
- The scopes that the user delegated
Other contextual information can be considered when setting the duration of validity as well. When a token should be expired before this time, it should be revoked.
There is a standard API for revoking tokens. This is defined in RFC 7009. This spec defines a simple request/response protocol that is depicted in the following diagram:
- The client sends the token to be revoked along with credentials to the revoke endpoint
- The server responds an empty body and a status code
In constrast with the basic flows this flow is performed at it’s own endpoint: the revoke endpoint. The client sends a refresh token or an access token together with its credentials. The request is an HTTP POST with the token in the body.
A “hint” about the kind of token can also be included in the body; this is done using the request parameter
token_type_hint and can have a value of either
If the hint is wrong or not understood, Curity will still revoke the token if found, but the hint may speed up the request slightly.
The client authenticates in the Token part of the flow. Client authentication can be done in many ways, the most common being client secret. The following authentication mechanisms are supported in Curity:
- No authentication (public client)
- Secret in post body
- Secret using basic Authentication
- Client Assertion JWT
- Mutual TLS (mTLS) client certificate
- Asymmetric Key
- Symmetric Key
- Credential Manager
- JWKS URI
When dealing with revocation it’s important to understand the token hierarchy.
Opaque access tokens can be revoked and will become inactive in the moment of revocation. However if the refresh token is still live, new access tokens can still be issued for the client without additional user interaction.
JSON Web Tokens (JWTs) are by-value tokens. This means that all identity data and metadata are in the token itself. One piece of metadata is the expiration time. These tokens can be revoked like any other; however, the consumer (i.e., back-end API) of these tokens might not notice this revocation. There are two general solutions to this:
- Listen to revoked token events in Curity and notify the back-end APIs when a JWT is revoked
- Use the phantom token pattern instead, so that all API access is gated by a check for a by-reference token.
The latter case is recommended and explained in more details in the phantom tokens tutorials. In any case, it is important that back-end APIs do not continue to use revoked tokens. So a system of some kind is needed if by-value tokens, like JWTs, are used and those are ever going to be revoked.
The root of all tokens is a delegation. A delegation is an abstraction that represents the user’s consent to the client application to do things on his or her behalf. Sometimes this consent is interactive, while other times it is implicit (e.g., when the user consents by virtue of being an employee or by downloading and accepting the app’s license agreement). In either case, all tokens that are issued to the client are rooted in a delegation.
Many access tokens can be issued from the same delegation. Also, as tokens are refreshed, the root delegation remains the same. This hierarchy is illustrated in the following figure:
When an access token is revoked, only that token is canceled. On the other hand, if an active refresh token is revoked, then the entire delegation is revoked. Consequently, revoking an active refresh token will revoke all access tokens issued from that delegation.
This does not mean to say that a refresh token and a delegration are the same thing. For instance, if a refresh token is used to obtain a new refresh token and access token in the normal refresh flow, normally the initial refresh token is revoked and different from the newly issued one. The new one is made active and the former is deactivated. If this canceled refresh token is revoked, the delegation will not be revoked. In fact, the revocation call will have no effect.
- Response Type:
|client_id||The Client ID||yes||The ID of the requesting client|
|client_secret||The client secret||yes*||The secret of the client. *Mandatory if client authentication is of type secret, and the authentication is not done using basic authentication|
|token_type_hint||no||Can be sent to inform the server of the type of token, if not sent the server will attempt to figure out the token type.|
|token||A token||yes||The token to revoke|
The response is very simple: it’s always an HTTP 200 if the token is revoked or unknown. In such a situation, the body of the response should be disregarded.
If any of these non-200 status codes is returned, the token has not been revoked.
Revocation is a simple API that can be used to revoke tokens and delegation (the root of all tokens). It is a standard API that Curity implements and makes available in any OAuth profile. This endpoint is helpful for canceling tokens before their normal expiration time. By-value tokens, like JWTs, can be revoked, but clients may continue using them, unaware of their cancellation; to avoid this, phantom tokens can be used.