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.
I am building a (set of) web application; the backend has REST-like API, the frontend will be some REST JS app, android apps etc; and I'm trying to come up with an SSO functionality.
Looking at Oauth2/OIDC it seems the best way would be to use Implicit flow; however, the access tokens in implicit flow (in oidc) have a set expiration. The refresh token is not part of implicit flow.
How do I ensure that the user will stay logged in? I.e. when the access token expires, the frontend application will try to obtain a new one from an auth server; that is supposed to ask for username/password. Alternatively, it can build a session with the frontend (using cookies), but how is that different from a refresh token?
It seems to me that getting the access token e.g. from the android app means at least opening the web browser; depending on the expiry length, that could be quite often. Is that the correct flow or am I missing something?
You are right, the issuance of a refresh token is not allowed with the Implicit grant type.
However, the refresh token and the access token are not needed to know if the user is logged in or not (the access token only allows you to access on protected resources). You have to use the ID Token which is issued in the authorization response.
You can verify if the user is still logged in by sending an authorization request with the query parameter prompt=none (see section 3.1.2.1. Authentication Request). I recommend you to send the current ID Token using the id_token_hint query parameter as mentioned in the same section:
ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client. If the End-User identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it SHOULD return an error, such as login_required. When possible, an id_token_hint SHOULD be present when prompt=none is used
If you receive an error (login_required or interaction_required) then the user may be logged out.
Another way could be to use the Session Management feature. But as this specification is not yet approved (draft 27), it may be subject to changes and may not be available. However it is a very simple way to know the status of the user.
I am facing a custom implementation of OpenId Connect. But (there is always a but) I have some doubts:
I understand the process of obtainning an acces_token an a id_token, except the step when the OP provides an authorization_code to the client. If it is done by a redirect (using the redirect uri)
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
The end-user is able to see that authorization code? It does not expire? Imagine we catch it and we use later (some days later) Is it a security hole? Should the state be expired in the Token Endpoint?
The flow continues and we got at the client the Access_token and the id_token in the client.
How the Access_token should be used on the OP side ? It should be stored in a database? Or be self containing of the information required to validate it ?What would you recommend?
And in the client-side , both tokens should be sent in every request?
And the last doubt, if we have an Access_token the existence of an id_token is for representing authorization and authentication in separated tokens?
Extra doubts:
I know the process to obtain an access token but I have doubts of how the OP ,once generated and sent, it validates the access_token that comes with every request
How the OP knows an access token is valid? As far as I know, the OP should say that an access_token is valid/invalid. There should be some way to check it right? How it gets to know that a token represents a valid authenticated user if it is not stored in DB?
Is it a bad idea to store access_token in a cookie? Because sometimes we call to some webservices and we want to send access_token as parameter. Or there is another workaroundsolution?
How the access token should be stored in the Client , for example, in ASP.NET, in the session?
Thanks very much to all of you, I will give upvote and mark as answer as soon as you give me the explanations.
Thanks!
The end-user is able to see that authorization code?
Yes. Although, even if the authorization code can be seen, the token request requires that the client's secret be sent as well (which the browser does not see)
it does not expires? Imagine we catch it and we use later (some days later) It is a security hole? Should the state be expired in the Token Endpoint?
The spec says that the authorization code should expire. See https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.
How the Access_token should be used on the OP side ? It should be stored in a database? Or be self containing of the information required to validate it ?What would you recommend?
The access token should be stored on the OP if you want to be able to revoke the tokens. If you don't, the token will be in JWT format (self-contained)...but you should store it if you want to be able to revoke it whether it's a JWT or not.
And in the client-side , both tokens should be sent in every request?
No, just the access token.
And the last doubt, if we have an Access_token the existance of an id_token is for representing authorization and authentication in separeted tokens?
Yes, they are separate tokens for different purposes. Access token is for authorization and Id token is self contained and used to communicate to the client that the user is authenticated.
How the OP knows an access token is valid? As far as i know, the OP should say that an access_token is valid/invalid. There should be some way to check it right? How it gets to know that a token represents a valid authenticated user if it is not stored in DB?
see How to validate an OAuth 2.0 access token for a resource server? about thoughts on how the resource server should validate the access token before letting the request from the client go through.
It´s a bad idea to store access_token in a cookie? because sometimes we call to some webservices and we want to send access_token as parameter. Or there is another workaroundsolution?
I'm assuming you're using the authorization code grant flow (...from your questions). If that's the case, the reason why an authorization code is, first of all, passed back from the OP rather than the access token is so that the access token can stay hidden on the server side--away from the browser itself. With the authorization code grant flow, the access token should stay out of the browser. If you're wanting to send api requests to the resource server directly from the browser, then look into the oauth2 implicit flow (https://www.rfc-editor.org/rfc/rfc6749#section-4.2).
How the access token should be stored in the Client , for example, in ASP.NET, in the session?
In the OpenID Connect flavour of OAuth2, the access token is for offline_access (i.e. outside of an authenticated "session"). The access token could be used during the session of the user but it might be better to store the refresh token in the database so that your client app can request new access tokens whenever it needs to and as long as the refresh token is valid...even when the user's authentication is expired. The access token should be short-lived so storing it in the database is an option but not necessary.
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!
there's two ways to fetch access token.
use authorization code to exchange it
use refresh token to refresh it
think about it!!
though the word of exchange and refresh is different,what they do are the same.
both action need to parse client id & client secret(Or signature) and token
we can just save the authorization code in our system,and again use auth code to
refresh access token just like refresh token do.
Except that authorization code is expired too soon.
so I wonder
why the designers of oauth2 designed these two concepts while not used just one single concept or say just design the authorization code and give it a long expired-time.
I am afraid that you have not understood the concepts of oauth2 too well. There aren't just two ways of getting the access token, there are more. Each is basically called a 'grant type'. I'm describing the use cases of the ones which I have deployed below :
1- Authorization code :
This is similar to the flow of "Login with Facebook" etc buttons which you see on different websites, which allow you to register/login using your facebook etc accounts. Here, on clicking this button, control is directed to Facebook, where the user enters his login credentials. If successful, an authorization code is sent to whatever redirecturl you entered while registering as a developer with Facebook. You then use this authorization code to request the access token service to get the access token which you then use whenever accessing any Facebook webservices to get the user's details.
2- Client credentials :
If you are running your own webservices and you want to allow access only to valid clients, then this is the grant type you would use. For example, you are running your webservices and now you want to consume it in your own native mobile app which you distribute through any app store. This will ensure that only those who installed your app will be able to access your webservice.
3- User credentials :
Same as above, only in this case this would allow you to authenticate a registered user as well and then give access to user restricted services like my account etc.
4- Refresh token :
By design, the access token service gives an access token as well as a refresh token. You would use the refresh token obtained from it here to refresh an expired access token. Essentially, this does not generate a new access token, it only "refreshes" an existing token. It will give you a new access token and refresh token and extend the expiry time. When this access token expires, you again call refresh token using the refresh token obtained last time, and keep repeating the process every time the token expires.
According to RFC 6749 10.5 The authorization codes are short lived and single-use. Therefore, you cannot use them again and again to get new authorization tokens.
Authorization codes MUST be short lived and single-use. If the
authorization server observes multiple attempts to exchange an
authorization code for an access token, the authorization server
SHOULD attempt to revoke all access tokens already granted based on
the compromised authorization code.
There are some additional misconceptions that seem to be presented here, so I wanted to help clear them up.
The differences between an access token and a refresh token can be summarised as follows:
An access token is used to provide access to restricted resources to an authorized client after authentication has taken place.
A refresh token, on the other hand, is used by a client in order to retrieve new access tokens with identical or narrower scopes.
The different between the Authorization Code Grant and the Implicit Grant (as well as their usages) help to illustrate how both should be used.
In general, the Authorization Code Grant should be preferred over the Implicit Grant unless a resource is being accessed directly via a publicly implemented client (e.g., browser-run code) or there is a specific reason that the Authorization Code Grant cannot be used (e.g., feasibility or performance). This is explained in the RFC definition for the Implicit flow.
During an Implicit Grant, access tokens are exposed to the user-agent which could lead to them being compromised since they are no longer under the control of a server app (confidential client) that could otherwise be requesting the protected resources. This is why refresh tokens are not issued during Implicit Grants. Though access tokens might be exposed, they are short-lived. Resource tokens, on the other hand, are long-lived and can be used to retrieve new access tokens.
The Authorization Code Grant, on the other hand, prevents the potential for refresh tokens to be exposed. During this grant, the authorisation server issues a code instead of tokens. The code is then passed by the user-agent to the client application which exchanges the code with the authorization server to retrieve access and refresh tokens. Since the code exchange is performed directly between the client application and a trusted authorization server, a refresh token can be securely issued.
The RFC spec cautions that the security implications of implementing the Authorization Code Grant in a public client versus a confidential (e.g., server-side) client should be seriously considered. "More OAuth 2.0 Surprises: The Refresh Token" clears up a few misconceptions and furthers the idea that auth codes should not be sent directly by the user-agent to the auth server in order to retrieve refresh tokens, though the OAuth 2.0 spec does not technically dictate this.
Answer from #ComfortableDust has the answer to original question. Just quoting the exact text from his reply
The Authorization Code Grant, on the other hand, prevents the potential for refresh tokens to be exposed. During this grant, the authorisation server issues a code instead of tokens. The code is then passed by the user-agent to the client application which exchanges the code with the authorization server to retrieve access and refresh tokens. Since the code exchange is performed directly between the client application and a trusted authorization server, a refresh token can be securely issued.