At its core, OAuth ("Open Authorization") is a mechanism for applications to access the Asana API on behalf of a user, all without the application having access to the user's username and password. Instead, the application gets a token, which can be used in subsequent API calls through the addition of an Authorization header:
-H "Authorization: Bearer ACCESS_TOKEN"
In the above example,
ACCESS_TOKEN should be replaced by the actual token received during the token exchange.
Most of the time, OAuth is the preferred method of authentication for developers, users, and Asana as a platform. If you are building a custom app, you should consider building a secure OAuth flow to authenticate users of your app.
If you are already using an OAuth library and/or have a deep understanding of OAuth, you may wish to skip ahead to the quick reference below:
- Asana supports the authorization code grant flow
- Applications can be created from the developer console (i.e., "My apps")
- The endpoint for user authorization is
- The endpoint for token exchange is
- The endpoint for revoking a token is
- Once an access token (i.e., bearer token) has been obtained, your application can make API requests on behalf of the user
Open source example
You can access an open source example OAuth server (written in Node.js) here in the devrel-examples repository. Feel free to follow the comments in the
./index.jsfile as you inspect the overall OAuth flow.
Note that this OAuth server should only be used for testing and learning purposes.
In addition to learning about how to use OAuth on the Asana platform (i.e., through this guide), feel free to review the official OAuth 2.0 specification.
This section describes the overall OAuth process (i.e., with the authorization code grant flow, which is the most common).
We recommend using a library (available in your language of choice) to handle the details of OAuth. Along with expediting development time, using a library can help mitigate the risk of security vulnerabilities due to inexperience or oversight.
As a prerequisite, ensure that you have registered an application with Asana. Take note of the application's client ID and the client secret (which should be protected as a password). Then, to begin:
A user arrives at your application and clicks a button that says Authenticate with Asana (or Connect with Asana, etc.)
The user is taken to the user authorization endpoint, which displays a page that asks the user if they would like to grant access to your third-party application
If the user clicks Allow, they are redirected back to your application, bringing along a special
codein the query string
The application makes a request to the token exchange endpoint to exchange that
code, along with the application's client secret, for two tokens:
- An access token (i.e., a bearer token, which lasts an hour)
- A refresh token (which can be used to fetch a new access token when the current one expires)
Using this access token, the application can now make requests against the Asana API for the next hour
Once the access token expires, the application can use the token exchange endpoint again (i.e., without user intervention) to exchange the refresh token for a new access token. This can be repeated for as long as the user has authorized the application
Authorization code grant
For additional details and a diagram of the authorization code grant flow, see Authorization Code Grant in the OAuth 2.0 specification.
You must first register your application with Asana to receive a client ID and client secret. To do so, first visit the developer console and select Create new app, as shown below:
To build a proper OAuth flow, you must supply your new application with two key details:
- App name - The name for your application. Users will see this name when your application requests permission to access their account as well as when they review the list of apps they have authorized.
- Redirect URL - Otherwise known as the callback URL, this is where the user will be redirected upon successful or failed authentication. Native or command line applications should use the special redirect URL
urn:ietf:wg:oauth:2.0:oob. For security reasons, non-native applications must supply a "https" URL (more on this below).
Along with the information above, you can also upload an icon, a description, and other basic information about your application. All of these attributes can also be changed later as well.
Once you have created an app, the OAuth tab in the sidebar will include a client ID (needed to uniquely identify your app to the Asana API) as well as a client secret.
Your client secret is a secret, and it should never be shared with anyone or added into source code that others could gain access to. If you need to reset your client secret:
- Select your app in the developer console
- Navigate to the OAuth tab in the sidebar
- Select Reset next to your client secret
During the user authorization step, the user is prompted by the application to either grant or deny the application to access to their account.
The endpoint for user authorization is
App distribution settings
In order for a user to be able to authorize the OAuth application via this user authorization endpoint, the application must be available in the user's workspace.
If you have not yet configured your application's distribution settings, visit the manage distribution documentation before moving forward.
Here is an example of sending a user to
https://app.asana.com/-/oauth_authorize via a basic link:
>Authenticate with Asana</a
By clicking on Authenticate with Asana in the above example, the application redirects the user to
https://app.asana.com/-/oauth_authorize, passing query parameters along as a standard query string:
|The client ID uniquely identifies the application making the request
|The URI to redirect to on success or error. This must match the redirect URL specified in the application settings
|Must be either
id_token, or the space-delimited combination:
|Encodes state of the app, which will be returned verbatim in the response and can be used to match the response up to a given request
|PKCE The hash method used to generate the challenge. This is typically
|PKCE. The code challenge used for PKCE
|A space-delimited set of one or more scopes to get the user's permission to access. If no scopes are specified, the
default OAuth scope is used
The Asana API supports a small set of OAuth scopes you can request using the
scope parameter during this user authorization step of your authentication flow. Multiple scopes can be requested at once as a space-delimited list of scopes. An exhaustive list of the supported scopes is provided here:
|Provides access to all endpoints documented in our API reference. If no scopes are requested, this scope is assumed by default.
|Provides access to OpenID Connect ID tokens and the OpenID Connect user info endpoint.
|Provides access to the user's email through the OpenID Connect user info endpoint.
|Provides access to the user's name and profile photo through the OpenID Connect user info endpoint.
For more information about the OpenID Connect and the
openidscope, view its documentation.
If either the
redirect_uri do not match, the user will simply see a plain-text error. Otherwise,
all errors will be sent back to the
The user then sees a screen giving them the opportunity to accept or reject the request for authorization. In either case, the user will be redirected back to the
Below is an example URL through which a user is redirected to the
Preventing CSRF attacks
stateparameter is necessary to prevent CSRF attacks. As such, you must check that the
stateis the same in this response as it was in the request. If the
stateparameter is not used, or not tied to the user's session, then attackers can initiate an OAuth flow themselves before tricking a user's browser into completing it. That is, users can unknowingly bind their accounts to an attacker account.
In terms of requirements, the
state parameter must contain an unguessable value tied to the user's session, which can be the hash of something tied to their session when the OAuth flow is first initiated (e.g., their session cookie). This value is then passed back and forth between the client application and the OAuth service as a form of CSRF token for the client application.
For additional security resources, see:
- OAuth 2.0 Security Best Current Practice
- Prevent Attacks and Redirect Users with OAuth 2.0 State Parameters
The token exchange endpoint is used to exchange a code or refresh token for an access token.
The endpoint for token exchange is
When your app receives a code from the authorization endpoint, it can now be exchanged for a proper token. At this point, your app should make a
POST request to
https://app.asana.com/-/oauth_token, passing the parameters as part of a standard form-encoded
POST body (i.e., passing in the data into a request with header 'Content-Type: application/x-www-form-urlencoded')
Below is an example request body in a
POST request to
https://app.asana.com/-/oauth_token and an example
curl --location 'https://app.asana.com/-/oauth_token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=<YOUR_REFRESH_TOKEN>' \
--data-urlencode 'client_id=<YOUR_CLIENT_ID>' \
If you have a
client_secret, this request should be sent from your secure server. The browser should not see your
client_secretshould never be exposed in client-side code).
Details of each parameter are described below:
refresh_token (see below for more details)
|The client ID uniquely identifies the application making the request
|The client secret belonging to the app, found in the Basic information tab of the developer console
|Must match the
redirect_uri specified in the original request
|This is the code you are exchanging for an authorization token
|If the value of
refresh_token, this is the refresh token you are using to be granted a new access token
|This is the string previously used to generate the
In the response to the request above, you will receive a JSON object similar to the example below:
"name": "Greg Sanchez",
"email": "[email protected]"
Details of each property are described below:
|The token to use in future requests against the API
|The number of seconds that the token is valid, typically
3600 (one hour)
|The type of token (in our case,
|If exchanging a
code, this is a long-lived token that can be used to get new access tokens when older ones expire
|An object encoding a few key fields about the logged-in user. Currently, this is the user's
When an access (bearer) token has expired, you'll see the following error when using such a token in an API request:
The bearer token has expired. If you have a refresh token, please use it to request a new bearer token, otherwise allow the user to re-authenticate.
You can get a new access token by having your application make a
POST request back to the token exchange endpoint using a
grant type of
"refresh_token". In the same request you must also pass in your long-lived
refresh_token from the original token exchange request.
Note on token format
Asana API tokens should be treated as opaque. Token formats may change without notice. Validating a token’s format on the client side could result in unexpected breakages. This applies to any Asana API tokens, including: personal access tokens, service account tokens, and both OAuth refresh tokens and access tokens.
An authorization token can be deauthorized or invalidated (i.e., revoked) by making a request to Asana's API.
The endpoint for revoking a token is
Your app should make a
POST request to
https://app.asana.com/-/oauth_revoke, passing the following parameters as part of a standard form-encoded
|The client ID uniquely identifies the application making the request.
|The client secret belonging to the app, found in the details pane of the developer console
|The refresh token that should be deauthorized. Access tokens (i.e., bearer tokens) will be rejected
The body should include a valid refresh token, which will cause the refresh token and any associated bearer tokens to be deauthorized. Bearer tokens are not accepted in the request body since a new bearer token can always be obtained by reusing an authorized refresh token.
A successful response with a
200 status code indicates that the token was deauthorized or not found. An unsuccessful response with a
400 status code will be returned if the request was malformed due to missing any required fields or due to an invalid token (such as a bearer token).
PKCE (proof key for code exchange) proves the app that started the authorization flow is the same app that finishes the flow. You can read more about it here: Protecting Apps with PKCE.
In short, the process is as follows:
- Whenever a user wants to OAuth with Asana, your app server should generate a random string called the
code_verifier. This string should be saved to the user, since every
code_verifiershould be unique per user. This should stay in the app server and only be sent to the token exchange endpoint.
- Your app server will hash the
code_verifierwith SHA256 to get a string called the
code_challenge. Your server will give the browser only the
code_challenge_methodshould be the string "S256" to tell Asana we hashed with SHA256. More specifically,
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))).
- The browser includes
code_challenge_methodwhen redirecting to the user authorization endpoint:
- The app server should include the
code_verifierin its request to the token exchange endpoint.
Asana confirms that hashing the
code_verifier with the
code_challenge_method results in the
code_challenge. This proves to Asana the app that hit the user authorization endpoint is the same app that hit the token exchange endpoint:
As the redirect from the authorization endpoint in either grant procedure contains a code that is secret between Asana's authorization servers and your application, this response should not occur in plaintext over an unencrypted
http connection. Because of this, we enforce the use of
https redirect endpoints for application registrations.
For non-production or personal use, you may wish to check out stunnel, which can act as a proxy to receive an encrypted connection, decrypt it, and forward it on to your application. For development work, you may wish to create a self-signed SSL/TLS certificate for use with your web server; for production work we recommend purchasing a certificate from a certificate authority. You may review a short summary of the steps for each of these processes here.
Your application will need to be configured to accept SSL/TLS connections for your redirect endpoint when users become authenticated. For many apps, this will simply require a configuration update of your application server. Instructions for Apache and Nginx can be found on their respective websites, and most popular application servers will contain documentation on how to proceed.
Updated 3 months ago