With OAuth2, a client application receives an Access Token that lets the application access a resource (the API) on behalf of the user (there might be a consent step involved if the application is considered “third-party”).
If a user logs out of the application, that action alone does not (should not) invalidate the issued token, because the user already gave consent to the application to access the resource. If anything, the application should discard any tokens previously obtained when the user logs off.
In case of revoked permissions, it’s the API itself that should be checking that the permissions of the user are appropriate for the action. Let’s make an example with Twitter. An application requests a token from Twitter to post tweets on my behalf. I approve the request and the application gets a token.
Now, say that my Twitter account gets suspended. The token that the application got might still be valid, but if the application tries to tweet on my behalf, the Twitter API itself will check that my account has been suspended and thus deny the request. So, in this case, the account is flagged as suspended in Twitter and the Twitter API makes the check.
Let’s try a different example of permissions: Gmail issues an access token for an app that wants to check my emails and notify me when I get new messages. If that application tries to access another inbox with the (still valid) access token that I approved, the request will be denied. The token is valid, but I (the subject of the token) don’t have permissions to see another person’s inbox.
So, it’s important to separate the concepts of delegated permissions given to an application and the concepts of actual authorization policies performed by the application. The token might be valid, but the operation might not be.
Having said all of the above, there might be cases where you want to invalidate tokens. Say, for instance, that a user reports that a device has been stolen. In this case, different strategies (architectural decisions) come into play.
Tokens can be stateful or stateless. JWT tokens (like the Access Tokens issued by Auth0) are stateful, they contain all the relevant information so that the recipient can validate them without reaching out to the authorization server (Auth0 in this case). This results in great performance benefits (no need to call Auth0 to validate each request) but it means that Auth0 has no control over the token once issued.
Stateless tokens, on the other hand, carry no meaning on their own (they are an obscure string) and the token audience (the API) needs to check with the authorization server to validate the token (via a “token introspection” endpoint). Auth0 does not currently use this strategy for Access Tokens.
So, how can you manage JWT Access Token revocation? Basically, you don’t, you wait for them to expire. And you choose a sensible expiration time that makes sense for your security requirements.
In comparison, Refresh Tokens, which are used by certain types of applications to “keep the user logged in” (i.e. get new access tokens when the existing ones expire) are stateless tokens, and these can be revoked (because to use a refresh token the application is talking back to Auth0, and Auth0 can keep track if the token has been revoked). In practice, you usually revoke the “grants” given to the application by the user, so that all refresh tokens obtained are invalidated. This is the strategy to use if a user loses a device, or uninstalls an application, or similar.
If you absolutely need to invalidate access tokens before they expire, you will need to add some type of access control in the API. For instance, an internal list where you can keep information like “Don’t accept tokens from user xxxx issued before some_timestamp”.
Hope that helps a bit. Happy to go clarify any points.
(that is one big wall of text, sorry about that)