Documentation

15. Token-Based Authentication

Starting with Ansible Tower 3.3, OAuth 2 is used for token-based authentication. You can manage OAuth tokens as well as applications, a server-side representation of API clients used to generate tokens. By including an OAuth token as part of the HTTP authentication header, you can authenticate yourself and adjust the degree of restrictive permissions in addition to the base RBAC permissions. Refer to RFC 6749 for more details of OAuth 2 specification.

15.1. Managing OAuth 2 Applications and Tokens

Applications and tokens can be managed as a top-level resource at /api/<version>/applications and /api/<version>/tokens. These resources can also be accessed respective to the user at /api/<version>/users/N/<resource>. Applications can be created by making a POST to either api/<version>/applications or /api/<version>/users/N/applications.

Each OAuth 2 application represents a specific API client on the server side. For an API client to use the API via an application token, it must first have an application and issue an access token. Individual applications are accessible via their primary keys: /api/<version>/applications/<pk>/. Here is a typical application:

    {
    "id": 1,
    "type": "o_auth2_application",
    "url": "/api/v2/applications/2/",
    "related": {
        "tokens": "/api/v2/applications/2/tokens/"
    },
    "summary_fields": {
        "organization": {
            "id": 1,
            "name": "Default",
            "description": ""
        },
        "user_capabilities": {
            "edit": true,
            "delete": true
        },
        "tokens": {
            "count": 0,
            "results": []
        }
    },
    "created": "2018-07-02T21:16:45.824400Z",
    "modified": "2018-07-02T21:16:45.824514Z",
    "name": "My Application",
    "description": "",
    "client_id": "Ecmc6RjjhKUOWJzDYEP8TZ35P3dvsKt0AKdIjgHV",
    "client_secret": "7Ft7ym8MpE54yWGUNvxxg6KqGwPFsyhYn9QQfYHlgBxai74Qp1GE4zsvJduOfSFkTfWFnPzYpxqcRsy1KacD0HH0vOAQUDJDCidByMiUIH4YQKtGFM1zE1dACYbpN44E",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false,
    "organization": 1
}

As shown in the example above, name is the human-readable identifier of the application. The rest of the other fields, like client_id and redirect_uris, are mainly used for OAuth2 authorization, which is covered later in Using OAuth 2 Token System for Personal Access Tokens (PAT).

The values for the client_id and client_secret fields are generated during creation and are non-editable identifiers of applications, while organization and authorization_grant_type are required upon creation and become non-editable.

15.1.1. Access Rules for Applications

Access rules for applications are as follows:

  • System administrators can view and manipulate all applications in the system
  • Organization administrators can view and manipulate all applications belonging to Organization members
  • Other users can only view, update, and delete their own applications, but cannot create any new applications

Tokens, on the other hand, are resources used to actually authenticate incoming requests and mask the permissions of the underlying user. There are two ways of creating a token:

  • POSTing to the /api/v2/tokens/ endpoint by providing application and scope fields to point to the related application and specify token scope
  • POSTing to /api/v2/applications/<pk>/tokens/ by providing only scope, while the parent application will be automatically linked

Individual tokens will be accessible via their primary keys: /api/<version>/tokens/<pk>/. Here is an example of a typical token:

{
    "id": 4,
    "type": "o_auth2_access_token",
    "url": "/api/v2/tokens/4/",
    "related": {
        "user": "/api/v2/users/1/",
        "application": "/api/v2/applications/1/",
        "activity_stream": "/api/v2/tokens/4/activity_stream/"
},
    "summary_fields": {
        "application": {
            "id": 1,
            "name": "Default application for root",
            "client_id": "mcU5J5uGQcEQMgAZyr5JUnM3BqBJpgbgL9fLOVch"
        },
        "user": {
            "id": 1,
            "username": "root",
            "first_name": "",
            "last_name": ""
        }
    },
    "created": "2018-02-23T14:39:32.618932Z",
    "modified": "2018-02-23T14:39:32.643626Z",
    "description": "App Token Test",
    "user": 1,
    "token": "*************",
    "refresh_token": "*************",
    "application": 1,
    "expires": "2018-02-24T00:39:32.618279Z",
    "scope": "read"
},

For an OAuth 2 token, the only fully editable fields are scope and description. The application field is non-editable on update, and all other fields are entirely non-editable, and are auto-populated during creation, as follows:

  • user field corresponds to the user the token is created for, and in this case, is also the user creating the token
  • expires is generated according to the Tower configuration setting OAUTH2_PROVIDER
  • token and refresh_token are auto-generated to be non-clashing random strings

Both application tokens and personal access tokens are shown at the /api/v2/tokens/ endpoint. The application field in the personal access tokens is always null. This is a good way to differentiate the two types of tokens.

15.1.2. Access rules for tokens

Access rules for tokens are as follows:

  • Users can create a token if they are able to view the related application; and are also able to create a personal token for themselves
  • System administrators are able to view and manipulate every token in the system
  • Organization administrators are able to view and manipulate all tokens belonging to Organization members
  • System Auditors can view all tokens and applications
  • Other normal users are only able to view and manipulate their own tokens

Note

Users can only view the token or refresh the token value at the time of creation only.

15.2. Using OAuth 2 Token System for Personal Access Tokens (PAT)

The most common use of OAuth 2 is authenticating users. The token field of a token is used as part of HTTP authentication header, in the format of Authorization: Bearer <token field value>. The Bearer token can be obtained by issuing a curl command at the /api/o/token/ endpoint, as shown in this example below:

curl -ku <user>:<password> -H "Content-Type: application/json" -X POST \
-d '{"description":"Tower CLI", "application":null, "scope":"write"}' \
https://<tower>/api/v2/users/1/personal_tokens/ | python -m json.tool

Note

The special OAuth 2 endpoints only support using the x-www-form-urlencoded Content-type, so as a result, none of the api/o/* endpoints accept application/json.

Following is an example of using the personal token to access an API endpoint using curl:

curl -k -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -X POST  -d '{}' https://tower/api/v2/job_templates/5/launch/

According to OAuth 2 specification, users can acquire, revoke, and refresh an access token. In Ansible Tower, the equivalent, and most efficient way to refresh a token, is create a token, delete a token, and then quickly followed by creating a new one. To revoke a token, simply delete it in the Applications configuration of the user interface, or at the token’s detail page in the API.

In Ansible Tower, the OAuth 2 system is built on top of the Django Oauth Toolkit, which provides dedicated endpoints for authorizing, revoking, and refreshing tokens. Tower implements them and puts related endpoints under the /api/o/ endpoint, which also provides detailed examples on some typical usage of those endpoints.

Note

The /api/o/ endpoints can only be used for application tokens, and are not valid for personal access tokens.

15.2.1. Token scope mask over RBAC system

The scope of an OAuth 2 token is a space-separated string composed of valid scope keywords, ‘read’ and ‘write’. These keywords are configurable and used to specify permission level of the authenticated API client. Read and write scopes provide a mask layer over the Role-Based Access Control (RBAC) permission system of Ansible Tower. Specifically, a ‘write’ scope gives the authenticated user the full permissions the RBAC system provides, while a ‘read’ scope gives the authenticated user only read permissions the RBAC system provides. Note that ‘write’ implies ‘read’ as well.

For example, if you have administrative permissions to a job template, you can view, modify, launch, and delete the job template if authenticated via session or basic authentication. In contrast, if you are authenticated using OAuth 2 token, and the related token scope is ‘read’, you can only view, but not manipulate or launch the job template, despite being an administrator. If the token scope is ‘write’ or ‘read write’, you can take full advantage of the job template as its administrator.

To acquire and use a token, first create an application token:

  1. Make an application with authorization_grant_type set to password. HTTP POST the following to the /api/v2/applications/ endpoint (supplying your own organization ID):
{
    "name": "Admin Internal Application",
    "description": "For use by secure services & clients. ",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false,
    "organization": <organization-id>
}
  1. Make a token and POST to the /api/v2/tokens/ endpoint:
{
    "description": "My Access Token",
    "application": <application-id>,
    "scope": "write"
}

This returns a <token-value> that you can use to authenticate with for future requests (this will not be shown again).

  1. Use the token to access a resource. The following uses curl as an example:
curl -H "Authorization: Bearer <token-value>" -H "Content-Type: application/json" -X GET https://<tower>/api/v2/users/

The -k flag may be needed if you have not set up a CA yet and are using SSL.

To revoke a token, you can make a DELETE on the detail page for that token, using that token’s ID. For example:

curl -ku <user>:<password> -X DELETE https://<tower>/api/v2/tokens/<pk>/

Similarly, using a token:

curl -H "Authorization: Bearer <token-value>" -X DELETE https://<tower>/api/v2/tokens/<pk>/ -k

15.3. Application Functions

This page lists OAuth 2 utility endpoints used for authorization, token refresh, and revoke. Endpoints other than /api/o/authorize/ are not meant to be used in browsers and do not support HTTP GET. The endpoints prescribed here strictly follow RFC specifications for OAuth 2, so use that for detailed reference. The implicit grant type can only be used to acquire an access token if you are already logged in via session authentication, as that confirms that you are authorized to create an access token. The following is an example of the typical usage of these endpoints in Tower, in particular, when creating an application using various grant types:

  • Authorization Code
  • Implicit
  • Password

15.3.1. Application using authorization code grant type

The application, grant type is intended to be used when the application is executing on the server. To create an application named AuthCodeApp with the authorization-code grant type, perform a POST to the /api/v2/applications/ endpoint:

{
    "name": "AuthCodeApp",
    "user": 1,
    "client_type": "confidential",
    "redirect_uris": "http://<tower>/api/v2",
    "authorization_grant_type": "authorization-code",
    "skip_authorization": false
}

Test the authorization flow with the new application by copying the client_id and URI link into the Django-oauth-toolkit simple test application and click Submit.

The workflow that occurs when you issue a GET to the authorize endpoint from the client application with the response_type, client_id, redirect_uris, and scope:

  1. Tower responds with the authorization code and status to the redirect_uri specified in the application.
  2. The client application then makes a POST to the api/o/token/ endpoint on Tower with the code, client_id, client_secret, grant_type, and redirect_uri.
  3. Tower responds with the access_token, token_type, refresh_token, and expires_in.

Refer to Django’s Test Your Authorization Server toolkit to test this flow.

15.3.2. Application using implicit grant type

An implicit grant type is used when a single page web application can’t keep a client_secret secure. This method skips the authorization code part of the flow and just returns an access token. The following supposes the application administrator’s app of grant type is implicit:

{
    "id": 1,
    "type": "application",
    "related": {
    ...
    "name": "admin's app",
    "user": 1,
    "client_id": "L0uQQWW8pKX51hoqIRQGsuqmIdPi2AcXZ9EJRGmj",
    "client_secret": "9Wp4dUrUsigI8J15fQYJ3jn0MJHLkAjyw7ikBsABeWTNJbZwy7eB2Xro9ykYuuygerTPQ2gIF2DCTtN3kurkt0Me3AhanEw6peRNvNLs1NNfI4f53mhX8zo5JQX0BKy5",
    "client_type": "confidential",
    "redirect_uris": "http://<tower>/api/",
    "authorization_grant_type": "implicit",
    "skip_authorization": false
}

In the API browser, first make sure you are logged in via session auth, then visit the authorization endpoint with the given parameters:

http://localhost:8013/api/o/authorize/?response_type=token&client_id=L0uQQWW8pKX51hoqIRQGsuqmIdPi2AcXZ9EJRGmj&scope=read

This is case, the client_id must be the same as the client_id field of the underlying application. On success, an authorization page displays with a prompt, asking you to grant or deny the access token. After you click grant, the API browser will POST to the same endpoint with the same parameters in the POST body, on success, a “302 redirect” will be returned:

HTTP/1.1 302 Found
Connection:keep-alive
Content-Language:en
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Tue, 05 Dec 2017 20:36:19 GMT
Location:http://localhost:8013/api/#access_token=0lVJJkolFTwYawHyGkk7NTmSKdzBen&token_type=Bearer&state=&expires_in=315360000000&scope=read
Server:nginx/1.12.2
Strict-Transport-Security:max-age=15768000
Vary:Accept-Language, Cookie

Note

Tokens created with implicit applications do not have a refresh token.

15.3.3. Application using password grant type

This type is also called the resource owner credentials grant. This is ideal for users who have native access to the web app and should be used when the client is the Resource owner. The following supposes an application, ‘Default Application’ with grant type password:

{
    "id": 6,
    "type": "application",
    ...
    "name": "Default Application",
    "user": 1,
    "client_id": "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l",
    "client_secret": "fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false
}

Logging in is not required for password grant type, so you can simply use curl to acquire a personal access token through the /api/o/token/ endpoint:

curl -X POST \
-d "grant_type=password&username=<username>&password=<password>&scope=read" \
-u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569e
IaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
http://<tower>/api/o/token/ -i

In the above POST request, parameters, username and password are the username and password of the related Tower user of the underlying application, and the authentication information is of format <client_id>:<client_secret>, where client_id and client_secret are the corresponding fields of the underlying application.

Note

The special OAuth 2 endpoints only support using the x-www-form-urlencoded Content-type, so as a result, none of the api/o/* endpoints accept application/json.

Upon success, a response displays in JSON format containing the access token, refresh token and other information:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 05 Dec 2017 16:48:09 GMT
Content-Type: application/json
Content-Length: 163
Connection: keep-alive
Content-Language: en
Vary: Accept-Language, Cookie
Pragma: no-cache
Cache-Control: no-store
Strict-Transport-Security: max-age=15768000

{"access_token": "9epHOqHhnXUcgYK8QanOmUQPSgX92g", "token_type": "Bearer", "expires_in": 315360000000, "refresh_token": "jMRX6QvzOTf046KHee3TU5mT3nyXsz", "scope": "read"}

15.4. Application Token Functions

This section describes the refresh and revoke functions associated with tokens. Everything that follows (Refreshing and revoking tokens at the /api/o/ endpoints) can currently only be done with application tokens. However, tokens created with implicit applications do not have a refresh token.

15.4.1. Refresh an existing access token

The following example shows an existing access token with a refresh token provided:

{
    "id": 35,
    "type": "access_token",
    ...
    "user": 1,
    "token": "omMFLk7UKpB36WN2Qma9H3gbwEBSOc",
    "refresh_token": "AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z",
    "application": 6,
    "expires": "2017-12-06T03:46:17.087022Z",
    "scope": "read write"
}

The /api/o/token/ endpoint is used for refreshing the access token:

curl -X POST \
    -d "grant_type=refresh_token&refresh_token=AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z" \
    -u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
    http://<tower>/api/o/token/ -i

In the above POST request, refresh_token is provided by refresh_token field of the access token above that. The authentication information is of format <client_id>:<client_secret>, where client_id and client_secret are the corresponding fields of the underlying related application of the access token.

Note

The special OAuth 2 endpoints only support using the x-www-form-urlencoded Content-type, so as a result, none of the api/o/* endpoints accept application/json.

Upon success, a response displays in JSON format containing the new (refreshed) access token with the same scope information as the previous one:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 05 Dec 2017 17:54:06 GMT
Content-Type: application/json
Content-Length: 169
Connection: keep-alive
Content-Language: en
Vary: Accept-Language, Cookie
Pragma: no-cache
Cache-Control: no-store
Strict-Transport-Security: max-age=15768000

{"access_token": "NDInWxGJI4iZgqpsreujjbvzCfJqgR", "token_type": "Bearer", "expires_in": 315360000000, "refresh_token": "DqOrmz8bx3srlHkZNKmDpqA86bnQkT", "scope": "read write"}

Essentially, the refresh operation replaces the existing token by deleting the original and then immediately creating a new token with the same scope and related application as the original one. Verify that new token is present and the old one is deleted in the /api/v2/tokens/ endpoint.

15.4.2. Revoke an access token

Similarly, you can revoke an access token by using the /api/o/revoke-token/ endpoint.

Revoking an access token by this method is the same as deleting the token resource object, but it allows you to delete a token by providing its token value, and the associated client_id (and client_secret if the application is confidential). For example:

curl -X POST -d "token=rQONsve372fQwuc2pn76k3IHDCYpi7" \
-u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
http://<tower>/api/o/revoke_token/ -i

Note

The special OAuth 2 endpoints only support using the x-www-form-urlencoded Content-type, so as a result, none of the api/o/* endpoints accept application/json.

Note

The Allow External Users to Create Oauth2 Tokens (ALLOW_OAUTH2_FOR_EXTERNAL_USERS in the API) setting is disabled by default. External users refer to users authenticated externally with a service like LDAP, or any of the other SSO services. This setting ensures external users cannot create their own tokens. If you enable then disable it, any tokens created by external users in the meantime will still exist, and are not automatically revoked.

This setting can be configured at the system-level in the Ansible Tower User Interface:

_images/configure-tower-system-oauth2-tokens-toggle.png

Upon success, a response of 200 OK displays. Verify the deletion by checking whether the token is present in the /api/v2/tokens/ endpoint.