.Nat Zone

Digital Identity et al.

HAL enhanced OAuth 2.0 response – Making OAuth 2.0 slightly more RESTful

      2012/08/31

Many people claim that OAuth 2.0 is JSON and REST. Well, yes, it is RESTish, but not quite REST. It notably misses the hyperlink capability.

Let us see an example OAuth 2.0 (OpenID Connect)  token endpoint response:

{
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "id_token": "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso"
}

With this response alone, you do not know where it came from, and where it can be used in the next step. This actually stems from one of the weakness of JSON itself.

There are several attempts to mitigate it. One of them is an informational Internet Draft called HAL.

http://tools.ietf.org/html/draft-kelly-json-hal-03

This is a very simple spec to convert the conventional JSON document to Hyper-Linked REST response.

To denote hyperlinks, it proposes to insert a top level JSON object called “_links”. e.g.,

  "_links": {
       "self": { "href": "/tokens?code=asdfasdf },
       "userinfo": {
          "href": "/userinfo{?access_token}",
          "templated":true}
  }

Inserting it in the OAuth response, it will become:

{
  "_links": {
       "self": { "href": "/tokens?code=asdfasdf },
       "userinfo": {
          "href": "/userinfo{?id_token,scope,access_token,schema}",
          "templated":true}
  }
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "id_token": "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso"
}

Since it was an OpenID Connect token endpoint response, the resource that the client was going to access with the access_token was primarily UserInfo endpoint, so I have denoted it like above, but if the protected resource in question was like photo service, then it could have been like:

{
  "_links": {
       "self": { "href": "/tokens?code=asdfasdf },
       "photo": {
          "href": "https://photo.example.com/{?access_token,schema}",
          "templated":true}
  }
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "id_token": "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso"
}

You no longer need to know the protected resource endpoint URLs (e.g.,userinfo endpoint) in advance from discovery.

Now, in the above examples, I was using URI template (RFC6570). If I had a spec that allows me to express HTTP Authorize header, I would have done something like this instead:

{
  "_links": {
       "self": { "href": "/tokens?code=asdfasdf },
       "userinfo": {
          "href": "/userinfo{?id_token,scope,schema}",
          "Authorize":"{token_type} {access_token}",
          "templated":true}
  }
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "scope":"openid profile email", 
 "id_token": "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso"
}

Nice, is it not?

Changes

  • 2012-08-30 Changed “href” to include “id_token” and “scope” so that the “href” actually points to a resource consistently. “schema” dictates the representation.

 

 - identity, OpenID Connect