Learn about the TAG OAuth 2.0 Security Profile.
Learn how to manage an OAuth 2.0 Token (issue, validate, refresh and revoke).
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.
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.
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.
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
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:
Username: alice
Email: [email protected]
Fullname: Alice
Roles: administrator
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.
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:
Username: movies_app
Email: [email protected]
Fullname: Movies App
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
.
We should now be ready to issue our first 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.
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.
You can copy both and used them in jwt.io by the Verify Signature
section to
validate the 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"
}
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.
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
}
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.
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
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.