18 KiB
Introduction
Starting from Tower 3.3, OAuth2 will be used as the new means of token-based authentication. Users will be able to manage OAuth2 tokens as well as applications, a server-side representation of API clients used to generate tokens. With OAuth2, a user can authenticate by passing a token as part of the HTTP authentication header. The token can be scoped to have more restrictive permissions on top of the base RBAC permissions of the user. Refer to RFC 6749 for more details of OAuth2 specification.
Basic Usage
To get started using OAuth2 tokens for accessing the browsable API using OAuth2, this document will walk through the steps of acquiring a token and using it.
- Make an application with
authorization_grant_type
set to 'password'. HTTP POST the following to the/api/v2/applications/
endpoint (supplying your ownorganization-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>
}
- Make a token with a POST to the
/api/v2/tokens/
endpoint:
{
"description": "My Access Token",
"application": <application-id>,
"scope": "write"
}
This will return a <token-value>
that you can use to authenticate with for future requests (this will not be shown again)
- Use token to access a resource. We will use
curl
to demonstrate this:
curl -H "Authorization: Bearer <token-value>" -X GET https://<awx>/api/v2/users/
The
-k
flag may be needed if you have not set up a CA yet and are using SSL.
This token can be revoked by making a DELETE on the detail page for that token. All you need is that token's id. For example:
curl -ku <user>:<password> -X DELETE https://<awx>/api/v2/tokens/<pk>/
Similarly, using a token:
curl -H "Authorization: Bearer <token-value>" -X DELETE https://<awx>/api/v2/tokens/<pk>/ -k
More Information
Managing OAuth2 Applications and Tokens
Applications and tokens can be managed as a top-level resource at /api/v2/applications
and
/api/v2/tokens
. These resources can also be accessed respective to the user at
/api/v2/users/N/<resource>
. Applications can be created by making a POST to either api/v2/applications
or /api/v2/users/N/applications
.
Each OAuth2 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 will be accessible via their primary keys:
/api/v2/applications/<pk>/
. Here is a typical application:
{
"id": 1,
"type": "o_auth2_application",
"url": "/api/v2/applications/1/",
"related": {
"user": "/api/v2/users/1/",
"tokens": "/api/v2/applications/1/tokens/",
"activity_stream": "/api/v2/applications/1/activity_stream/"
},
"summary_fields": {
"user": {
"id": 1,
"username": "root",
"first_name": "",
"last_name": ""
},
"tokens": {
"count": 1,
"results": [
{
"scope": "read",
"token": "*************",
"id": 2
}
]
}
},
"created": "2018-02-20T23:06:43.215315Z",
"modified": "2018-02-20T23:06:43.215375Z",
"name": "Default application for root",
"user": 1,
"client_id": "BIyE720WAjr14nNxGXrBbsRsG0FkjgeL8cxNmIWP",
"client_secret": "OdO6TMNAYxUVv4HLitLOnRdAvtClEV8l99zlb8EJEZjlzVNaVVlWiKXicznLDeANwu5qRgeQRvD3AnuisQGCPXXRCx79W1ARQ5cSmc9mrU1JbqW7nX3IZYhLIFgsDH8u",
"client_type": "confidential",
"redirect_uris": "",
"authorization_grant_type": "password",
"skip_authorization": false
},
In the above example, user
is the primary key of the user associated to this application and name
is
a human-readable identifier for the application. The other fields, like client_id
and
redirect_uris
, are mainly used for OAuth2 authorization, which will be covered later in the 'Using
OAuth2 Token System' section.
Fields client_id
and client_secret
are immutable identifiers of applications, and will be
generated during creation; Fields user
and authorization_grant_type
, on the other hand, are
immutable on update, meaning they are required fields on creation, but will become read-only after
that.
On RBAC side:
- System admins will be able to see and manipulate all applications in the system;
- Organization admins will be able to see and manipulate all applications belonging to Organization members;
- Other normal users will only be able to see, 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. Tokens can be created by POSTing to /api/v2/tokens/
endpoint by providing application
and scope
fields to point to related application and specify
token scope; or 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 at
/api/v2/tokens/<pk>/
. Here is 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 OAuth2 token, the only fully mutable fields are scope
and description
. The application
field is immutable on update, and all other fields are totally immutable, and will be auto-populated
during creation.
user
- this field corresponds to the user the token is created forexpires
will be generated according to Tower configuration settingOAUTH2_PROVIDER
token
andrefresh_token
will be auto-generated to be non-clashing random strings.
Both application tokens and personal access tokens will be shown at the /api/v2/tokens/
endpoint. Personal access tokens can be identified by the application
field being null
.
On RBAC side:
- A user will be able to create a token if they are able to see the related application;
- The System Administrator is able to see and manipulate every token in the system;
- Organization admins will be able to see and manipulate all tokens belonging to Organization members; System Auditors can see all tokens and applications
- Other normal users will only be able to see and manipulate their own tokens.
Note: Users can only see the token or refresh-token value at the time of creation ONLY.
Using OAuth2 Token System for Personal Access Tokens (PAT)
The most common usage of OAuth2 is authenticating users. The token
field of a token is used
as part of the HTTP authentication header, in the format Authorization: Bearer <token field value>
. This Bearer
token can be obtained by doing a curl to the /api/o/token/
endpoint. For example:
curl -ku <user>:<password> -H "Content-Type: application/json" -X POST \
-d '{"description":"Tower CLI", "application":null, "scope":"write"}' \
https://<awx>/api/v2/users/1/personal_tokens/ | python -m json.tool
Here is an example of using that PAT to access an API endpoint using curl
:
curl -H "Authorization: Bearer kqHqxfpHGRRBXLNCOXxT5Zt3tpJogn" http://<awx>/api/v2/credentials/
According to OAuth2 specification, users should be able to acquire, revoke and refresh an access token. In AWX the equivalent, and easiest, way of doing that is creating a token, deleting a token, and deleting a token quickly followed by creating a new one.
The specification also provides standard ways of doing this. RFC 6749 elaborates
on those topics, but in summary, an OAuth2 token is officially acquired via authorization using
authorization information provided by applications (special application fields mentioned above).
There are dedicated endpoints for authorization and acquiring tokens. The token
endpoint
is also responsible for token refresh, and token revoke can be done by the dedicated token revoke endpoint.
In AWX, our OAuth2 system is built on top of
Django Oauth Toolkit, which provides full
support on standard authorization, token revoke and refresh. AWX implements them and puts related
endpoints under /api/o/
endpoint. Detailed examples on the most typical usage of those endpoints
are available as description text of /api/o/
. See below for information on Application Access Token usage.
Note: The
/api/o/
endpoints can only be used for application tokens, and are not valid for personal access tokens.
Token Scope Mask Over RBAC System
The scope of an OAuth2 token is a space-separated string composed of keywords like 'read' and 'write'. These keywords are configurable and used to specify permission level of the authenticated API client. For the initial OAuth2 implementation, we use the most simple scope configuration, where the only valid scope keywords are 'read' and 'write'.
Read and write scopes provide a mask layer over the RBAC permission system of AWX. In specific, a 'write' scope gives the authenticated user the full permissions the RBAC system provides, while 'read' scope gives the authenticated user only read permissions the RBAC system provides.
For example, if a user has admin permission to a job template, he/she can both see and modify, launch and delete the job template if authenticated via session or basic auth. On the other hand, if the user is authenticated using OAuth2 token, and the related token scope is 'read', the user can only see but not manipulate or launch the job template, despite being an admin. If the token scope is 'write' or 'read write', she can take full advantage of the job template as its admin. Note that 'write' implies 'read' as well.
Application Functions
This page lists OAuth2 utility endpoints used for authorization, token refresh and revoke.
Note endpoints other than /api/o/authorize/
are not meant to be used in browsers and do not
support HTTP GET. The endpoints here strictly follow
RFC specs for OAuth2, so please use that for detailed
reference. Below are some examples to demonstrate the typical usage of these endpoints in
AWX context (note that the AWX net location defaults to http://localhost:8013
in these examples).
Application Using authorization code
Grant Type
This 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,
make a POST to the /api/v2/applications/
endpoint:
{
"name": "AuthCodeApp",
"user": 1,
"client_type": "confidential",
"redirect_uris": "http://<awx>/api/v2",
"authorization_grant_type": "authorization-code",
"skip_authorization": false
}
You can test the authorization flow out with this new application by copying the client_id
and URI link into the
homepage here and click submit. This is just a simple test
application Django-oauth-toolkit
provides.
From the client app, the user makes a GET to the Authorize endpoint with the response_type
,
client_id
, redirect_uris
, and scope
. AWX will respond with the authorization code
and state
to the redirect_uri
specified in the application. The client application will then make a POST to the
api/o/token/
endpoint on AWX with the code
, client_id
, client_secret
, grant_type
, and redirect_uri
.
AWX will respond with the access_token
, token_type
, refresh_token
, and expires_in
. For more
information on testing this flow, refer to django-oauth-toolkit.
Application Using password
Grant Type
This is also called the resource owner credentials grant
. This is for use by users who have
native access to the web app. This should be used when the client is the Resource owner. Suppose
we have 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
}
Login is not required for password
grant type, so we can simply use curl
to acquire a personal access token
via /api/o/token/
:
curl -X POST \
-d "grant_type=password&username=<username>&password=<password>&scope=read" \
-u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569e
IaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
http://<awx>/api/o/token/ -i
In the above POST request, parameters username
and password
are the username and password of the related
AWX 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
underlying application.
Upon success, the access token, refresh token and other information are given in the response body in JSON format:
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"}
Token Functions
Refresh an Existing Access Token
Suppose we have an existing access token with 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://<awx>/api/o/token/ -i
In the above POST request, refresh_token
is provided by refresh_token
field of the access token
above. The authentication information is of format <client_id>:<client_secret>
, where client_id
and client_secret
are the corresponding fields of underlying related application of the access token.
Upon success, the new (refreshed) access token with the same scope information as the previous one is given in the response body in JSON format:
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"}
Internally, the refresh operation deletes the existing token and a new token is created immediately
after, with information like scope and related application identical to the original one. We can
verify by checking the new token is present and the old token is deleted at the /api/v2/tokens/
endpoint.
Revoke an Access Token
Alternatively Revoke 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://<awx>/api/o/revoke_token/ -i
200 OK
means a successful delete.
We can verify the effect by checking if the token is no longer present
at /api/v2/tokens/
.
Acceptance Criteria
- All CRUD operations for OAuth2 applications and tokens should function as described.
- RBAC rules applied to OAuth2 applications and tokens should behave as described.
- A default application should be auto-created for each new user.
- Incoming requests using unexpired OAuth2 token correctly in authentication header should be able to successfully authenticate themselves.
- Token scope mask over RBAC should work as described.
- Tower configuration setting
OAUTH2_PROVIDER
should be configurable and function as described. /api/o/
endpoint should work as expected. In specific, all examples given in the description help text should be working (a user following the steps should get expected result).