Authentication and URL Locations

Authentication is done via OAuth 2.0 client credentials. Provisioned app credentials are used to authenticate and retrieve an access token from the environment-specific OAuth token endpoint. This access token is then provided on each API request as a bearer token. This token is valid for one hour and should be cached to avoid rate limit issues against the token endpoint.

IMPORTANT: There is a 50 per minute rate limit in production and a 5 per minute rate limit in preview on new token requests. If you exceed this limit, your application will be blocked (429 response) from requesting new tokens for the remainder of the minute. .

To authenticate against the token endpoint, we support both secret-based authentication as well as client assertion JWT-based authentication. Secret-based authentication is the simplest method, involving providing your client ID and secret in a basic authorization header when making a request to the token endpoint. JWT-based authentication is more involved, but also more secure. A JWT is generated and signed with a public/private keypair by the caller and presented to the token endpoint. The call is authenticated if the JWT's signature can be verified using one of the app's public keys. These methods are explained in more detail below.

Note: Client credentials created or issued through the Developer Portal Console currently support only 2-legged OAuth for service-to-service API calls. 3-legged OAuth credentials are available by request, but are currently limited to patient-facing applications and read-only access of 2015 CEHRT APIs. To request 3-legged OAuth credentials for a patient-facing application, you must complete our CEHRT API request form and check the box indicating 3-legged OAuth, then submit your completed form to marketplace@athenahealth.com. Please refer back to this page for updates on support for 3-legged OAuth credentials for non-patient applications and non-CEHRT APIs.

Environment URLs

Apps are provisioned against a specific environment and may only be used in that environment. The below URLs are used for all access to authentication endpoints and API resources, regardless of token authentication method.

athena Environment

Site (API path)

OAuth Endpoint

Preview

https://api.preview.platform.athenahealth.com/v1/{practiceid} 

https://api.preview.platform.athenahealth.com/oauth2/v1/token

Production

https://api.platform.athenahealth.com/v1/{practiceid}

https://api.platform.athenahealth.com/oauth2/v1/token

Secret-Based Authentication

When using secret-based authentication, your app will have a client secret associated with its client ID. This secret will be passed in a basic authorization header on every request to the token endpoint.

Please remember that as a security measure, you should never share your client ID or secret. If this happens, we will need to create another set of credentials associated with your account.

Token Generation

To request a token using secret-based authentication, make a request with the following parameters utilizing basic authentication to include your client ID and client secret in a basic authorization header.

curl https://api.preview.platform.athenahealth.com/oauth2/v1/token \ 
-X POST \ 
-u "$CLIENT_ID:$CLIENT_SECRET" \ 
-d "grant_type=client_credentials" \ 
-d "scope=athena/service/Athenanet.MDP.*"

Troubleshooting

There is a common error that involves POSTing the username and password. This does not work. It must use basic authentication (essentially, "$key:$secret" Base64 encoded).

Client Assertion JWT-Based Authentication

Client-assertion JWT-based authentication uses a public/private keypair to authenticate the caller to the token endpoint. Using this approach, your application will have no client secret. You must associate at least one public key with your application and can have up to five public keys associated. You can never have fewer than one key associated with your app.

JWT-based authentication is more involved for the end-user, requiring you to generate keypairs and sign JWT (JSON Web Token) assertions to retrieve a token. The benefit of using this method is that it is more secure. The private portion of your keypair is never out of your hands. This method also enables zero-downtime rotation of credentials. You can have up to five public keys associated with your app enabling you to rotate by: introducing a new keypair, transitioning your code to use this new keypair, and then deleting the old keypair once you've transitioned. Using the secret-based authentication method as soon as the secret is rotated your previous secret is invalid.

Requesting a Token

Requesting a token using the client assertion JWT-method requires that you sign a JWT assertion using the private portion of a keypair associated with your app. This assertion is then provided to the token endpoint for authentication. The assertion JWT must contain the following claims:

Required Assertion Claims

Claim

Description

Type

aud

Required. The URL of the backend authorization resource. This varies by environment, listed below.

string

exp

Required. An expiration time for this token in seconds since the epoch. Must be less than an hour into the future. The assertion token will no longer be valid after this time expires.

number

iss

Required. Set to the client ID of the application for which you're requesting a token.

string

sub

Required. Set to the client ID of the application for which you're requesting a token.

string

Environment-Specific aud Claim URLs

Environment

aud Claim URL

Preview

https://athena.okta.com/oauth2/aus2hfei6ookPyyCA297/v1/token

Production

https://athena.okta.com/oauth2/aus2hff5eqFb7Wqfh297/v1/token

Constructing the Client Assertion JWT

Generate a JWT by including the required claims as indicated above. An example is provided below in javascript using the njwt library. Similar libraries exist in most languages. A collection of libraries is listed on the jwt.io site. Take care when considering the use of a third-party package.

const njwt = require("njwt");  

const fs = require("fs");  

  

// Some packages require the key in pem format, you can use a package such as  

// pem-jwk to convert.  

const privateKey = fs.readFileSync("private.pem");  

const clientId = "<Your Client ID>";  

const now = Math.floor(new Date().getTime() / 1000);  

const expire = new Date((now + 300) * 1000);  

  

// Set the environment-appropriate audience claim to the appropriate URL.  

const claims = {  

  aud: "<Environment-Appropriate URL>",  

};  

  

const jwt = njwt  

  // Provide the algorithm you configured your key with.  

  .create(claims, privateKey, "RS256")  

  .setHeader("kid", "<The kid of the key used in signing>")  

  .setIssuedAt(now)  

  .setExpiration(expire)  

  .setIssuer(clientId)  

  .setSubject(clientId)  

  .compact();  

Requesting the Token

To request a token with the JWT you've generated, include the JWT along with the other parameters as demonstrated below:

# Construct the client assertion JWT using a script like the above.  

JWT=$(node generate-jwt.js)  

   

# Include the JWT in a token request with the appropriate parameters for the 

# environment. 

curl -X POST "https://api.preview.platform.athenahealth.com/oauth2/v1/token" \  

  -H "Accept: application/json" \  

  -H "Content-Type: application/x-www-form-urlencoded" \  

  -d "grant_type=client_credentials" \  

  -d "scope=athena/service/Athenanet.MDP.*" \  

  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \  

  -d "client_assertion=$JWT"    

Token Endpoint Response Format

The token endpoint returns a JSON object with the following format:

{ 

  "access_token": "bSQeVaRd47Tnof8GWbDZTud9ghLP", 

  "expires_in": "300" 

} 

Breakdown of JSON Response:

  • access_token - to be used as a bearer token in API requests
  • expires_in - represents the lifetime of the token in seconds
    Note: The token lifespan is 60 minutes, which is the same as it was on the old platform. 

The access token in the response should be included in subsequent requests to API endpoints.

 curl “https://api.preview.platform.athenahealth.com/v1/195900/ping” \ 
  -H”Authorization: Bearer {access_token}” 

Tracking Daily Quotas

There are response headers returned upon each request for your convenience.  The headers may represent different meanings depending on the request context.  Below is a table detailing the differences. 

Request Context 

Header 

Description 

OAuth Request 

X-RateLimit-Limit 

The limit of OAuth requests the app can make within a period of one minute. 

OAuth Request 

X-RateLimit-Remaining 

The number of remaining counts the OAuth API can be used within the minute before being rate-limited.  This count resets at the top of every minute. 

API Request 

X-RateLimit-Limit 

The limit of API requests you can make in a period of one day. 

API Request 

X-RateLimit-Remaining 

The number of remaining counts the normal API can be used before being capped.  This count resets at midnight GMT. 

API Request 

X-Request-Id 

An identifier associated with this request. 

Best Practices

The best practice for a production deployment is to maintain the key and secret in a centralized place (a key server). The key server is responsible for calling the OAuth endpoint and getting and caching (until expiration) the access token. It should then make that token available for any callers in your environment who need to make an API call. You can store the key and secret in one place and can maintain a higher level of security there (for example, a smaller number of people able to log into that particular server).

For security reasons, we urge you not to check the key and secret into your codebase.

Secrets and JWKs are listed under Credentials tab of Apps listed in your Account Dashboard.

Typically, if you see "Developer Inactive" and a 401 response, it is because you are not passing the bearer token in the Authorization header with your call to an endpoint.

Was this information helpful? Yes | No What went wrong? Incomplete or incorrect information | Irrelevant Content | Others

On this Page