Issuing two access tokens through hybrid flow

Issuing two access tokens through hybrid flow

Preface

You can use OpenID Connect’s “Hybrid” flows to issue two access tokens for a client through a single authorization flow.

This article describes a use case of issuing access tokens with restricted scopes and/or a shortened duration, and how Authlete processes it.

Use case

The hybrid flows are useful for authorization server to issue access tokens for a client that consists of a native application (e.g. mobile app) and an web server application (e.g. app backend), with a single authorization request.

A native application is often a public client, whose security risk is higher than a confidential client such as an web server application. In order to mitigate the risk, the hybrid flows can issue an access token for each side and restrict scopes (subsetting) of the particular token for the native application.

Furthermore,  for an web server application that makes requests to high-stakes APIs, the flows allow authorization servers to issue access tokens with a shortened duration.

How it works using Authlete

Authlete supports the hybrid flows. For example, its /auth/authorization/issue API can handle an authorization request with “response_type=code token” and create an authorization response including an authorization code and an access token. Also, its /auth/token API accepts a token request including the authorization code and creates a token response including another access token.

Authlete’s token updating API (/auth/token/update API) allows authorization servers to modify properties of access tokens, such as scopes, duration. These modifications are applied to access tokens stored in Authlete.

On receiving such access tokens from clients, resource servers will check status and details of the tokens, using Authlete’s introspection API (/auth/introspection ) and obtain the information modified in the previous step.

hybrid-flow-1

Examples

This section describes examples where an authorization server issues two access tokens on receiving an authorization request with “response_type=code token” and modifies scopes and duration for each of the tokens.

hybrid-flow-2

Obtaining an access token to be sent back in an authorization response

On receiving an authorization request from a client (via a user agent), an authorization server typically does the following processes.

  • Send content of the authorization request to Authlete’s /auth/authorization API (step #2, #3)
  • Authenticate an end user and ask consent (step #4, #5)
  • Make a request to  /auth/authorization/issue API and obtain content of an authorization response (step #6, #7)

The following example is a response from /auth/authorization/issue API  (step #7). It contains an access token as a value of “accessToken”, to be provided from the authorization server’s authorization endpoint to the client. Content of “responseContent” indicates that the token has scope=openid profile payment. (all examples below are folded for readability)

{
    "type": "authorizationIssueResponse",
    "resultCode": "A040001",
    "resultMessage": "[A040001] The authorization request was processed successfully.",
    "accessToken": "stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c",
    "accessTokenDuration": 1800,
    "accessTokenExpiresAt": 1596379999493,
    "action": "LOCATION",
    "authorizationCode": "iEATjO9V24Rubj1_PnATziCGh4ivVPRcTK_VZOsGplI",
    "responseContent": "https://client.example.org/cb/example.com
                        #code=iEATjO9V24Rubj1_PnATziCGh4ivVPRcTK_VZOsGplI
                        &access_token=stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c
                        &token_type=Bearer
                        &expires_in=1800
                        &scope=openid+profile+payment"
}

Narrowing (subsetting) scopes

The access token has three scopes: openid, profile and payment. The authorization server modifies the granted scopes (removes unnecessary scopes) using Authlete’s /auth/token/update API (step #6, #7). In this example, the server removes “payment” scope and preserves “openid” and “profile.”

If JWT-based access token is enabled, the value to be specified as “accessToken” is a value of “accessToken” in the previous response, which is not a JWT-formatted one (“jwtAccessToken”).

  • Request (step #6 using curl command)
curl -s -X POST $apiUrl/auth/token/update \
  -u $apiKey:$apiSecret \
  -H 'Content-type: application/json' \
  -d '{"accessToken":"stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c", \
      "scopes":["openid","profile"]}'

  • Response (step #7)
{
    "type": "tokenUpdateResponse",
    "resultCode": "A135001",
    "resultMessage": "[A135001] Updated the access token successfully.",
    "accessToken": "stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c",
    "accessTokenExpiresAt": 1596379999000,
    "action": "OK",
    "scopes": [
        "openid",
        "profile"
    ],
    "tokenType": "Bearer"
}

The authorization server will send back the content of “responseContent”, obtained from a response of /auth/authorization/issue API as shown above, to the client (step #8).

Note that values of scope parameter in the “responseContent” are the same as the original ones (“scope=openid+profile+payment”). Also note that, if JWT-based access token is enabled, the values of “scopes” in the access token are not updated i.e. “scope”: “openid profile payment”.

Obtaining an access token to be sent back in a token response

On receiving a token request from a client (step #13), an authorization server typically does the following process.

  • Send content of the token request to Authlete’s /auth/token API (step #14, #15)

The following example is a response from /auth/token API (step #15). It contains an access token as a value of “accessToken”, to be provided from the authorization server’s token endpoint to the client. A value of “accessTokenExpiresAt” indicates that the token is to be expired on 1596380052227 (in Unix time).

{
   "type": "tokenResponse",
   "resultCode": "A050001",
   "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
   "accessToken": "zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg",
   "accessTokenDuration": 1800,
   "accessTokenExpiresAt": 1596380052227,
   "action": "OK",
   "clientId": 17201083166161,
   "clientIdAlias": "clientapp01",
   "clientIdAliasUsed": false,
   "grantType": "AUTHORIZATION_CODE",
   "idToken": "eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjE3MjAxMDgzMTY2MTYxIl0s ImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTk2NDY0NjUy LCJpYXQiOjE1OTYzNzgyNTJ9. uJiJxISW7d0HEgOyQsIFbqptOREeqs9zJYMs_ZhZ72w",
   "refreshToken": "98YoC89H9CYcc56amo0bvNZ8pV02dYZmxCvB0Wz31jU",
   "refreshTokenDuration": 9000,
   "refreshTokenExpiresAt": 1596387252227,
   "responseContent":
        "{\"access_token\":\"zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg\",
        \"refresh_token\":\"98YoC89H9CYcc56amo0bvNZ8pV02dYZmxCvB0Wz31jU\",
        \"scope\":\"openid profile payment\",
        \"id_token\":
          \"eyJhbGciOiJIUzI1NiJ9.
            eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjE3MjAxMDgzMTY2MTYxIl0s
            ImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTk2NDY0NjUy
            LCJpYXQiOjE1OTYzNzgyNTJ9.
            uJiJxISW7d0HEgOyQsIFbqptOREeqs9zJYMs_ZhZ72w\",
        \"token_type\":\"Bearer\",
        \"expires_in\":1800}",
   "scopes": [
      "openid",
      "profile",
      "payment"
   ],
   "subject": "testuser01"
}

Shortening a duration

In the same way as the previous example, the authorization server modifies the duration of the access token using Authlete’s /auth/token/update API (step #16, #17). In this example, it specifies 1596379152000, that means 900 seconds (900,000 milliseconds) shorter than the original duration.

  • Request (Step #16 using curl command)
curl -s -X POST $apiUrl/auth/token/update \
    -u $apiKey:$apiSecret \
    -H 'Content-type: application/json' \
    -d '{"accessToken":"zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg", \
        "accessTokenExpiresAt":1596379152000}'

  • Response (step #17)
{
    "type": "tokenUpdateResponse",
    "resultCode": "A135001",
    "resultMessage": "[A135001] Updated the access token successfully.",
    "accessToken": "zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg",
    "accessTokenExpiresAt": 1596379152000,
    "action": "OK",
    "scopes": [
        "openid",
        "profile",
        "payment"
    ],
    "tokenType": "Bearer"
}