Last updated Mar 18, 2024

User impersonation for Connect apps

Atlassian Connect supports user impersonation using the JWT Bearer token authorization grant type for OAuth 2.0. This authorization method allows apps with the appropriate scope (ACT_AS_USER) to access resources and perform actions in Jira and Confluence on behalf of users.

Note that the JWT Bearer token authorization grant type for OAuth 2.0 is different from OAuth 2.0 authorization code grants. JWT Bearer token authorization grant type for OAuth 2.0, also known as two-legged OAuth with impersonation (2LOi), can only be used in Connect apps. OAuth 2.0 authorization code grants, also known as three-legged OAuth (3LO), can be used in any apps or integrations.

Flow for user impersonation authorization grants

The flow for accessing a user's resources works as follows:

The connect OAuth impersonation flow

  1. Install hook fires with the oauthClientId and the shared secret.
  2. App creates a JWT assertion with the shared secret and the oauthClientId, and then POSTs it to the authorization server.
  3. Authorization server returns an OAuth 2.0 access token.
  4. App uses the access token to perform actions as a user.

Request an OAuth 2.0 access token

For an app to make requests on a user's behalf, you need an OAuth 2.0 access token. These steps describe how a token is retrieved:

  1. Admin installs the app: This initiates the installation handshake with the oauthClientId and the shared secret in the request body:

    1
    2
    {
      "key": "addon-key",
      "oauthClientId": "your-oauth2-client-id"
    }
    
  2. JWT token exchange: The app creates an assertion, a JWT that is HMAC-SHA256 signed with the shared secret the app received during the installation handshake, and adds these claims in the payload:

    AttributeTypeDescription
    issStringThe issuer of the claim. For example: urn:atlassian:connect:clientid:{oauthClientId}
    subStringThe subject of the token. For example: urn:atlassian:connect:useraccountid:{account ID of the user to run services on behalf of} Note: urn:atlassian:connect:userkey:{userkey of the user to run services on behalf of} has been deprecated.
    tntStringThe instance the app is installed on. For example: https://{your-instance}.atlassian.net. For a Confluence instance, add /wiki to the end.
    audStringThe Atlassian authentication server: https://oauth-2-authorization-server.services.atlassian.com
    iatLongIssue time in seconds since the epoch UTC.
    expLongExpiry time in seconds since the epoch UTC. Must be no later that 120 seconds in the future.
  3. OAuth bearer token generated: The assertion and the payload are POSTed to the authorization server: https://oauth-2-authorization-server.services.atlassian.com/oauth2/token
    Example request:

    1
    2
    POST /oauth2/token HTTP/1.1
    Host: oauth-2-authorization-server.services.atlassian.com
    Accept: application/json
    Content-Length: {length of the request body}
    Content-Type: application/x-www-form-urlencoded
    grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=READ+WRITE&assertion={your-signed-jwt}
    
    • grant_type is the literal url-encoded urn:ietf:params:oauth:grant-type:jwt-bearer.
    • assertion is set to the assertion created in the previous step.
    • scope is space-delimited and capitalized. Tokens are only granted for scopes your app is authorized for.
      If you omit the scope, the request is interpreted as a request for an access token with all the scopes your app has been granted.
  4. Requests on user's behalf: The token endpoint validates the signatures and issues an access token.
    Example response:

    1
    2
    HTTP/1.1 200 OK
    Status: 200 OK
    Content-Type: application/json; charset=utf-8
    ...
    {
        "access_token": "{your access token}",
        "expires_in": {15 minutes expressed as seconds},
        "token_type": "Bearer"
    }
    

Using an access token in a request

Set the Authorization header to Bearer {your access token} and make your request:

Example request:

1
2
GET /rest/api/latest/myself HTTP/1.1
Host: {your-registered-instance}.atlassian.net
Accept: application/json
Authorization: Bearer {your-access-token}

Example response:

1
2
HTTP/1.1 200 OK
Status: 200 OK
Content-Type: application/json; charset=utf-8
...
{
  "key": "{key-of-user-to-run-services-on-behalf-of}",
  "displayName": "{impersonated user's display name}",
  ...
}

The example is from a Jira REST API resource — not every resource has these properties.

Rate limiting

Apps are allowed 5000 access token requests every 5 minutes for each host product the app is installed on. If you exceed the limit, the server returns a 429 status response and a JSON formatted error message.

Usage limits are returned in these headers for every server response:

  • X-RateLimit-Limit - The number of requests allowed for 5 minutes (set to 5000).
  • X-RateLimit-Remaining - The number of requests remaining before you hit the limit.
  • X-RateLimit-Reset - Unix timestamp indicating when the limit will update. When this occurs, the X-RateLimit-Remaining count will reset to 5000 and the X-RateLimit-Reset time will reset to 5 minutes in the future.

Example response:

1
2
HTTP/1.1 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999
X-RateLimit-Reset: 1366037820
...

Token expiration

The OAuth 2.0 access token expiry time is included in the access token response (it is 15 minutes but this may change). Write your code to anticipate the possibility that a granted token might no longer work. Rather than handling a token expiration error, track the expiration time and request a new token before it expires. You should refresh tokens 30-60 seconds before the expiry, to make sure you are not using expired tokens.

Code Examples

Runnable Java and node.js sample code demonstrating the flow is available from this repository.

Atlassian Connect for Express.js support

Atlassian Connect for Express.js provides the .asUserByAccountId method to make requests on users' behalf.

1
2
var httpClient = addon.httpClient(req);
httpClient.asUserByAccountId('accountid-of-user-to-act-as').get('/rest/api/latest/myself', function(err, res, body) {
  ...
});

Atlassian Connect for Spring Boot support

Atlassian Connect for Spring Boot supports making requests on behalf a user in the AtlassianHostRestClients component. See the readme for more information.

1
2
@Autowired
private AtlassianHostRestClients atlassianHostRestClients;

public void doSomething() {
    atlassianHostRestClients.authenticatedAsHostActor().getForObject("/rest/api/example", Void.class);

Rate this page: