Product Version

OAuth 2.0 Token Management Guide

Learning Objectives

  • Learn about the TAG OAuth 2.0 Security Profile.

  • Learn how to manage an OAuth 2.0 Token (issue, validate, refresh and revoke).

Prerequisites

  • Docker

  • Curl

  • TAG Quickstart Guide (optional)

  • Python (optional)

  • Python Pygments (optional)

The OAuth 2.0 Authorization Framework

The OAuth 2.0 Authorization framework provides a way to obtain limited access to user information on an HTTP service.

Instead of using the resource owner’s credentials to access protected resources, the client obtains an access token — a string denoting a specific scope, lifetime, and other access attributes. Access tokens are issued to third-party clients by TAG with the approval of the resource owner. The client uses the access token to access the protected resources hosted by TAG or any other resource server.

The TAG OAuth 2.0 implementation, based on the RFC OAuth2 Authorization Framework, uses JSON Web Tokens (JWT RFC JSON Web Tokens) as the tokens issued and used to authenticate and access protected endpoints.

JWT tokens consist of a header, a set of claims, which are both Base64 encoded, and a signature calculated using RSA with SHA256. Overall, this creates three Base64 strings which are concatenated together with dots (.). Each use of a JWT token within TAG is validated using the TAG RSA public key, preventing tokens issued by the TAG from being tampered with.

OAuth 2.0 Security Profile

In TAG, a Security Profile is a runtime configuration of an HTTP Security scheme. It offers the flexibility of configuring different aspects of the scheme. This allows a complete control of all the generated Tokens, Authentication and Authorization that takes place.

TAG already ships with a convenient default OAuth 2.0 Profile, that allows you to quickly start issuing OAuth 2.0 Tokens. Of course you are free to create your own to suit your own needs.

oauth2 token management 01 oauth2 profile
Figure 1. TAG OAuth 2.0 Security Profile

Issue a Token

Usually, to issue a token, we need the following information:

  • grant_type

  • username

  • password

  • client_id

  • client_secret

The most common way to issue a token is to use the password grant type, given the username and password of the resource owner. This allows you to enable password logins to your own applications. The client_id and the client_secret are used to authenticate the third-party application.

In TAG, this means that we need two Accounts. One to identify the user and another one to identify the service. Let’s try it.

Run the Sample

If you already have TAG running from another quick start guide you can skip this step. Or if you have the container already create but stopped, start it with the command docker start tag.

Use Docker to start an instance of the TAG with the command:

docker run -e LICENSE=accept --name tag -p 8080:8080 tomitribe/tribestream-api-gateway

Setup

Let’s start by creating an account. If you have followed the TAG Quickstart Guide, you already know how to do that. If you already have an account named Alice you can skip this step.

Click on the Accounts to navigate to the Accounts page, click the + button on the upper right side and select Account. Create a new account with the following information:

oauth2 token management 02 create account
Figure 2. Create Account

Hit Save. Click the …​ button on the upper right side and select Add Password from the menu. In the Add Password modal window, type supersecret as the password and then click the Save button.

oauth2 token management 03 add password
Figure 3. Add Password

Next, let’s create an Account for an application we are developing, the Movie Client Application. Repeat the same steps to create the account with the following information:

When setting the password, instead of picking the Add Password option, use the Add Client Secret. Use the secret moviessecret, keep the default OAuth Security Profile and hit Save.

oauth2 token management 04 add client secret
Figure 4. Add Client Secret

We should now be ready to issue our first token.

Issue the Token

To issue the OAuth 2.0 Token, we are going to use the 'curl' command to send the request to TAG. Data needs to be posted as a Form with the following Form Parameters:

Form Parameter Value

grant_type

password

username

alice

password

supersecret

client_id

movies_app

client_secret

moviessecret

This translates in the following curl command:

  curl -s \
        "http://localhost:8080/oauth2/token" \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode "grant_type=password" \
        --data-urlencode "username=alice" \
        --data-urlencode "password=supersecret" \
        --data-urlencode "client_id=movies_app" \
        --data-urlencode "client_secret=moviessecret"

Run the command. You should get an output simular to this:

{
    "access_token": "eyJraWQiOiJvYXV0aDIta2V5cGFpciIsImN0eSI6Impzb24iLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXRva2VuIiwibmJmIjoxNTMxODQzNjQ2LCJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sIm5hbWUiOiJhbGljZSIsImlzcyI6Ilwvb2F1dGgyXC90b2tlbiIsImdyb3VwcyI6W10sInRhZy1pbnRlcm5hbCI6eyJncmFudC10eXBlIjoicGFzc3dvcmQiLCJwcm9maWxlIjoiT0F1dGgyIFByb2ZpbGUiLCJjbGllbnQtaWQiOiJtb3ZpZXNfYXBwIiwidmVyc2lvbiI6IjEuMCIsInVzZXJuYW1lIjoiYWxpY2UifSwiZXhwIjoxNTMxODQ1NDQ2LCJpYXQiOjE1MzE4NDM2NDYsImVtYWlsIjoiYWxpY2VAZW1haWwuY29tIiwianRpIjoiNDc4NGNhNGI1MDdiN2ExZiJ9.WqmRH0uBInkqzvVcbvutux3A_iyBlEUmxVB_iD0vcbF0fx7T_291KUGZ0Vc2rsvRkRHWneFw1eUtRm3EzEZmv9PFuyo8eb8vKE0zltD9P4FsT7-NfthXnaZL87QfRXX-fTKO_jCfKL78zN7niX_lcMe31TFh1osnXz8HM_HleOsLJ6gr-FFkytnSoc7MRVWBmAl8fNcDKoF1r9qolzQNo1rzzo-Wlhykr5VtObps083856hwKjKNhYhTScnI6lI0-UAkJEQDIYd3bugLXOsyC3HuXm2ijAnqfrvT6MHxlPkVnYk_Y68hhKQwNQiCN0KAcOY1NQAineFofrYdZiGxMA",
    "expires_in": 1799,
    "refresh_token": "eyJraWQiOiJvYXV0aDIta2V5cGFpciIsImN0eSI6Impzb24iLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0b2tlbi10eXBlIjoicmVmcmVzaC10b2tlbiIsIm5iZiI6MTUzMTg0MzY0NiwiaXNzIjoiXC9vYXV0aDJcL3Rva2VuIiwidGFnLWludGVybmFsIjp7Imp3dC1hY2Nlc3MtdG9rZW4iOiJleUpoYkdjaU9pSnViMjVsSW4wLmV5SjBiMnRsYmkxMGVYQmxJam9pWVdOalpYTnpMWFJ2YTJWdUlpd2libUptSWpveE5UTXhPRFF6TmpRMkxDSnliMnhsY3lJNld5SmhaRzFwYm1semRISmhkRzl5SWwwc0ltNWhiV1VpT2lKaGJHbGpaU0lzSW1semN5STZJbHd2YjJGMWRHZ3lYQzkwYjJ0bGJpSXNJbWR5YjNWd2N5STZXMTBzSW5SaFp5MXBiblJsY201aGJDSTZleUpuY21GdWRDMTBlWEJsSWpvaWNHRnpjM2R2Y21RaUxDSndjbTltYVd4bElqb2lUMEYxZEdneUlGQnliMlpwYkdVaUxDSmpiR2xsYm5RdGFXUWlPaUp0YjNacFpYTmZZWEJ3SWl3aWRtVnljMmx2YmlJNklqRXVNQ0lzSW5WelpYSnVZVzFsSWpvaVlXeHBZMlVpZlN3aVpYaHdJam94TlRNeE9EUTFORFEyTENKcFlYUWlPakUxTXpFNE5ETTJORFlzSW1WdFlXbHNJam9pWVd4cFkyVkFaVzFoYVd3dVkyOXRJaXdpYW5ScElqb2lNRE5qT1dJeE4ySXlOakZrWXpRd1l5SjkuIiwiZ3JhbnQtdHlwZSI6InBhc3N3b3JkIiwicHJvZmlsZSI6Ik9BdXRoMiBQcm9maWxlIiwicmVmcmVzaC10aW1lcyI6MCwiY2xpZW50LWlkIjoibW92aWVzX2FwcCIsInZlcnNpb24iOiIxLjAiLCJ1c2VybmFtZSI6ImFsaWNlIn0sImV4cCI6MTUzNDQzNTY0NiwiaWF0IjoxNTMxODQzNjQ2LCJqdGkiOiJmYjU2NzcyY2QxYzk2YWEzIn0.NMugrjGdCBZ28jKuJxAGXHlRiU53hCVnLSEpTyOVXMtDhLhsJstYmQ1YY5KqcZWhAvazqCVhyyh7IGsx-dSS9NE0FsLRSe3iwaYc6myveLzqALdXQcmReWZ4PIKW3us0UnDQExD9jv4zka0esf-5etBDHxW8AkmuSqzeAVHISG0ik8f-K05uvPGPc0UBAZdpTO0pIAWfMAfeM5ui7ZNLbpBnwGkyL1lmfGS0AN4Bvp4jJPJLsnVbjVvbUGasdoenQySbehs-mnyeWd5WyLlmIGS5TdcDVfuF6MnZJPMrOS6ZW_4CYlEB8F-GDg9puzRF1Ok0MczCyKJFhqfEIhybjg",
    "scope": "administrator",
    "token_type": "bearer"
}
If you have Python and Python Pygments installed you can pipe the curl result with | python -m json.tool | pygmentize -l json to get a pretty version of the JSON response.

The OAuth 2.0 Token Response contains:

  • access_token The actual access token in JWT format issued by TAG.

  • expires_in The duration of time the access token is granted for (in seconds).

  • refresh_token A refresh token which clients can use to obtain another access token.

  • token_type The type of the token. Usually bearer.

  • scope a list of roles assigned to the user in the TAG.

Validate a Token

Tokens may be invalid if they are beyond their expiration date or if the key used to sign it doesn’t match the OAuth 2.0 Security Profile of TAG.

The TAG provides you a specific endpoint that you can use to validate if an Access Token is valid. To validate a token, you need the access_token response value and post it in the token Form Parameter with the following curl command:

curl -s \
        "http://localhost:8080/oauth2/introspect" \
        --user admin:admin \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode "token=ACCESS_TOKEN_REPLACE_ME"

If the Token is valid you should get a response similar to:

{
    "active": true,
    "exp": 1531838438000,
    "iat": 1531836638000,
    "iss": "/oauth2/token",
    "jti": "6d0dc5c011a29b8b",
    "nbf": 1531836638000,
    "token_type": "bearer",
    "username": "alice"
}

You could also validate the JWT, using the external service jwt.io. Just copy the token into the Encoded text area of the webpage. It actually unwraps the JWT, so you can see how it looks.

To verify the TAG signature, you need the Public and Private key. These can be retrieved from the OAuth 2.0 Security Profile Detail page. Go to http://localhost:8080/tag/profile/oauth2/oauth2-profile and scroll down to the Signature section. You should find a Key Id entry. Mouseover the …​ icon to reveal options to view the Public and Private key.

oauth2 token management 05 public key
Figure 5. OAuth 2.0 Security Profile Public Key

You can copy both and used them in jwt.io by the Verify Signature section to validate the token.

oauth2 token management 06 jwtio validate
Figure 6. JWT.io Validate OAuth 2.0 Token

Refresh a Token

By default, TAG issues a Refresh Token that clients can use to retrieve a new Access Token when expired. This allows you to limit the risk of leaking an Access Token (depending on its expirations period). It also prevents the need to resent the user credentials again since the new token is now obtained using the Refresh Token.

To refresh an Access Token, you need the refresh_token response value obtained in the issued token and post it in the refresh_token parameter. You also need to set the grant_type parameter to refresh_token. The curl command will look like this:

curl -s \
        "http://localhost:8080/oauth2/token" \
        --user movies_app:moviessecret \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode "grant_type=refresh_token" \
        --data-urlencode "refresh_token=REFRESH_TOKEN_REPLACE_ME"

And the response should get you a new token:

{
    "access_token": "eyJraWQiOiJvYXV0aDIta2V5cGFpciIsImN0eSI6Impzb24iLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXRva2VuIiwibmJmIjoxNTMxODQzNzQ4LCJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sIm5hbWUiOiJhbGljZSIsImlzcyI6Ilwvb2F1dGgyXC90b2tlbiIsImdyb3VwcyI6W10sInRhZy1pbnRlcm5hbCI6eyJncmFudC10eXBlIjoicmVmcmVzaF90b2tlbiIsInByb2ZpbGUiOiJPQXV0aDIgUHJvZmlsZSIsImNsaWVudC1pZCI6Im1vdmllc19hcHAiLCJ2ZXJzaW9uIjoiMS4wIiwidXNlcm5hbWUiOiJhbGljZSJ9LCJleHAiOjE1MzE4NDU1NDgsImlhdCI6MTUzMTg0Mzc0OCwiZW1haWwiOiJhbGljZUBlbWFpbC5jb20iLCJqdGkiOiJjOTNkOWI0YjMyOWU2YjBhIn0.ij2MZENY_NrTpPuoGsaWvBDZCW2D25AUhDoiUcRT5jEV8hNic8-nqHgWqove3-t07isWnhGhSNZ7TJbwoxV3W_0jEBq7M6Ex7fRCpQ3ezxBHqtYWwWMakXkjMLJn_Ym6ZfTGaDSbcV38tUJZiHmrxaGBRD1dHBZW0hL4RFJkTWPr0aI_GyXa0kxNAwERFD4kImc3T2kQuD1rZdzALy_NxxLkU20V4u8Ekk6f2f1kPdjervs8pH1EpP_DzguWvg_ZQmUma1BSdSN1Fpv8PWzenqPgyotx-uqlvbkpxcznqtz9fEcm2q-ZVvyvshUn5hRJRoNu5_9kLBG9ZqM2sB6z2w",
    "expires_in": 1799,
    "refresh_token": "eyJraWQiOiJvYXV0aDIta2V5cGFpciIsImN0eSI6Impzb24iLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0b2tlbi10eXBlIjoicmVmcmVzaC10b2tlbiIsIm5iZiI6MTUzMTg0Mzc0OCwiaXNzIjoiXC9vYXV0aDJcL3Rva2VuIiwidGFnLWludGVybmFsIjp7Imp3dC1hY2Nlc3MtdG9rZW4iOiJleUpoYkdjaU9pSnViMjVsSW4wLmV5SjBiMnRsYmkxMGVYQmxJam9pWVdOalpYTnpMWFJ2YTJWdUlpd2libUptSWpveE5UTXhPRFF6TnpRNExDSnliMnhsY3lJNld5SmhaRzFwYm1semRISmhkRzl5SWwwc0ltNWhiV1VpT2lKaGJHbGpaU0lzSW1semN5STZJbHd2YjJGMWRHZ3lYQzkwYjJ0bGJpSXNJbWR5YjNWd2N5STZXMTBzSW5SaFp5MXBiblJsY201aGJDSTZleUpuY21GdWRDMTBlWEJsSWpvaWNtVm1jbVZ6YUY5MGIydGxiaUlzSW5CeWIyWnBiR1VpT2lKUFFYVjBhRElnVUhKdlptbHNaU0lzSW1Oc2FXVnVkQzFwWkNJNkltMXZkbWxsYzE5aGNIQWlMQ0oyWlhKemFXOXVJam9pTVM0d0lpd2lkWE5sY201aGJXVWlPaUpoYkdsalpTSjlMQ0psZUhBaU9qRTFNekU0TkRVMU5EZ3NJbWxoZENJNk1UVXpNVGcwTXpjME9Dd2laVzFoYVd3aU9pSmhiR2xqWlVCbGJXRnBiQzVqYjIwaUxDSnFkR2tpT2lKa1pXTTBaV0ZtT1Rsak1HUTJPRFl6SW4wLiIsImdyYW50LXR5cGUiOiJyZWZyZXNoX3Rva2VuIiwicHJvZmlsZSI6Ik9BdXRoMiBQcm9maWxlIiwicmVmcmVzaC10aW1lcyI6MSwiY2xpZW50LWlkIjoibW92aWVzX2FwcCIsInZlcnNpb24iOiIxLjAiLCJ1c2VybmFtZSI6ImFsaWNlIn0sImV4cCI6MTUzNDQzNTc0OCwiaWF0IjoxNTMxODQzNzQ4LCJqdGkiOiIzNzYwYzQ2ODNhZTU4ZmFhIn0.CRrfOsfMsfICfuSjY01OpHJqvO1KW1lsbqP1Jpg7zLr-urEn5S1XmHXQaiG4L8mvT-P5apkbtl8tbfMBKc90UIorg_MoeypIZVyy9hJxQltvOtNGMg3pX9iDylpiuqlfnqmZEAUwtp-gTE7p7uq2nq2bmGM7uy0YcvU3hu48Wtg3mamF5rQsDDOAFEBr9sN6vys_bsZ3xDheayCXGeOEaqkiDt6p_geyvlJ7lD5BvBAtowmZ0Qm95ZFznApWGbjMlDvMpJnAt3v8V8e0f1U1KrscI_c2Zh5CYWLfv5BzafL_1cExAwxZDFmgeayKHsLvzkrJQHjLyUO260SAZCpciw",
    "scope": "administrator",
    "token_type": "bearer"
}

Both Access Token and Refresh Token are issued again. The client uses the new issued Access Token, and when it needs to refresh again, uses new Refresh Token.

If you try to refresh with an old Refresh Token, you will get the following error:
{
    "error": "grant_invalid",
    "error_description": "The refresh token is invalid"
}

Revoke a Token

It is possible for the client to Revoke a Token, making it unusable. This allows to simulate a logout mechanism for clients and clean up resources associated with the Token. TAG only allows revoking Refresh Tokens, since it doesn’t keep the state about Access Tokens.

The TAG provides you a specific endpoint that you can use to revoke an Access Token. To revoke a token, you need the refresh_token response value and post it in the token Form Parameter with the following curl command:

curl -s -i \
        "http://localhost:8080/oauth2/revoke" \
        --user movies_app:moviessecret \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode "token=REFRESH_TOKEN_REPLACE_ME"

And the response should be a plain 200 - OK.

It is also possible to revoke the Refresh Token via the TAG UI. Just go to http://localhost:8080/tag/refresh-tokens. You should get a list of all the valid Refresh Tokens. Deleting a Refresh Token entry is the same as Revoking it.

oauth2 token management 07 refresh tokens
Figure 7. Refresh Tokens List

And now if we try to validate using the curl command:

curl -s \
        "http://localhost:8080/oauth2/introspect" \
        --user admin:admin \
        -H 'Content-Type: application/x-www-form-urlencoded' \
       --data-urlencode "token=eyJraWQiOiJvYXV0aDIta2V5cGFpciIsImN0eSI6Impzb24iLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0b2tlbi10eXBlIjoicmVmcmVzaC10b2tlbiIsIm5iZiI6MTUzMTg0Mzc0OCwiaXNzIjoiXC9vYXV0aDJcL3Rva2VuIiwidGFnLWludGVybmFsIjp7Imp3dC1hY2Nlc3MtdG9rZW4iOiJleUpoYkdjaU9pSnViMjVsSW4wLmV5SjBiMnRsYmkxMGVYQmxJam9pWVdOalpYTnpMWFJ2YTJWdUlpd2libUptSWpveE5UTXhPRFF6TnpRNExDSnliMnhsY3lJNld5SmhaRzFwYm1semRISmhkRzl5SWwwc0ltNWhiV1VpT2lKaGJHbGpaU0lzSW1semN5STZJbHd2YjJGMWRHZ3lYQzkwYjJ0bGJpSXNJbWR5YjNWd2N5STZXMTBzSW5SaFp5MXBiblJsY201aGJDSTZleUpuY21GdWRDMTBlWEJsSWpvaWNtVm1jbVZ6YUY5MGIydGxiaUlzSW5CeWIyWnBiR1VpT2lKUFFYVjBhRElnVUhKdlptbHNaU0lzSW1Oc2FXVnVkQzFwWkNJNkltMXZkbWxsYzE5aGNIQWlMQ0oyWlhKemFXOXVJam9pTVM0d0lpd2lkWE5sY201aGJXVWlPaUpoYkdsalpTSjlMQ0psZUhBaU9qRTFNekU0TkRVMU5EZ3NJbWxoZENJNk1UVXpNVGcwTXpjME9Dd2laVzFoYVd3aU9pSmhiR2xqWlVCbGJXRnBiQzVqYjIwaUxDSnFkR2tpT2lKa1pXTTBaV0ZtT1Rsak1HUTJPRFl6SW4wLiIsImdyYW50LXR5cGUiOiJyZWZyZXNoX3Rva2VuIiwicHJvZmlsZSI6Ik9BdXRoMiBQcm9maWxlIiwicmVmcmVzaC10aW1lcyI6MSwiY2xpZW50LWlkIjoibW92aWVzX2FwcCIsInZlcnNpb24iOiIxLjAiLCJ1c2VybmFtZSI6ImFsaWNlIn0sImV4cCI6MTUzNDQzNTc0OCwiaWF0IjoxNTMxODQzNzQ4LCJqdGkiOiIzNzYwYzQ2ODNhZTU4ZmFhIn0.CRrfOsfMsfICfuSjY01OpHJqvO1KW1lsbqP1Jpg7zLr-urEn5S1XmHXQaiG4L8mvT-P5apkbtl8tbfMBKc90UIorg_MoeypIZVyy9hJxQltvOtNGMg3pX9iDylpiuqlfnqmZEAUwtp-gTE7p7uq2nq2bmGM7uy0YcvU3hu48Wtg3mamF5rQsDDOAFEBr9sN6vys_bsZ3xDheayCXGeOEaqkiDt6p_geyvlJ7lD5BvBAtowmZ0Qm95ZFznApWGbjMlDvMpJnAt3v8V8e0f1U1KrscI_c2Zh5CYWLfv5BzafL_1cExAwxZDFmgeayKHsLvzkrJQHjLyUO260SAZCpciw"

You should get a response back stating that the token is now invalid:

{
    "active": false
}

OAuth 2.0 Security Profile

The OAuth 2.0 Security Profile allows you to configure different aspectes related with OAuth 2.0 and the Token generation.

In the issue section, you are able to change the endpoint used to issue the OAuth 2 Token, plus set the available OAuth 2 flows or make the client id mandatory.

In the validation section, you are able to set up the header and the prefix used to authenticate the token when accessing a protected resource behind the TAG.

The Access Token Policy allows you to set the expiration time of a token after being issued or make the token valid forever.

The Refresh Token Policy also allows you to set the expiration time of a token after being issued or make the token valid forever. Plus, you can set a limit to the number of refreshes or disallow it completely.

The Signature section is where the key to sign the JWT is setup. The TAG can auto-generate the key for you, or you can input your own.

Finally, the Claims section allows you to retrieve user data from other sources to enhance the JOT payload.

Remember, you can create multiple OAuth 2 Security Profiles, with different configurations that will generate different JOTs. In this way, you can customize the experience of third-party clients trying to retrieve user information.

Cleanup

To stop TAG run the following command:

docker stop tag

And if you want to completely remove the associated Docker container run the following command:

docker remove tag

Summary

This guide was an introduction to the TAG OAuth 2.0 Token Management. It taught you how to issue, validate, refresh and revoke OAuth 2.0 tokens.

With TAG OAuth 2.0 tokens, you can issue authorization tokens to third-party clients with the approval of the resource owner, exposing only a limited fraction of the user information.

Use TAG OAuth 2.0 tokens to share the user information in a safe and reliable way.