I've spend the last few days reading through all the specs with regards to OAuth2 and OpenIDConnect and implementing a test client using Thinktecture Identity Server. I've also followed several pluralsight courses and I think understand the main gist of it. However i'm still extremely confused about the Response Types.
OpenIDConnect spec specifies that the Hybrid flow response types are a combination of "code", "id_token" and "token". I understand the "id_token" allows us to get access to basic id information initially.
I also understand code" refers to the authorization code and "token" refers to an access token and combining "code" with one or both of the other two triggers the flow but my understanding was that you swap an authorization code for an access token in the Authorization flow, while the Implicit flow supplies the Access Code implicitly?
Could someone clear up my confusion?
The following statements that you made are correct:
code refers to the Authorization Code
token refers to an Access Token or (access_token)
in the Authorization Code flow one switches the code for an access_token
But part of your confusion may originate from terminology mixup:
the term Authorization flow is not entirely correct; its official name is Authorization Code flow
the term Access Code does not exist
the Implicit flow does not have an Authorization Code (nor Access code) in fact there's no credential (or grant) involved at all that allows the Client to get tokens from the Token endpoint, hence it's name
As #juanifioren pointed out, Hybrid flows combine things:
the code id_token flow would get a code and id_token in the Authentication Response directly but you'd use the code to get an access_token from the Token endpoint
the code token flow would get a code and access_token in the Authentication Response directly but you'd use the code to get an id_token and possibly another access_token in the backend from the Token endpoint
the code id_token token flow would get a code, access_token and an id_token in the Authentication Response directly and you could use the code in the backend to get another access_token from the Token endpoint
Getting an access_token from the Token endpoint differs from getting it from the Authorization endpoint because the confidential clients authenticate themselves to the Token endpoint (and not to the Authorization endpoint). Hence the access_token for the confidential part of the client might have more permissions and or a longer life.
See also a short thread on the spec mailing list on this topic: http://lists.openid.net/pipermail/openid-specs-ab/Week-of-Mon-20150209/005229.html
To understand the possible relationships between Response Types and Grant Types see IdentityServer4\Constants.cs
public static readonly Dictionary<string, string> ResponseTypeToGrantTypeMapping = new Dictionary<string, string>
{
{ OidcConstants.ResponseTypes.Code, GrantType.AuthorizationCode },
{ OidcConstants.ResponseTypes.Token, GrantType.Implicit },
{ OidcConstants.ResponseTypes.IdToken, GrantType.Implicit },
{ OidcConstants.ResponseTypes.IdTokenToken, GrantType.Implicit },
{ OidcConstants.ResponseTypes.CodeIdToken, GrantType.Hybrid },
{ OidcConstants.ResponseTypes.CodeToken, GrantType.Hybrid },
{ OidcConstants.ResponseTypes.CodeIdTokenToken, GrantType.Hybrid }
};
Your thoughts about Authorization Code Flow and Implicit Flow are right.
But I think you are over-complicating the hybrid flow. When using hybrid you just simply can get both code and id_token.
After that, either you can grab code and exchange it for access token or just use the id_token (or access token) directly. Both approaches have their own flaws, especially in terms of security.
https://medium.com/#darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660#9401
6. response_type=code token
When the value of response_type is code token, an authorization code
and an access token are issued from the authorization endpoint, and an
access token is issued from the token endpoint. In addition, if openid
is included in the scope request parameter, an ID token is issued from
the token endpoint, too.
Both the authorization endpoint and the token endpoint issue an access
token, but the contents of the access tokens are not always the same.
Regarding this, “3.3.3.8. Access Token” in OpenID Connect Core 1.0
says as follows:
If an Access Token is returned from both the Authorization Endpoint
and from the Token Endpoint, which is the case for the response_type
values code token and code id_token token, their values MAY be the
same or they MAY be different. Note that different Access Tokens might
be returned be due to the different security characteristics of the
two endpoints and the lifetimes and the access to resources granted by
them might also be different.
Related
I'm pretty new to FastAPI and OAuth2 in general. I just worked through the tutorial "Simple OAuth2 with Password and Bearer" and it mostly made sense, but there was one step that felt like magic to me..
How does the access token get stored onto the client and subsequently get passed into the client's requests?
My understanding of the flow is that it's basically
User authenticates with their username and password (these get POST'ed to the /token endpoint).
User's credentials are validated, and the /token endpoint returns the access token (johndoe) inside some JSON. (This is how the user receives his access token)
???
User make a subsequent request to a private endpoint, like GET /users/me. The user's request includes the header Authorization: Bearer johndoe. (I don't think the docs mention this, but it's what I've gathered from inspecting the request in Chrome Developer Tools)
The authorization token is then used to lookup the user who made the request in (4)
Step (3) is the part that I don't understand. How does the access token seemingly get stored on the client, and then passed as a header into the next request?
Demo
When you run the code in the tutorial, you get the following swagger docs. (Note the Authorize button.)
I click Authorize and enter my credentials. (username: johndoe, password: secret)
And now I can access the /users/me endpoint.
Notice how the header Authorization: Bearer johndoe was automagically included in my request.
Last notes:
I've checked my cookies, session storage, and local storage and all are empty
The authorization header disappears if I refresh the page or open a new tab
I suspect Swagger is doing something under the hood here, but I can't put my finger on it.
If you need persistence for the token you'd usually use localStorage or similar, but in SwaggerUIs specific case, the authentication information is kept internally in the library.
If you have enabled persistence SwaggerUI will persistent the access token to localStorage:
export const persistAuthorizationIfNeeded = () => ( { authSelectors, getConfigs } ) => {
const configs = getConfigs()
if (configs.persistAuthorization)
{
const authorized = authSelectors.authorized()
localStorage.setItem("authorized", JSON.stringify(authorized.toJS()))
}
}
I'm using directus to grant users access to ressources required by an SPA written in Angular. To authenticate users I created an auth service and interceptor to handle sessions and attach the "Authorization" header. Those services work fine and login as intended. But here comes the problem:
Directus session times are configured with default values (15 min validity for access_token, 7d for refresh_token) but as soon as the access_token expires I cannot retrieve a new one using the refresh token. This bugs me, because the goal is to keep users logged in for the next 7d (refresh_token lifespan) or until logout if they check this option.
My attempts at achieving this:
Since i'm using graphQL, i tried the "auth_refresh" mutation from the authentication documentation. While the access token is still valid, refreshing works fine. After the access token expired there is no way to retrieve a new one via a valid refresh token.
Alternatively I tried to achieve a refresh via the POST request specified by the docs (to double check if it was some sort of config error with graphql) but I encounter exactly the same problems as with graphQL. Directus returns either "401 unauthorized : Token expired."
if i extend the lifespan of the access token for longer than the server defined lifetime,
Response: Sending a token with prolonged life
or "401 unauthorized : Invalid user credentials." if I request a new token without an
"Authorization" header.
Response: Sending no access token
The refresh token is correctly loaded and sent to the server as specified in the docs in both
cases.
Now my questions are:
Am I missing something? I haven't found any further specification in the docs and the Auth0 protocol specifies that a new access token should be retrievable with a valid refresh token.
If this feature is not intended: How could I achieve a "keep me signed in" option with directus? I would like to keep user rights management in one place and do not really want to handle user auth redundantly for my current use case.
2b. Why is the lifespan of the refresh token so much longer than the lifespan of the access token if this isn't intended?
One of my thoughts is, that it has to do with access rights of the "public" role on the "directus_sessions" table. But I can't think of a way to grant only read rights for owned/received tokens, since there are no payload variables available inside the filters. Could this be the cause? Would there be a way to achieve this?
Thx&Greetz
The OpenID Connect JWT token contains both an id_token and an access_token (Like "access_token": "SlAV32hkKG"). What is the access_token for?
ANSWER EXAMPLE
Just adding an example to complement the answers taken from this linked article
User Info Endpoint Request
GET /userinfo HTTP/1.1
Host: openid.c2id.com
Authorization: Bearer SlAV32hkKG
User Info Endpoint Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub" : "alice",
"email" : "alice#wonderland.net",
"email_verified" : true,
"name" : "Alice Adams",
"picture" : "https://c2id.com/users/alice.jpg"
}
The answer to your question lies in the section 5.3 and 16.4 of the specification. The access token is used to access the userinfo endpoint which is a protected resource.
16.4. Access Token Disclosure
Access Tokens are credentials used to access Protected Resources, as defined in Section 1.4 of OAuth 2.0 [RFC6749]. Access Tokens represent an End-User's authorization and MUST NOT be exposed to unauthorized parties.
[...]
5.3. UserInfo Endpoint
The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns Claims about the authenticated End-User. To obtain the requested Claims about the End-User, the Client makes a request to the UserInfo Endpoint using an Access Token obtained through OpenID Connect Authentication. These Claims are normally represented by a JSON object that contains a collection of name and value pairs for the Claims.
tl;dr - Access token grants authorization to access a protected resource where the ID token is consumed by the client for authentication.
Access token
Being an extention to OAuth2.0, OpenID Connect keep tokens/prameters defined in OAuth2.0 specification. Access token is one such thing. As defined by the protocol, access token is used to access protected resources. It replaces user credentials, manually generated tokens or security keys which were used in good old days (ex:- Think about basic authentication).
Note the access token could be an opaque string. Which means it's nothing that could be consumed by the client which recieves it. But the authorization server knows what it is. For example, introspection endpoint (RFC7662) can be used to validate the validity of an access token. And access token can be a JWT too depending on the usage and implementation.
ID token
This is what OpenID Connect introduced to OAuth2.0. ID token is a JWT and it is consumed by client to authenticate the end user(the resource owner). By validating an ID token, client have the ability to authenticate. Due to this fact, one can argue it surves one time usage.
Also note that in some implementations, ID tokens are being used as bearer tokens. That mean, ID token used like an access token for authentication and authorization.
Context:
I've implemented OpenIddict in my application, basing on GitHub readme. I currently use TokenEndpoint to log user in.
services.AddOpenIddict<ApplicationUser, UsersDbContext>().EnableTokenEndpoint("/api/Account/Login")
Although calling /api/Account/Info works and it returns token in response, I need to get some basic data about logged in user (username, email, account type). Response from token endpoint doesn't provide that.
I've found something like UserinfoEndpoint:
.EnableUserinfoEndpoint("/api/Account/Info")
But what I see after in http response is:
{
"sub": "ea2248b4-a[...]70757de60fd",
"iss": "http://localhost:59381/"
}
This should return me some Claims. As it doesn't return anything, I assume that no Identity Claims were created during token generation.
What I need to know in a nutshell:
Is using Token Endpoint correct way to log in user?
Do I need to generate Claims by myself?
Can I control Claims by myself and how?
How to make some Claims visible through UserInfoEndpoint?
Is using Token Endpoint correct way to log in user?
Yep.
Do I need to generate Claims by myself?
The userinfo endpoint simply exposes the tokens stored in the access token (which is something that may change in the future).
Can I control Claims by myself and how?
How to make some Claims visible through UserInfoEndpoint?
To allow the userinfo endpoint to return more claims, you'll have to request more scopes. Read http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims for more information.
In a future version, we may allow you to return custom claims, but it's not currently supported.
I apologize in advance for some confusion over the terminology. I get a bit confused with the whole OAUTH process.
I've noticed that I need to pass a redirect_uri to the facebook grant access token method even though I can't see how it's being used. My server is making the request and getting the response so there is no redirect going on. Plus it seems that the redirect uri in the granting access token call must be the same one used in the request access token call (I understand that it's needed in the request access token call but not in the grant access token call).
Not sure this is needed but here is the code I'm using in order to get facebook to grant the access token.
var url = String.Format(
"https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&code={2}&redirect_uri={3}",
this.AppId,
this.AppSecretKey,
code,
System.Web.HttpContext.Current.Server.UrlEncode(this.CallbackUrl)
);
Yes, to help Facebook verify the request to exchange the auth code for an access token, the redirect_url must be the same for both requests.
You're right in that to get the access token, no actual redirect is performed, the access token is returned in the body of the HTTP response.