Does refresh_token make sense with client credentials oauth flow? - oauth-2.0

I am testing keycloak for learning purposes. I am testing the client credentials flow token endpoint to return a jwt for rest api use.
The endpoint returns an access_token and a refresh_token (refresh token is disabled by default unless I enable it in console for the client). I can call the same token endpoint with a refresh token generated from the first client credentials call but it still requires a client secret.
Is it not possible to regenerate an access token in the client credentials flow with just a refresh token?
If not why would I ever bother to pass a grant_type of refresh_token - wouldn't I just call the client_credential flow again since they both require a client secret? I have to guess the answer will be that refresh tokens don't make sense to be used with client_credential flows?
token parameters:
refresh token parameters:

You guess right, refresh tokens don't make sense for the client_credentials grant type. Refresh tokens are used for interactive clients i.e a person. The idea of the refresh token is to remove the requirement for them to have to frequently re-authenticate e.g re-enter their username and password, whilst still allowing the token expiry time to be kept short. The reason you want to keep the expiry time short is that once it is issued it is usually not possible to revoke it. On the other hand if an account has been suspended or the password has been changed and a refresh token has been presented the reissuing of the token can be refused by the identity provider.
As the client credentials flow is used for machine to machine authentication frequently re-authenticating is not a problem. The OAuth RFC specifically states "refresh token SHOULD NOT be included." in the response for the client_credentials grant type.

#James Adcock's answer is right on the spot, aside from a minor detail that I will hopefully clarify with my answer since I have seen this inaccuracy a few times already on stack overflow:
The OAuth2 documentation link states explicitly that "A refresh token SHOULD NOT be included" for client_credentials grant type.
First, one needs to clarify the meaning of "SHOULD NOT" in that context. According to rfc2119:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
RFC 2119.
Note that the force of these words is modified by the requirement
level of the document in which they are used.
MUST This word, or the terms "REQUIRED" or "SHALL", mean that the definition is an absolute requirement of the specification.
MUST NOT This phrase, or the phrase "SHALL NOT", mean that the definition is an absolute prohibition of the specification.
SHOULD This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
As you can read, "SHOULD NOT" does not mean that the specification prohibits it but rather that it does not recommend it.
I have to guess the answer will be that refresh tokens dont make sense to be used with client_credential flows?
As you have seen, the specification does not explicitly prohibits it. Notwithstanding, conceptually, (as already point out by James) it does not make sense to have a refresh token when using the client credentials flow since the backend already has the client secret stored locally anyway, and can simply acquire a new access token without the hassle of either having to store user credentials or request the user to enter them again.
A bit of historical reasoning on why Keycloak allows you to use the refresh token. From this thread of the keycloak mailing list:
Hi,
Currently, every time a confidential client tries to get a new access token
from the token endpoint a new session is created on the server. This can
lead to multiple active sessions for a single client/service account when
doing multiple requests to token endpoint.
To avoid that the client should store the access token/refresh token and
use a refresh token when appropriate in case the access token has expired.
That is fine.
one can infer that the reason why Keycloak had (at that time) the refresh token available (by default) for the client credential flows was to be used as a workaround to a technical issue with their client credential flow implementation (i.e., creating too many sessions).

Related

Keycloak client credentials grant type with refresh token

I have a client in keycloak (7.0.0) with given configuration - highlighted option "enables support for 'Client Credentials Grant'":
When I obtain a token for that client (POST /auth/realms/{my-realm}/protocol/openid-connect/token), I receive a following json response:
{
"access_token": "xxx",
"expires_in": 600,
"refresh_expires_in": 1800,
"refresh_token": "xxx",
"token_type": "bearer",
"not-before-policy": 1572415518,
"session_state": "xxx",
"scope": "xxx xxx"
}
I want to integrate it with OAuth2FeignRequestInterceptor, which by default ignores refresh token and creates a new session every access_token's expires_in seconds:
if (accessToken == null || accessToken.isExpired()) {
try {
// no refresh token usage here
accessToken = acquireAccessToken();
}
...
}
My questions are:
Why do I receive a refresh token at all for client_credentials, which is a grant type for backend -> backend communication? The OAuth2 documentation link says explicitly that "A refresh token SHOULD NOT be included" for client_credentials grant type.
Is it possible to configure keycloak that it won't send refresh tokens for clients with client_credentials grant type?
If answer to 2. is 'NO' - do I have to implement my own feign request interceptor with refresh token support?
If the client scope includes offline_access - the refresh_expires_in is 0 and, from what I understood, the refresh token is an offline token. Here is a link to keycloak mailing list where offline token usage is recommended by someone from keycloak's team - isn't it in conflict with OIDC specification for client_credentials grant type?
My answer is maybe a bit too late, but Keycloak recently added support for disabling refresh tokens in the case of client_credentials grants. This was implemented in version 12.0.0, which was released in December 2020.
Here is the part of the documentation pointing to that: https://www.keycloak.org/docs/12.0/release_notes/#other-improvements
Before this version, it was not possible to disable refresh tokens, and so you would have indeed to write your own version of OAuth2FeignRequestInterceptor that would either ignore the refresh token (and try to obtain a new access token), or use the refresh token to obtain a renewed access token. I would choose the first solution, as it closer from the OAUTH2 spec.
My questions are:
Why do I receive a refresh token at all for client_credentials, which is a grant type for backend -> backend communication? The OAuth2
documentation link says explicitly that "A refresh token SHOULD
NOT be included" for client_credentials grant type.
First, one needs to clarify the meaning of "SHOULD NOT" in that context. According to rfc2119:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
RFC 2119.
Note that the force of these words is modified by the requirement
level of the document in which they are used.
MUST This word, or the terms "REQUIRED" or "SHALL", mean that the definition is an absolute requirement of the specification.
MUST NOT This phrase, or the phrase "SHALL NOT", mean that the definition is an absolute prohibition of the specification.
SHOULD This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
As you can read, "SHOULD NOT" does not mean that the specification prohibits it but rather that it does not recommend it.
Is it possible to configure keycloak that it won't send refresh tokens for clients with client_credentials grant type?
As first mentioned in the answer provided by #Clement Petit; Keycloak v12 introduced:
Support for OAuth2 Client Credentials grant without refresh token and without user session.
That option can be found in each client configuration under the section "OpenID Connect Compatibility Modes" as shown below:
If the client scope includes offline_access - the refresh_expires_in is 0 and, from what I understood, the refresh
token is an offline token. Here is a link to keycloak mailing
list where offline token usage is recommended by someone from
keycloak's team - isn't it in conflict with OIDC specification for
client_credentials grant type?
As you have seen, the specification does not explicitly prohibits it. Notwithstanding, conceptually, it does not make sense to have a refresh token when using the client credentials flow since the backend already has the client secret stored locally anyway, and can simply acquire a new access token without the hassle of either having to store user credentials or request the user to enter them again.
From the original poster from the thread of the keycloak mailing list that you have posted:
Hi,
Currently, every time a confidential client tries to get a new access token
from the token endpoint a new session is created on the server. This can
lead to multiple active sessions for a single client/service account when
doing multiple requests to token endpoint.
To avoid that the client should store the access token/refresh token and
use a refresh token when appropriate in case the access token has expired.
That is fine.
one can infer that the reason why Keycloak had (at that time) the refresh token available for the client credential flows was to be used as a workaround to a technical issue with their client credential flow implementation (i.e., creating too many sessions).

Why use openid connect ID token if the access token had all the claims and can be revoked?

I'm using oauth2 authorization code flow with the ASP.NET core 2.2 AddJwtBearer. My token end point returns JWT access toke with all the claims needed for checking the user's permissions.
I can send this token as the bearer for any Web API call and the standard .net code can use those claims to check permissions eg [Authorize(Policy="somePolicy")].
One of the claims points at an internal session key that we can revoke.
So my question is why would I need an ID token or even a refresh token?
The claims and other details are in the access token so what would an ID token add to this?
Having to use a further call to a userinfo end points send to be a waste if the info is in the Auth token?
If I can revoke the session that Auth token points at, surely I don't need a refresh token and can have longer life Auth tokens?
I've read lots of examples and comparisons but most computations between just oauth2 and enhanced with openid connect seem to be with very basic oauth2 not using JWT etc and so written to exaggerate the differences.
So I'm unclear when both are using the same authorization code flow and JWT tokens, what the team advantages are in using the id token in my situation??
Given your context, it seems that OpenId Connect is not necessary for your situation. It really adds value when you are implementing single sign-on (SSO). In that case the Identity token can also be used on SSO logout.
Having additional claims about the identity in the access token is also a waste. Having to send all this information on each call. Especially when you need the information only once (a Spa may persist the information in memory). It's better to have some api (endpoint) expose the information when requested.
About the access token, you can't revoke it. You may be able to revoke authorization, but the access token remains valid until it expires. You want invalid access tokens to short-circuit as soon as possible in the pipeline, before policies are evaluated.
Please note that it's not a common scenario where the api can revoke access by using an internal session key. Most api's are 'session-less' and fully rely on the access token. Because that's the purpose of a JWT, being self-contained, not having to contact the authority to verify the token.
Perhaps you can use a long-lived access token because in your situation the authorization is determined at another level. But are you capable of detecting when the token is compromised? And where are you going to check it? In every api and client? Or would you rather let the authority take care of it (single responsibility)?
When implementing security you should look at the design, the responsibilities, where to do what. Let the authority, that issues the tokens, take care of authentication and client/resource authorization. The Api, being the resource where the business rules (policies) are implemented, can take care of (user) authorization.
The problem with a long-lived token is that when it falls into the wrong hands, it allows access until it expires or, in your case, until you detect something is wrong. Where a short-lived token always allows access for a short time, making it almost not worthwhile for a hacker to obtain a token for the time it can be used.
With short-lived access tokens you'll have to use refresh tokens. The authority can verify on each call whether a new access token should be issued. Of course here counts the same, this only applies to the situation where you are actually verifying the request. Tokens in itself are not safe. You'll have to add some level of security, e.g. check the ip address. But having the authority to take care of it and using one-time-use refresh tokens already does add security.
In my experience with oidc/oauth2, the access token is mainly used to grant client applications access to a resource (on behalf of a user). Where scope claims define the accessible functionality and the sub claim identifies the user.
Authorization can be implemented on different levels and doesn't have to be part of the access token. In fact, permissions should not be part of the access token at all.
So your setup may be fine. But I wouldn't use long-lived access tokens for the reasons already mentioned. Plus they are not managable. You can't update the access token when someting changes in the flow, e.g. when a scope is added.

Should I send the Secret with the Refresh Token in OAuth 2.0

I'm working to implement a OAuth 2.0 server, and while reading the RFC6749 specification I realized that section 6 on Page 47 regarding "Refreshing an Access Token". Explains that we need to just use the Refresh Token that we have to get a new Token.
But for example, in addition to the Refresh Token, Google require the User ID and the Secret to do so.
This confuses me, because on one hand we have Google that is processing high volume of requests every day, and we have a specification written probably with a smaller scope in mind.
Is it good to send the Secret every hour with the Refresh Token?
Personally I believe no: because the User ID and Secret should be used only to go over the whole OAuth 2.0 process.
Basically
You use the token on each request to prove that you are who you are.
Refresh Token get used only once an hour (and potentially changed at each refresh)
Secret and User ID go to the internet as rarely as possible. Only when option 1 and 2 get compromised.
I personally believe that sending the Secret with the Refresh token is less secure. But maybe I'm missing something.
If you have another point of view, please share it :)
I might be missing something, but what Google requires and what's also specified by OAuth2 is that when refreshing a token from a confidential client application the client must authenticate itself.
The most common type of credentials being used for confidential clients are a client identifier alongside a client secret. This information is issued to a client application and is unrelated to the end-user.
By requiring client authentication the authorization server can be sure the request comes from a specific client and adjust its response accordingly. For example, an authorization server can decide that certain permissions - scopes - can only be requested from confidential clients.
The argument around reducing the number of times the client secret needs to be sent over the wire is a non-issue. OAuth2 mandates that the communication happens over TLS and if you have issues with sending the secret then you would also have issues with sending bearer access tokens.
In conclusion, although sometimes doing things exactly according to spec without questioning the overall context might lead to vulnerabilities:
... some libraries treated tokens signed with the none algorithm as a valid token with a verified signature. The result? Anyone can create their own "signed" tokens with whatever payload they want, allowing arbitrary account access on some systems.
(source: Critical vulnerabilities in JSON Web Token libraries)
Some libraries treated the none algorithm according to spec, but ignored the usage context; if the developer passed a key to verify a signature it most likely did not want to treat unsigned tokens as valid.
However, passing the secret on the refresh token request is not one of these situations so don't worry about it.

How can I revoke a JWT token?

I am using Spring Security OAuth2 and JWT tokens. My question is: How can I revoke a JWT token?
As mentioned here
http://projects.spring.io/spring-security-oauth/docs/oauth2.html, revocation is done by refresh token. But it does not seem to work.
In general the easiest answer would be to say that you cannot revoke a JWT token, but that's simply not true. The honest answer is that the cost of supporting JWT revocation is sufficiently big for not being worth most of the times or plainly reconsider an alternative to JWT.
Having said that, in some scenarios you might need both JWT and immediate token revocation so lets go through what it would take, but first we'll cover some concepts.
JWT (Learn JSON Web Tokens) just specifies a token format, this revocation problem would also apply to any format used in what's usually known as a self-contained or by-value token. I like the latter terminology, because it makes a good contrast with by-reference tokens.
by-value token - associated information, including token lifetime, is contained in the token itself and the information can be verified as originating from a trusted source (digital signatures to the rescue)
by-reference token - associated information is kept on server-side storage that is then obtained using the token value as the key; being server-side storage the associated information is implicitly trusted
Before the JWT Big Bang we already dealt with tokens in our authentication systems; it was common for an application to create a session identifier upon user login that would then be used so that the user did not had to repeat the login process each time. These session identifiers were used as key indexes for server-side storage and if this sounds similar to something you recently read, you're right, this indeed classifies as a by-reference token.
Using the same analogy, understanding revocation for by-reference tokens is trivial; we just delete the server-side storage mapped to that key and the next time the key is provided it will be invalid.
For by-value tokens we just need to implement the opposite. When you request the revocation of the token you store something that allows you to uniquely identify that token so that next time you receive it you can additionally check if it was revoked. If you're already thinking that something like this will not scale, have in mind that you only need to store the data until the time the token would expire and in most cases you could probably just store an hash of the token so it would always be something of a known size.
As a last note and to center this on OAuth 2.0, the revocation of by-value access tokens is currently not standardized. Nonetheless, the OAuth 2.0 Token revocation specifically states that it can still be achieved as long as both the authorization server and resource server agree to a custom way of handling this:
In the former case (self-contained tokens), some (currently non-standardized) backend interaction between the authorization server and the resource server may be used when immediate access token revocation is desired.
If you control both the authorization server and resource server this is very easy to achieve. On the other hand if you delegate the authorization server role to a cloud provider like Auth0 or a third-party component like Spring OAuth 2.0 you most likely need to approach things differently as you'll probably only get what's already standardized.
An interesting reference
This article explain a another way to do that: Blacklist JWT
It contains some interesting pratices and pattern followed by RFC7523
The JWT cann't be revoked.
But here is the a alternative solution called as JWT old for new exchange schema.
Because we can’t invalidate the issued token before expire time, we always use short-time token, such as 30 minute.
When the token expired, we use the old token exchange a new token. The critical point is one old token can exchange one new token only.
In center auth server, we maintain a table like this:
table auth_tokens(
user_id,
jwt_hash,
expire
)
user_id contained in JWT string.
jwt_hash is a hash value of whole JWT string,Such as SHA256.
expire field is optional.
The following is work flow:
User request the login API with username and password, the auth server issue one token, and register the token ( add one row in the table. )
When the token expired, user request the exchange API with the old token. Firstly the auth server validate the old token as normal except expire checking, then create the token hash value, then lookup above table by user id:
If found record and user_id and jwt_hash is match, then issue new token and update the table.
If found record, but user_id and jwt_hash is not match , it means someone has use the token exchanged new token before. The token be hacked, delete records by user_id and response with alert information.
if not found record, user need login again or only input password.
when use changed the password or login out, delete record by user id.
To use token continuously ,both legal user and hacker need exchange new token continuously, but only one can succeed, when one fails, both need to login again at next exchange time.
So if hacker got the token, it can be used for a short time, but can't exchange for a new one if a legal user exchanged new one next time, because the token validity period is short. It is more secure this way.
If there is no hacker, normal user also need exchange new token periodically ,such as every 30 minutes, this is just like login automatically. The extra load is not high and we can adjust expire time for our application.
source: http://www.jianshu.com/p/b11accc40ba7
This doesn't exactly answer you question in regards to the Spring framework, but here's an article that talks about why if you need the ability to revoke JWT's, you might not want to go with JWT's in the first place, and instead use regular, opaque Bearer tokens.
https://www.dinochiesa.net/?p=1388
One way to revoke a JWT is by leveraging a distributed event system that notifies services when refresh tokens have been revoked. The identity provider broadcasts an event when a refresh token is revoked and other backends/services listen for the event. When an event is received the backends/services update a local cache that maintains a set of users whose refresh tokens have been revoked.
This cache is then checked whenever a JWT is verified to determine if the JWT should be revoked or not. This is all based on the duration of JWTs and expiration instant of individual JWTs.
This article, Revoking JWTs, illustrates this concept and has a sample app on Github.
For Googlers:
If you implement pure stateless authentication there is no way to revoke the token as the token itself is the sole source of truth
If you save a list of revoked token IDs on the server and check every request against the list, then it is essentially a variant of stateful authentication
OAuth2 providers like Cognito provides a way to "sign out" a user, however, it only really revokes refresh token, which is usually long-lived and could be used multiple times to generate new access tokens thus has to be revoked; the existing access tokens are still valid until they expire
What about storing the JWT token and referencing it to the user in the database? By extending the Guards/Security Systems in your backend application with an additional DB join after performing the JWT comparison, you would be able to practically 'revoke' it by removing or soft-deleting it from the DB.
In general, the answer about tokens by reference vs. tokens by value has nailed it. For those that stumble upon this space in future.
How to implement revocation on RS side:
TL;DR:
Take a cache or db that is visible to all your backend service instances that are verifying tokens. When a new token arrives for revocation, if it's a valid one, (i.e. verifies against your jwt verification algo), take the exp and jti claims, and save jti to cache until exp is reached. Then expire jti in cache once unixNow becomes > exp.
Then on authorization on other endpoints, you check everytime if a given jti is matching something in this cache, and if yes, you error with 403 saying token revoked. Once it expires, regular Token Expired error kicks in from your verification algo.
P.S. By saving only jti in cache, you make this data useless to anyone since it's just a unique token identifier.
The best solution for JWT revocation, is short exp window, refresh and keeping issued JWT tokens in a shared nearline cache. With Redis for example, this is particularly easy as you can set the cache key as the token itself (or a hash of the token), and specify expiry so that the tokens get automatically evicted.
I found one way of resolving the issue, How to expire already generated existing JWT token using Java?
In this case, we need to use any DB or in-memory where,
Step 1: As soon as the token is generated for the first time for a user, store it in a db with the token and it's "issuedAt()" time.
I stored it in DB in this JSON format,
Ex: {"username" : "username",
"token" : "token",
"issuedAt" : "issuedAt" }
Step 2: Once you get a web service request for the same user with a token to validate, fetch "issuedAt()" timestamp from the token and compare it with stored(DB/in-memory) issued timestamp.
Step 3: If stored issued timestamp is new (using after()/before() method) then return that the token is invalid (in this case we are not actually expiring the token but we are stop giving access on that token).
This is how I resolved the issue.

What is intent of ID Token expiry time in OpenID Connect?

In OpenID Connect an access token has an expiry time. For authorization code flow, this is typically short (eg 20 minutes) after which you use the refresh token to request a new access token.
The ID token also has an expiry time. My question is what is the intent of this?
Any ID token expiry time less than the expiry time of the refresh token will mean you will eventually have an expired ID token, but a valid access token.
So are you meant to:
give your ID token an expiry longer than the refresh token expiry, or
set it to the same expiry as the access token and take some action (what?) when it expires, or
just consume the ID token in your client on receipt, then ignore the expiry time after that?
The OpenID Connect specification just says that when validating an ID token,
"The current time MUST be before the time represented by the exp Claim."
which (possibly) supports the third option above.
EDIT
As OpenID Connect builds on OAuth2 the answer to the supplementary question below can be found in the OAuth2 specification which says,
expires_in
RECOMMENDED. The lifetime in seconds of the access token.
A related question is when you exchange an authorization code for the tokens, the same specification says you might get a response such as:
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbG[...]"
}
But what does "expires_in" relate to in this case? The access token, the refresh token or the ID token?
(For information, IdentityServer3 sets this to the access token expiry time).
I'm answering my own question as have discovered that some of the assumptions behind my question were wrong, so easier to clarify here, rather than re-write the question.
An ID token is meant for proving to a Client that the user has authenticated, and who they are as a result.
When a Client receives an ID token, it will generally do something like convert it to a ClaimsIdentity, and persist this, eg using a cookie.
The ID token has to be un-expired at this point of use (which it should be, since it has just been issued). But after this it is not used again, so it does not matter if it expires while the user still has an active session. The Client has the authentication information it needs, and in turn can choose its own policy for how long the session lasts before the user has to log in again.
My wrong assumption when asking the question was that an ID token and access token should be used together, and therefore both needed to have valid expiry dates. This is wrong for various reasons:
ID tokens are only for authenticating to a Client (as described above).
Access tokens have nothing to do with Clients. They are for access to resources and a Client only handles them if it in turn needs to call an resource.
Something like a standalone MVC or WebForms application only needs an ID token. If it isn't calling an external resource, there is nothing to grant access to, so no access token.
I had to dig into this for my own reasons and wrote it up, so I'll post what I learned here...
First, I'll answer the question at the risk of stating the obvious: The ID token cannot be trusted and its content must be ignored if the current time is greater than the expired time. The questioner's answer states that the after the initial authentication of the user, the ID Token isn't used again. However, since the ID Token is signed by the identity provider, it certainly could be useful at any time to give a way of reliably determining who the user is to other services that an app might be using. Using a simple user ID or email address isn't reliable because it can be easily spoofed (anyone can send an email address or user ID), but since an OIDC ID Token is signed by the Authorization server (which also usually has the benefit of being a third party) it cannot be spoofed and is a much more reliable authentication mechanism.
For example, a mobile app may want to be able to tell a backend service who the user is that is using the app and it may need to do so after the brief period following the initial authentication, at which time the ID Token is expired, and thus, cannot be used to reliably authenticate the user.
Therefore, just like the access token (used for authorization - specifying what permissions the user has) can be refreshed, can you refresh the ID Token (used for authentication - specifying who the user is)? According to the OIDC specification, the answer isn't obvious. In OIDC/OAuth there are three "flows" for getting tokens, The Authorization Code flow, the Implicit flow, and the Hybrid flow (which I'll skip below because it's a variant of the other two).
For the implicit flow in OIDC/OAuth you request the ID Token at the authorization endpoint by redirecting the user in the browser to the Authorization endpoint and including id_token as the value of the response_type request parameter. An Implicit Flow Successful Authentication Response is REQUIRED to include the id_token.
For the Authentication Code flow, the client specifies code as the value of the response_type request parameter when redirecting the user to the authorization endpoint. A successful response includes an authorization code. The client client makes a request to the token endpoint with the authorization code and, according to OIDC Core Section 3.1.3.3 Successful Token Response the response MUST include an ID Token.
So for either flow, that's how you initially get the ID Token, but how do you refresh it? OIDC Section 12: Using Refresh Tokens has the following statement about the Refresh Token Response:
Upon successful validation of the Refresh Token, the response body is the Token Response of Section 3.1.3.3 except that it might not contain an id_token.
It might not contain an ID Token and since there is no way specified to force it to include the ID token, you must assume that the response will not contain the ID Token. So technically there is no specified way to "refresh" an ID Token using a refresh token. Therefore, the only way to get a new ID Token is to re-authorize/authenticate the user by redirecting the user to the authorization endpoint and starting the implicit flow or authentication code flow as described above. The OIDC specification does add a prompt request parameter to the authorization request so the client can request that the authorization server not prompt the user with any UI, but the the redirect still has to happen.
If I understand correctly, according to this and the OpenID Connect Core 1.0 spec, the ID token itself can be stored in cookies as a mechanism to persist sessions, and sent with every authentication-requiring request to the Client. The Client can then verify the ID token either locally or through the Provider's verifier endpoint (if provided, like Google does). If the token is expired, it should make another auth request, except this time with prompt=none in the URL parameter. Also make sure to send the expired ID token in the id_token_hint parameter, otherwise the Provider may return an error.
So, it does seem natural for the ID Token to expire, but prompt=none ensures the new ID token can be obtained smoothly with no user intervention (unless of course the user is logged out of that OpenID).
It is the same intent: you can't use the id_token after it is expired. The main difference is that an id_token is a data structure and you won't need to call any servers or endpoints, as the information is encoded in the token itself. A regular access_token is usually an opaque artifact (like a GUID).
The consumer of the id_token must always verify the (time) validity of it.
I'm not 100% familiar with IS, but I would guess it is a convenience field. You should always check the exp claim.
Expiration is just one of the validations. id_tokens are also digitally signed and that is also a validation you must perform.
Refreshing a token means that you can use it again for requesting something from the authorization server (in this case the OP - the OpenID-Connect Provider) EVEN WHEN THE USER IS NOT LOGGED IN. You typically allow this for limited resources only, and only after the user has logged in and been authenticated at least once. The refresh tokens themselves should be also limited in time.
In OIDC implicit flow you call the Authorization endpoint,
and receive the ID token in the response along with all the scopes and in them all the claims info.
Subsequent calls to an API are meant to be done with code flow.
Implicit flow is meant to enable a javascript only or browser only app. Not an app that is interacting with a server.
So even if there was a way to "refresh" this token, you should not - security wise - allow it to live too long. It will be stolen and reused by unauthorized users impersonating the id. You should force a new login for that.
In code flow you call the OP's Authorization endpoint, and receive an Authorization code (also called an authorization token, or authcode for short). This should expire similar to the id_token that you received in implicit flow, for the same reasons and cannot and should not be renewed.
Your UI or app then call the OP's Token endpoint, and receives (sometimes after the user's further consent through a UI to allow use of their owned resources on the OP's server) both:
An id_token, for authentication - which should never be used again in server calls, except as a hint during logout, when its expiration is not important anymore, and so, for the reasons above should be let to expire, and never be refreshed.
An access_token - which later on, when calling an API, can be given to the OP's UserInfo endpoint. That will return the claims, and the API can authorize accordingly.
You can refresh this access_token, since it only tells the API what claims the user has, and what resources (by scopes and each scope's claims) the user agreed to give you. As explained above this is for allowing access even after the user is not logged in anymore. Of course you never wish to allow the id_token to be refreshed, because you don't want to allow impersonation without logging in.
I wanted to post this answer as a comment but since I haven't been very active on StackOverflow, I guess I'm posting it as an alternate answer.
You also use id_token as the id_token_hint when attempting to log the user out of a session http://openid.net/specs/openid-connect-session-1_0.html. I honestly don't think that it really matters if the id_token is expired at this point since you're only concerned about logging out a particular user.
TLDR;
Validate the ID token before trusting what it says.
More Details
What is intent of ID token expiry time in OpenID Connect?
The intent is to allow the client to validate the ID token, and the client must validate the ID token before operations that use the ID token's information.
From the OpenID Implicit Flow spec:
If any of the validation procedures defined in this document fail, any operations requiring the information that failed to correctly validate MUST be aborted and the information that failed to validate MUST NOT be used.
To corroborate that, Google's OpenID Connect documentation says this about ID token validation:
One thing that makes ID tokens useful is that fact that you can pass them around different components of your app. These components can use an ID token as a lightweight authentication mechanism authenticating the app and the user. But before you can use the information in the ID token or rely on it as an assertion that the user has authenticated, you must validate it.
So, if our client application is going to take some action based on the content of the ID token, then we must again validate the ID token.
Just share my journey. It's June, 2021. I'm writing this because I've stumbled into 3rd-party authentication business. I'm a veteran programmer but novice to security. In other words, all standards, spec, and terminologies are strangers, anybody can beat me in this field. Forgive me for not going by all the terms.
Cut to chase, I'm writing an Angular/Node app, so UI=Angular, API (API server)=Node/Express. Instead of creating my own Username/Password authentication, I'm turning to 3rd-party authentication, let them validate the genuineness of what the users claim they are. Here are two important guidebooks for me:
Angular Authentication With JSON Web Tokens (JWT): The Complete Guide
Eiji's Authenticate with a backend server
Combining No. 1 and angularx-social-login, I have the UI hooked up with Google, then attach idToken to API, vola! Following No. 2 using local library API can validate idToken, great!
Wait, idToken has exp expires in 1-hour. How do I refresh it?
My understanding is all I need is Google's authentication, I don't care what standard and version they use, but like others I just trust their authentication. Authentication is basically verify who they claim they are. Authorization is access control for what/where users can do/goto. The internal access control (allow users to do what) is not exposed to Google they have no idea of it. So accessToken should be out of the picture. Right?
I've spent days studying on how to refresh idToken now concluded Google does not recommend it nor angularx-social-login offers a way. In No. 2, Eiji has stated clearly:
Hence, solution for my situation is
use Google's authentication.
creates an own session management/timeout-rules at API after initial validation of idToken to mitigate exp.
preferably add this session data into cookie with res.cookie("SESSIONID", myOwnID, {httpOnly:true, secure:true});
For better protection, Eiji also recommends Cross Account Protection. Hope this will help someone!

Resources