# Authenticate an OAuth API key

Authenticating your system with an OAuth API key is our recommended auth method. [Review our authentication overview](/basics/data-exchange-with-redox/authenticating-and-sending-data-via-redox). 

## Prerequisites

- A user must be assigned to an <u>**engineer role**</u> to manage OAuth API keys. [Learn about user roles](/basics/managing-access-for-your-redox-organization/what-are-user-roles).
- _If using your own private/public key pair_, generate your own key pair. 
- _If using your own application for sending the auth request_, make sure the `axios` and `jose` libraries are installed with `npm i axios jose`.

## Overview of the OAuth flow

First, here’s a high-level overview of the authentication flow: 

1. Provide your own or generate a public key with Redox.
2. Send an auth request for an access token from Redox, then store the access token in your system.
3. Initiate any API request with the access token in the header. 

Check out the diagram for a visual version of these steps:

![Diagram of OAuth flow: Your system owns the private key and uses it to send a signed assertion. Redox uses the public key to validate signed assertion then generates an access token. You store the access token and use it to initiate API requests to Redox.](https://images.ctfassets.net/cl3wt5ehhnlv/6ZpexxsEZAo75eiIRQSZZC/6d5151435009baa86426f6586232a660/authentication-flow-oauth.png)

*OAuth flow*

## Step 1: Generate or provide a public key

Before you can authenticate an API request, you must have an API key with a private/public key pair. There are a few options for this step: 

- <u>Option 1</u>: Generate a private/public key pair in the Redox dashboard.
- <u>Option 2</u>: Provide your public key (i.e., JWKS entry) and store your own private key.
- <u>Option 3</u>: Provide a JWKS URL for your public key and store your own private key.

The navigation steps are the same, but there are different steps for each method provided below. 

1. Log in to the dashboard. 
2. From the navigation menu, select the **Developer** page.
3. By default, the <u>Sources</u> tab opens. Click the **Authentication** tab. 
4. All configured OAuth API keys and auth credentials display in a table. Click the **New** button in the top-right corner, then click **OAuth API key** from the drop-down menu. 
Alternatively, you can skip the create steps and open an existing OAuth API key (skip to step 7). 
5. A modal opens. In the <u>Name</u> field, enter the **API key name**. 


> **For multi-region organizations**
>
> The source you use designates which region an API request comes from. As best practice for security, though, we recommend using a unique OAuth API key for sources in each region.
>
> You can designate the region the API key name (e.g., `Production API key — U.S.`).


6. Click the **Add** button. 


![A user creates a new OAuth API key in the Redox dashboard. ](https://images.ctfassets.net/cl3wt5ehhnlv/gnsU4l9Qn3cdiDO0BU0yn/7f56b55e1dc1e9428ba7301959d9b82d/oauth-api-key-create-new.gif)

*Create an OAuth API key*


7. The <u>Details</u> page opens with the following:
   - The <u>Name</u> field contains the API key name you entered previously. Click the **Edit** button to change it.
   - The <u>ID</u> field shows the automatically generated ID for the API key. Copy and store the **ID** (also known as the client ID) to use when sending an auth request. 
8. For a new API key, the <u>Public keys</u> section is already in <u>Edit</u> mode. 
If using an existing API key, click the **Edit** button under the <u>Public keys</u> section. 
9. Either use the JWKS entry or JWKS entry to provide your public key. Follow the steps for your preferred option. 


<details>
<summary>Option 1: Generate keys in the Redox dashboard</summary>

Follow these steps to generate a new key pair (i.e., private and public keys) in the Redox dashboard. Generating gives you a private key that you store and a JWKS entry for the public key that Redox stores.

1. From the <u>Method</u> drop-down, select the **Generate or provide keys** option, then click the **Generate a key pair** tab. 
2. Click the **Generate keys** button. 
3. The <u>Private key</u> and <u>Public key</u> fields populate. Copy and store these values by clicking the **format** (i.e., JSON, PEM) you want, then selecting the **Copy** or **Download** options. 
_This is the only time you_’_ll see the private key in the Redox dashboard, so make sure to copy or download, then store it securely._ 
   1. _For Postman users_: If using Postman to send an auth request, you need the related <u>Postman environment</u> and <u>collection</u>. Click the **Download** button next to each. 
4. Click the **Save** button. 
_The keys aren_’_t stored until you save them._ Note that the newly generated public key automatically populates the JWKS entry under the <u>Provide your own</u> tab.


![A user generates a new public/private key pair in the Redox dashboard.](https://images.ctfassets.net/cl3wt5ehhnlv/1JeLvkPm9AztGN8fnUpEre/f2d94f37ff8211c196ca7e8fabd1d6d2/oauth-api-key-generate-key-pair.gif)

*Generate a key pair*



</details>



<details>
<summary>Option 2: Provide your public key (JWKS entry)</summary>

If you already have a key pair, you can provide the public key to Redox and store your private key. The <u>JSON Web Key Set</u> (<u>JWKS</u>) entry is the actual JSON entry for the public key. 

1. From the <u>Method</u> drop-down, select the **Generate or provide keys** option.
2. Click the **Provide your own** tab. 
3. Paste the **JWKS entry** in the field provided. 
4. Click the **Save** button. _The public key isn't stored until you save._ 


![A user pastes the JWKS entry of their public key in the Redox dashboard.](https://images.ctfassets.net/cl3wt5ehhnlv/2K0f6TGXF4ecYsv1VtFI1I/0425a9b8610b9fde95cd184724ed033b/oauth-api-key-jwks-entry.gif)

*Provide your public key (JWKS entry)*



</details>



<details>
<summary>Option 3: Provide a JWKS URL</summary>

If you already have a key pair, you can provide the public key to Redox and store your private key. The <u>JSON Web Key Set</u> (<u>JWKS</u>) URL publicly exposes the public key you use for generating the signed assertion. The URL can be internal to your servers or somewhere like a GitHub Gist. If you use this URL, Redox fetches the `kid` property in the JWT to find the right public key.  

1. From the <u>Method</u> drop-down, click the **Provide a JWKS URL** option. 
2. Paste the **JWKS URL** in the field provided. 
3. Click the **Save** button. _The public key isn't stored until you save._


![A user pastes the JWKS URL of their public key in the Redox dashboard.](https://images.ctfassets.net/cl3wt5ehhnlv/25NxgY0cvQ82Xvz2JBTEaR/785fb68307dae3ca4cc1960a44eaf4a1/oauth-api-key-jwks-url.gif)

*Provide a JWKS URL*



</details>


10. You can edit the public key at any time by clicking the **Edit** button and repeating the steps of your preferred method. 
11. (Optional) If necessary later, [learn how to rename or delete an OAuth API key](/how-to-use-redox/manage-your-redox-setup/rename-or-delete-an-oauth-api-key).  

## Step 2: Send an auth request

Next, send an auth request to receive an access token from Redox. The auth request steps are the same, but there are two options for completing them: 

- <u>Option 1</u>: Use an application of your choice.
- <u>Option 2</u>: Use our Postman collection (_You must complete the prep steps first_).


<details>
<summary>Postman prep for auth requests</summary>

For Postman, there’s a little bit of setup to do before sending an authentication request. 

1. From the <u>Settings</u> page of the OAuth API key, click the **DevTools (Postman)** tab.
2. The Postman options display. Click the **Download Postman collection** button. 
3. Run the authentication collection in Postman and select the option for how you want to run it: **Postman for Web** or **Postman for Mac**.
4. Postman opens and automatically imports the <u>Redox Platform Authentication</u> collection. You can use this collection for authenticating requests sent via the Redox FHIR API or the Redox Data Model API. 
5. If you haven’t already downloaded the Postman environment when you were generating keys, click the **Download Postman environment** button from the <u>DevTools</u> tab.


> **Note about the private key**
>
> If you download the Postman environment from the <u>Settings</u> tab (when you're generating keys), the value of the private key automatically populates. But if you download the Postman environment from the <u>DevTools</u> tab instead, the private key is empty. Make sure to manually enter the private key in the Postman environment if you download it at this step.


6. In Postman, click the **Environments** tab. 
7. Click the **Import** button. 
8. From the file explorer, select the **Postman environment** that you just downloaded.
9. The Postman environment is added. Select the **environment name** in the left-hand pane or in the Environment drop-down. 
10. If the private key variable is still empty, enter the private key that you generated previously. 


> **JWKS URL**
>
> If you provided your public key with a JWKS URL, you also need to populate the `kid` value. 


11. Proceed with the auth request steps.  

</details>



1. Generate a signed request in your system using your private key. 
2. Copy the relevant code example for the auth request. Check out the <u>header and parameter definitions</u> below these steps for expected values.
   1. <u>Option 1</u>: _With your own application_, the auth request should look like one of these: 


> **Prerequisite: Install axios and joes libraries**
>
> Make sure the `axios` and `jose` libraries are installed with `npm i axios jose`.


F_or JavaScript_, update the `privateKeyPEM`, `clientId`, and `kid` based on the signed request you generated in step 1. 


**Example: Auth request for OAuth (JavaScript) **

```javascript
const jose = require('jose');
const axios = require('axios');
const randomBytes = require('crypto').randomBytes;
const qsStringify = require('querystring').stringify;
const https = require('node:https');

// import your private key as PEM or JWK
const privateKeyPEM = ``;  // INSERT PRIVATE PEM KEY HERE

const clientId = '<INSERT CLIENT ID HERE>';
const iat = Math.floor(new Date().getTime()/1000);  // Current timestamp in seconds (undefined is valid)
const aud = 'https://api.redoxengine.com/v2/auth/token';
const kid = '<INSERT KID HERE>';

async function getSignedAssertion(clientId, privateKeyPEM, kid, aud, iat) {
    const privateKey = await jose.importPKCS8(privateKeyPEM, 'RS384');

    const payload = {};

    const signedAssertion = await new jose.SignJWT(payload)
    .setProtectedHeader({
        alg: 'RS384',
        kid: kid
    })
    .setAudience(aud)
    .setIssuer(clientId)
    .setSubject(clientId)
    .setIssuedAt(iat)
    .setJti(randomBytes(8).toString('hex')) // a random string to prevent replay attacks
    .sign(privateKey);

    return signedAssertion;
}

async function requestJwtAccessTokenAxios(signedAssertion) {
    const requestBody = qsStringify({
      grant_type: 'client_credentials',
      client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
      client_assertion: signedAssertion
    });

    try {
      const result = await axios.post(
        "https://api.redoxengine.com/v2/auth/token", requestBody, {
          headers: {
            'content-type': 'application/x-www-form-urlencoded'
          }
        }
      );

      // return response with keys: access_token, token_type, and expires_in
      return result.data;
    }
    catch(e) {
      return e.response.data;
    }
}

async function requestJwtAccessTokenNoLibrary(signedAssertion) {

    const requestBody = qsStringify({
        grant_type: 'client_credentials',
        client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
        client_assertion: signedAssertion
      });

    const options = {
        method: 'POST',
        headers: { 
            'content-type': 'application/x-www-form-urlencoded'
        }
    };

    return new Promise(function(resolve, reject) {
        try {
            const req = https.request('https://api.redoxengine.com/v2/auth/token', options, (res) => {
                console.log('statusCode:', res.statusCode);
                console.log('headers:', res.headers);
                let data = '';

                res.on('data', (d) => {
                    data += d;
                });
                res.on('end', (d) =>{
                    const parsed = JSON.parse(data);
                    resolve(parsed);
                });
                res.on('error', (error) => {
                    throw error;
                })
            });

            req.write(requestBody);
            req.end();
        }
        catch(e) {
            reject(e);
        }

    });
}

(async() => {
    const signedAssertion = await getSignedAssertion(clientId, privateKeyPEM, kid, aud, iat);
    const accessTokenAxios = await requestJwtAccessTokenAxios(signedAssertion);
    const accessTokenNoLibrary = await requestJwtAccessTokenNoLibrary(signedAssertion);
    console.log({accessTokenAxios});
    console.log({accessTokenNoLibrary});
})();
```


      _For Python_, update the `clientId`, and `kid` based on the signed request you generated in step 1, then save your private key in a file named `private_key.pem` in the same directory as your script.


**Example: Auth request for OAuth (python)**

```python
import datetime
import jwt
import requests
from uuid import uuid4

with open('private_key.pem', 'rb') as f:
  private_key = f.read()

class BackendServiceAuth(requests.auth.AuthBase):

  def __init__(self, auth_location, client_id, kid, private_key):
    self.auth_location = auth_location
    self.client_id = client_id
    self.kid = kid
    self.private_key = private_key
    self.token = None

  def __call__(self, request):
    if not self.token:
      expiration = datetime.datetime.now() + datetime.timedelta(minutes=5)
      # Add 'kid' property to the header
      headers = {
        'kid': self.kid,
        'alg': 'RS384',
        'typ': 'JWT',
      }

      assertion = jwt.encode(
        {
          'iss': self.client_id,
          'sub': self.client_id,
          'aud': self.auth_location,
          'exp': int(expiration.strftime('%s')),
          'iat': int(datetime.datetime.now().strftime('%s')),
          'jti': uuid4().hex,
        },
        self.private_key,
        algorithm='RS384',
        headers=headers)

      print(f"assertion: {assertion}")

      payload = {
        'grant_type': 'client_credentials',
        'client_assertion_type':
        'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
        'client_assertion': assertion
      }
      try:
        response = requests.post(self.auth_location, data=payload, timeout=30)
        response.raise_for_status()
        self.token = response.json()['access_token']
        print(f"success! {response.json()}")
      except requests.exceptions.RequestException as e:
        if hasattr(e, 'response') and e.response is not None:
          print(f"Request failed with status code: {e.response.status_code}")
          print(f"Response body: {e.response.text}")
        else:
          print(f"Request failed: {e}")

    request.headers['Authorization'] = 'Bearer %s' % self.token
    return request

def main():
  client_id = '<your client id>'
  kid = '<the key id (kid) for your private key>'
  auth_location = 'https://api.redoxengine.com/v2/auth/token'
  auth = BackendServiceAuth(auth_location, client_id, kid, private_key)

  # Now you can use 'auth' as the authentication mechanism for your requests.
  response = requests.post(
    'https://api.redoxengine.com/fhir/R4/redox-fhir-sandbox/Development/Patient/_search',
    auth=auth)
  print(response.json())  # Example usage of the token to fetch patient data.

if __name__ == "__main__":
  main()
```


   2. <u>Option 2</u>: _With Postman_, the authentication request should look like this:


**Example:  Auth request for OAuth (cURL)**

```bash
curl --location --request POST 'https://api.redoxengine.com/v2/auth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion={{signed_assertion}}'
```


3. Send the populated auth request with the signed assertion to `https://api.redoxengine.com/v2/auth/token` via `HTTP POST` from your system. 
4. Redox validates the signature in the request using your public key, then sends back a response with the access token. 
5. Store the <u>access token</u> in your system.  


> **Expiration for access token**
>
> <u>Access tokens</u> expire after 5 minutes. If an API requests fails, check that your access token hasn't expired. 
>
> We don't support <u>refresh tokens</u>. Choose a strategy for access tokens based on your expected traffic volume: 
>
> - _For low traffic_, request an access token each time before making an API request. 
> - _For high traffic_, get as many access tokens as you need, then use different access tokens for multiple API requests. 



### JWT header and body definitions

Review the definitions and expected values for the JWT head and body. Or, [view an example of a valid JWT](https://jwt.io/#debugger-io?token=eyJhbGciOiJSUzM4NCIsImtpZCI6InJlZG94LWRvY3VtZW50YXRpb24iLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJsaXB0YS1sZWUtODczMiIsInN1YiI6ImxpcHRhLWxlZS04NzMyIiwiYXVkIjoiaHR0cHM6Ly9hcGkucmVkb3hlbmdpbmUuY29tL3YyL2F1dGgvdG9rZW4iLCJpYXQiOjE2MDIyNjk3NzMsImV4cCI6MTYwMjI3MDA3MywianRpIjoicmVkb3gxNjAyMjY5NzczNjUwIiwic2NvcGVzIjpbXX0.menhDYTyfxS8J2Ux7x8XHGJCnIHjcWv2yBKWGeKbmhdfoDtLnYUFNUBs57wZqHT3Zgcy8g5kMz14eQ7Xxxb7wnuOSjAqBgc5ns1f6xHHWJ97CtPwljrD1Oysh1oEIwNFbI72XffxpLUYW_e3WypZPPPw-AriWy36-6DYmot96TE9bMSZjheR0NAxlsq-ZgOYvY889kzu_fsLhobQsZNyStgSvGRLtMkv3yhHB8BrzPQGC_a6J7XKYl3OxJy0lrXMjhCWRI0Z920MOGzBjwSYUVFTj6kh9HSfDENTLHSaedKCdGoAbIvOpXYSmZv7pTq5XuFUMBu-tYw7iW3wbdsVPg&publicKey=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoFXZ6SWwLEwL%2BXye3yf8%0AvJMQLEj97rQuxM6O5BZg4kc%2FO59EuiI4sQEQqWiaArar80lFikAoK3U1WidudRdB%0AsAP0Ew%2BwQPzWQ0DGCiHZuyuf6Qevh8Jn1gZc9qKAdLLqKK%2BCelLXKbahTQDyM26N%0AaPd10%2BgoYUTIT786zUv5NZvohFY4ikFGKKfqrniW4hgjH%2BkVlS%2FR%2FGbHzve1g9TM%0ADsqA7h40Zm9jJ98RBMGZXikdR%2Fll0zmOi9R8qMEOoeXWzaLMgKVwGFmeHrelrMV2%0AuXTtC2Mqjsn14IOhLsgUZvGMSIIhO6zDcpdR%2BZ7tiZwjBSVi74pqv%2Fayv8EXZA7s%0AAQIDAQAB%0A-----END%20PUBLIC%20KEY-----) with all of these values populated.

For the JWT header, you should use `content-type application/x-www-form-urlencoded` and these values: 

| **Parameter** | **Required** | **Description** |
| --- | --- | --- |
| `alg` | Y | The <u>**JSON Web Alorgithm**</u> (<u>**JWA**</u>) used for signing the authentication JWT.  We currently support `RS384`, `RS512`, `ES384`, and `ES512`. |
| `kid` | Y | The identifier of the key-pair used to sign this JWT. This identifier tells us which public key to use to verify the JWT. |
| `typ` | Y | The type of token request. Populate this with `JWT`. |
| `jku` | N | This field may be populated if you provided a JWKS URL in the Redox dashboard.  If you provide it here, the URL should match what’s saved in the dashboard. Populate this with the TLS-protected JWKS URL, which contains the public key(s) that are accessible without authentication or authorization.  If this field isn’t present, Redox reverts to what’s saved in the dashboard. |

For the JWT body, use all these required parameters: 

| **Parameter** | **Required** | **Description** |
| --- | --- | --- |
| `iss` | Y | The issuer of the JWT. Populate this with the `client_id` of the key you created in the dashboard (in the <u>Settings</u> page of the OAuth API key). The `iss` and `sub` contain the same values. |
| `sub` | Y | Populate this with the `client_id` of the key you created in the dashboard (in the <u>Settings</u> page of the OAuth API key). The `iss` and `sub` contain the same values. |
| `aud` | Y | The audience of the JWT. Populate this with `https://api.redoxengine.com/v2/auth/token`. |
| `iat` | Y | The UTC timestamp for when the JWT was created (e.g., `1970-01-01T00:00:00Z UTC`). This time _must not_ be greater than 5 minutes before `exp`. |
| `exp` | Y | The expiration UTC timestamp for the JWT (e.g., `1970-01-01T00:00:00Z UTC`). This time _must not_ be greater than 5 minutes in the future. |
| `jti` | Y | A nonce string value that uniquely identifies the JWT. Redox denies requests if you attempt to reuse the `jti` within the lifetime of the JWT. |

### HTTP request body definitions

For the HTTP request body, use all these required parameters: 

| **Parameter** | **Required** | **Description** |
| --- | --- | --- |
| `grant_type` | Y | Populate this with `client_credentials`. |
| `client_assertion_type` | Y | _With your own application_, populate this with `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. _With Postman_, this value automatically populates when you download the Postman collection. |
| `client_assertion` | Y | _With your own application_, populate this with the signed assertion you generated with your private key.  _With Postman_, this value automatically populates. |

## Step 3: Initiate API requests

Now you’re ready to initiate API requests with the access token in the Authorization HTTP header using the Bearer authentication scheme like this: 

**Example: Initiating requests**

```bash
curl \
-X POST https://api.redoxengine.com/endpoint \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d '{}'
```

## Related resources

Learn how to:

- [Rename or delete an OAuth API key](/how-to-use-redox/manage-your-redox-setup/rename-or-delete-an-oauth-api-key)
- [Troubleshoot OAuth API key errors](/troubleshooting/troubleshoot-oauth-api-key-errors)

> **Platform API endpoints**
>
> This article describes how to manage OAuth API keys in the <u>**Redox dashboard**</u>. To manage OAuth API keys via <u>API</u>, use these <u>**Redox Platform API**</u> endpoints instead:
>
> - [Create OAuth API key](https://docs.redoxengine.com/permalink/343fc208-32a5-542c-a1ca-c68f77964172-create-oauth-key)
> - [Update OAuth API key](https://docs.redoxengine.com/permalink/396c9c0b-42fc-5567-b241-623895463d97-update-oauth-key)
> - [Delete OAuth API key](https://docs.redoxengine.com/permalink/0c061eba-dea9-5cf5-98f1-0d927a2d2736-delete-oauth-key)
