[Openid-specs-ab] About ID Tokens being access tokens

Andreas Åkre Solberg andreas.solberg at uninett.no
Mon Aug 29 19:23:39 UTC 2011


On 26. aug.2011, at 20:27, John Bradley wrote:

> An important issue for RP is perceived latency.  That is why they don't want to have to do a extra round trip to get the id_token.

That is a very good argument. 

> If you have a JS client that needs an access token as well as a id_token it would need to make two separate implicit calls to at the authorization endpoint.  
> I suspect that is the biggest problem.

In the 'OAuth approach', there is no way of obtaining multiple tokens in the implicit flow; the oauth approach would in this flow issue a multi-scope token to the RP. This could make things much simpler to JS clients. It could be a simpler and smarter way - or it could be a showstopper; anyone with first-hand knowledge to the implicit flow use case that can elaborate a bit on this?

Note that by issuing a multi-scoped token ( the OAuth way ) in the implicit flow; we solve the issue mentioned earlier about the necessity to include a hash of the access token in the id_token. 

Actually in OAuth implicit grant flow, you are NOT allowed to return both an access token and a refresh token; and I suspect that the reasoning behind this is the exact same that we run into with id_token and access token.

>  For a server it is also several round trips that each may require user consent.  From a user experience point of view one trip to the OP is enough.

No, there is no additional front-channel round trips in the proposal. Instead some backchannel calls are added to retrieve tokens, and id token + user data.

> We are running into a OAuth issue.  There should be a standard way to request multiple tokens.  

There is; (you may request multiple scopes in the request) and I'm suggesting that we use it. What there is not, is a way to requset multiple tokens and receive them all at the same time.

> In the case where you only want code in the redirect, scope is about the only place to say that you want a access token for the user-info endpoint and a id_token returned from the token endpoint.
> 
> That was why I was proposing that the openid scope be about the id_token and we define other scopes for user-info.

Great, that is a brilliant idea :) And this is also what I am suggesting :)

> That way scope controls what tokens you want (perhaps others for portable contacts etc), and response_type controls how you get them.

Exactly.

> There was a proposal to the allow multiple access tokens to be returned by making it an array or structure.  
> That didn't happen, so we are on our own to define how multiple tokens are returned.

If we need them returned all at once.

> That makes it more complicated to use standard libraries.    I don't think we are the only ones with this issue
> 
> The other issue with treating a id_token as an access token is that some frameworks don't make it easy to get at the contents of the token used to authenticate to the endpoint. 
> I suspect that is going to be a problem with them using any sort of JWT as an access token.

Yup. The response of the token endpoint is most likely handled all by the OAuth client library, and it should follow the OAuth spec section 4.2.2:

	The client SHOULD ignore unrecognized response parameters.
 
And I assume there it is a significant probability that these unrecognized response parameters are accessilble through the client library API.

This also is pointing in the direction of beeing more streamlined with OAuth.

Some arguments pro this approach:
* Simpler spec, easier to understand.
	No confusion of what is a token, and is a 'token' to be decoded or used as if it were an access token…
* Simpler to implement, more re-use of OAuth library. 

Some arguments against this approach:
* Additonal back-channel calls to resolve tokens, and tokens to JWT.
* Potentially a showstopper with a multi-purpose token in the implicit flow?

Here is an example of how the flows may look like (I hope it do not include too many errors):
Walkthrough of the OpenID Connect more streamlined with OAuth-Approach

Using the OAuth approach of obtaining two tokens (by the use of a refresh token), we gain the flexibility of both having the user data token and the session (id) token as two completely independent tokens; and in other use cases (like in implicit grant) have a shared multi-scoped token for use on both services.

Authorization Code Flow

Client prepares an authorization request:

GET /authorization?
    response_type=code&
    cliend_id=ab1&
    scope=openid-session%20openid-userdata HTTP/1.1
Host: rp.example.com
The user logs in, performs consent, etc. and the user agent is redirected back to the RP with the following response:

GET /callback?
    code=BBB3330
Host: consumer.example.org
Obtaining an session access token, and getting the ID Token

The client uses the access token endpoint to resolve an openid-session token:

POST /token
Host: rp.example.org
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW 
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

grant_type=authorization_code&
code=BBB3330&
scope=openid-session
The provider responds by issuing an openid-session access token, and also an refresh_token.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8 Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZFEjr1zCsicMWpAA", 
    "token_type":"bearer", 
    "scope": "openid-session",
    "expires_in":3600, 
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
Note: the openid-session access token is transparent and NOT an JWT. It may be exchanged for a JWT using the check session service.

The provider obtains the ID Token using the openid-session access token; performing a request to the check session endpoint; a basic OAuth protected endpoint:

POST /check_session HTTP/1.1
Accept: application/jwt
Authorization: Basic 2YotnFZFEjr1zCsicMWpAA
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
The provider returns an JWT (because of the Accept header, may also return unsigned JSON):

Content-Type: application/jwt;charset=UTF-8

eyJpc3MiOi.Jqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogIm.h0dHA6Ly9le
And the returned JWT may be decoded (and validated) by the client to something like:

{
  "issuer":"http://server.example.com",
  "client_id","http://client.example.com",
  "audience", "http://client.example.com",
  "user_id":"user_328723",
  "exp":1303852880
}
Obtaining an user data access token, and getting the user data

The client uses the already obtained refresh_token when accessing the OAuth Token Service, and requests the scope openid-userdata:

POST /token
Host: rp.example.org
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW 
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

grant_type=refresh_token&
refresh_token=tGzv3JOkF0XG5Qx2TlKWIA&
scope=openid-userdata
The provider responds by issuing an openid-userdata Access Token:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8 Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZFEjr1zCsicMWpAA", 
    "scope": "openid-userdata",
    "token_type":"bearer", 
    "expires_in":3600, 
}
The consumer, uses the OAuth protected user data endpoint to retrieve user data:

POST /user_info HTTP/1.1
Accept: application/jwt
Authorization: Basic 2YotnFZFEjr1zCsicMWpAA
Content-Type: application/x-www-form-urlencoded

schema=openid
The provider returns an JWT (because of the Accept header, may also return unsigned JSON):

Content-Type: application/jwt;charset=UTF-8

eyJpc3MiOi.Jqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogIm.h0dHA6Ly9le
And the returned JWT may be decoded (and validated) by the client to something like:

{
    "id": "90125",
    "name": "Jonathan Q. Doe"
    "given_name": "Jonathan",
    "middle_name": "Q.",
    "family_name": "Doe",
    "nickname": "John",
    "email": "johndoe at example.com",
    "verified": true,
    "profile": "http://example.com/johndoe/",
    "picture": "http://example.com/johndoe/me.jpg",
    "website": "http://john.doe.blogs.example.net/",
    "gender": "male",
    "birthday": "05/02/0000",
    "zoneinfo": "America/Los_Angeles"
    "locale": "en_US",
    "phone_number": "+1 (425) 555-1212",
    "address": {
        "region": "WA",
        "country": "United States"
    },
    "last_updated": "2011-06-29T21:10:22+0000"
}
Implicit Flow

The consumer, performs an authentication request:

GET /authorization?
    response_type=token&
    cliend_id=ab1&
    scope=openid-session%20openid-userdata HTTP/1.1
Host: rp.example.com
The user logs in, performs consent, etc. and the user agent is redirected back to the RP with the following response:

HTTP/1.1 302 Found
Location: http://consumer.example.org/callback#access_token=2YotnFZFEjr1zCsicMWpAA
&scope=openid-session%20openid-userdata&state=xyz&token_type=bearer&expires_in=3600
Then the consumer got an Access Token that is valid at both the User Data service and at the Session Service.

The user agent may send the access token to the web protected resource, and the web protected resource will use the access token with both the user info and session info service.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openid.net/pipermail/openid-specs-ab/attachments/20110829/4a610dbc/attachment.html>


More information about the Openid-specs-ab mailing list