# AIRisk API OAuth2 Authentication

## Overview

The **AIRisk API** uses **OAuth 2.0** – the industry-standard protocol for authorization – to allow third-party applications to access its resources securely (see [the OAuth 2.0 spec RFC 6749](https://tools.ietf.org/html/rfc6749) and a high-level OAuth2 overview for more information).

In essence, OAuth2 enables your application (the client) to obtain limited access to a resource owner's account (the user) without sharing the user's credentials. It works by delegating user authentication to the service that hosts the user account (the AIRisk platform) and then authorizing the client to access resources on the user's behalf, as described in this introduction to OAuth 2.0.

AIRisk API implements an Authorization Code Grant flow. This is a two-step process:

1. **Authorization Request**: Your application directs the user’s browser to AIRisk’s Authorization Endpoint with your `client_id`, a `redirect_uri`, optionally a `state` string/UUID, and optionally a `userid` if you are requesting a user-specific token. If requesting a user-specific token, the `userid` must be an existing `id` on some `ApplicationUser` in the AIRisk deployment. Also,
   * `client_id` and `client_secret` must be valid for some `APIUser` in the instances database.
   * `redirect_uri` must match at least one comma-separated value in the same `APIUser`s (as the client id & secret) `redirect_uri` column
     * ![See Here](/files/fXMjcqaNq5bLjrK55wZI)
2. **Authorization Code Redirect**: If AIRisk’s authorization server authorizes the request, then it redirects the client back to the specified `redirect_uri` with an authorization code in the URL (e.g. <https://localhost/callback?code=REMOTELY\\_GENERATED\\_AUTH\\_CODE\\_HERE>). This code is short-lived and cannot be used by itself except to request a token.
3. **Token Request**: Your application then makes a secure POST request to the Token Endpoint, providing the code along with your `client_id`, `client_secret`, `redirect_uri`, and (if used in step 1) the same `userid`. This step happens server-to-server (backend of your app to AIRisk API) and does not involve the user’s browser.
4. **Token Response**: If the request is valid, the AIRisk authorization server responds with a JSON payload containing an access\_token (and possibly other details like expiration). This token can then be used by your app to authenticate future API requests to AIRisk (usually by including it in an HTTP Authorization: Bearer header).
5. (*Optional*) **Error Handling**: If any step fails (e.g., missing parameters, invalid code, or user denies access), an error will be returned instead of an access token. Error responses are yielded as HTTP Reponses with a `status_code` int, and a `reason` string explaining the error. Some endpoints like `/api/oauth2` return a response with json bodies containing fields for further understanding and debugging the error.

> **Note**: The AIRisk OAuth2 flow supports two modes of obtaining tokens:
>
> * **Generic tokens** – obtained by omitting the `userid` parameter. These tokens are not tied to any specific end-user; they represent the application’s own access.
> * **User-specific tokens** – obtained by including a `userid`. These tokens represent an authorization on behalf of a particular user. The following sections detail how to use each endpoint and clarify the differences between these token types.sequenceDiagram

<figure><img src="/files/IAohVdzolFAWQmgXc1Zb" alt=""><figcaption></figcaption></figure>

### Authorization Endpoint (`GET /api/oauth2`)

The Authorization Endpoint is the URL that the user’s browser visits to initiate the OAuth flow. It is used to request user authorization and produce an authorization code.

**Endpoint:** `GET /api/oauth2`

**Purpose:** Initiate an OAuth2 authorization code request (user login & consent).

**Query Parameters**:

* `client_id` (string, <mark style="color:red;">Required</mark>)
  * Your application’s client identifier. This value is provided when you register your app with AIRisk. It tells the authorization server which application is making the request.
* `redirect_uri` (string, <mark style="color:red;">Required</mark>)
  * The URL in your application where users will be sent after authorization. This must match one of the allowed redirect URIs you configured for your client. After the user approves or denies the request, AIRisk will redirect their browser to this URL with additional parameters (code or error).
* `userid` (string, <mark style="color:orange;">Optional</mark>)
  * An identifier for the user on whose behalf the authorization is being requested. Include this if you want a user-specific token. When provided, the authorization process will associate the resulting code with the specified user. If omitted, the authorization is not tied to a specific user and will result in a generic token (suitable for application-level access).
* `state` (string, <mark style="color:orange;">Optional</mark>)
  * A unique id/UUID generated for this request and which must be stored locally. Then, after having made the request and having been redirected to the redirect uri, the stored value must then be compared to the state parameter in the response when redirected (can be retrieved in the same way as retrieving the `code` value). This parameter is used to prevent CSRF attacks and its purpose may be learned about from [this documentation article](https://auth0.com/docs/secure/attack-protection/state-parameters).

#### Example Authorization URL:

To start the flow, redirect the user’s browser to the authorization endpoint. For example, if your client ID is abc123 and your redirect URI is <https://localhost/callback>, you would use:

`https://api.AIRisk.example.com/api/oauth2?client_id=abc123&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback`

If you want a token on behalf of a user (e.g., user ID user\_42), include the `userid` parameter:

`https://api.AIRisk.example.com/api/oauth2?client_id=abc123&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback&state=1&userid=user_42`

When the user visits this URL, if the client id and user id are valid, the user’s browser will be redirected to:

`https://localhost/callback?state=1&code=SplxlOBeZQQYbYS6WxSbIA`

The query parameter `code=SplxlOBeZQQYbYS6WxSbIA` (this is a sample code) is the authorization code that your application will exchange for an access token in the next step. The code is typically a short-lived, single-use string. (If the server denied the request or an error occurred, you might instead receive error parameters in this redirect, or may be redirected to a JSON response with error parameters).

#### Error Responses

* 401 - Unauthorized
  * For example, if passing in an unknown `client_id`, the endpoint returns a response like:

    >

    ```json
        {
        "type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
        "title": "Unauthorized",
        "status": 401,
        "traceId": "00-676e4f60e893d3cf6bb37b9262dda023-7d0ef785dd85a317-00"
        }
    ```
  * With Parameters:
    1. `type`: string url - Link to the RFC
    2. `title`: string - Human-readable name of the error
    3. `status`: int - The Error Code
    4. `traceId`: string - The id of the trace with which to use to check the stack trace later on.
* Note: It’s recommended to also include a "state" parameter (in our case: `code`) in the authorization URL (not shown above) to maintain state between the request and callback (protecting against CSRF attacks). The AIRisk authorization server will return this state value unmodified in the redirect. This is part of OAuth2 best practices, though for brevity the examples here focus on the core parameters.

### Token Endpoint (`POST /api/oauth2/Token`)

The Token Endpoint is used by your application to exchange the authorization code for an actual access token. This is a server-to-server request (the user does not interact with this endpoint directly). Your client must authenticate itself when calling this endpoint, typically by providing its client secret, to ensure the request is secure.

**Endpoint:** `POST /api/oauth2/Token`

**Purpose:** Exchange an authorization code for an access token.

\
Request Parameters (POST body):

* `client_id` (string, <mark style="color:red;">Required</mark>)\
  – Your client identifier, as in the authorization step.
* `client_secret` (string, <mark style="color:red;">Required</mark>)\
  – Your client secret, which is a confidential key provided when you registered your application. This secret proves to the authorization server that the request is coming from your application (and not a malicious attacker who intercepted the code).
* `redirect_uri` (string, <mark style="color:red;">Required</mark>)\
  – The same redirect URI that was used in the authorization request. It must exactly match the URI originally provided. This is used as an additional security check: the authorization server will ensure the code was issued for this specific redirect URI.
* `code` (string, <mark style="color:red;">Required</mark>)\
  – The authorization code received from the Authorization Endpoint redirect. This is the value of the `code` query parameter that was passed to your redirect URI. The code has a short expiration time and can only be used once.
* `userid` (string, <mark style="color:orange;">Optional</mark>)\
  – The user identifier, if one was included in the authorization request. You must include the same `userid` here to obtain a user-specific token. If you omit `userid` here (and the code was associated with a user), the token request will fail. Conversely, if no `userid` was used in the authorization step, you should not include one in this request.

**Request Format**: The token request is an HTTP POST. You can send the parameters as form URL-encoded (`Content-Type application/x-www-form-urlencoded`) or as a JSON body. The examples below demonstrate both approaches using curl and Python.

#### Example Token Request – Generic Token (no `userid`)

1. Using curl (with form-encoded body):

   ```bash
   curl -X POST "https://api.AIRisk.example.com/api/oauth2/Token" \
       -H "Content-Type: application/x-www-form-urlencoded" \
       -d "client_id=YOUR_client_id&client_secret=YOUR_client_secret&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback&code=AUTH_CODE_HERE"
   ```

   In this example:

   * Replace `YOUR_client_id` and `YOUR_client_secret` with your actual credentials.
   * `redirect_uri` is URL-encoded (https%3A%2F%2Flocalhost%2Fcallback corresponds to <https://localhost/callback>).
   * `AUTH_CODE_HERE` should be replaced with the code you received in the previous step (e.g., SplxlOBeZQQYbYS6WxSbIA).
2. Alternatively, using Python and the `requests` library (sending JSON payload):

   ```python
   import requests

   token_url = "https://api.AIRisk.example.com/api/oauth2/Token"
   data = {
       "client_id": "YOUR_client_id",
       "client_secret": "YOUR_client_secret",
       "redirect_uri": "https://localhost/callback",
       "code": "AUTH_CODE_HERE"
   }

   response = requests.post(token_url, json=data)
   token_response = response.json()

   print(token_response.get("access_token"))
   ```

Here we post a JSON body with the required fields. The server will respond with a JSON object (as shown below). We then parse the JSON to extract the access\_token. In a real application, you would store this token and use it to authorize API calls via the HTTP Header for a bearer token.

Example Success Response (JSON):

```json

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", 
  "expiration": "2025-03-31T13:12:17Z"
}
```

* `access_token` – The OAuth2 access token string. This is typically a long opaque string or JWT that you will use to authenticate requests to the AIRisk API.
* `expiration` – The UTC-formatted datetime in which the token expires.

#### Example Token Request – User-Specific Token (with `userid`)

If you included a `userid` in the authorization step, use the same `userid` when exchanging the code. For example, suppose we indicated `userid=user_42` in the authorization URL and got back a code. We would make the token request as follows:

```bash
curl -X POST "https://api.AIRisk.example.com/api/oauth2/Token" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "client_id=YOUR_client_id&client_secret=YOUR_client_secret&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback&code=AUTH_CODE_HERE&userid=user_42"
```

This is identical to the earlier request, except we have appended &`userid=user_42` (using the same user ID that was in the auth request). The response format for a user-specific token is the same JSON structure:

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", 
  "expiration": "2025-03-31T14:10:57Z"
}
```

In this case, `access_token` here is associated with User 42’s account. When using this token to call protected endpoints, the AIRisk API will treat the request as on behalf of that user.

#### Error Responses

If the token request is invalid or the authorization code has expired/been used, the token endpoint will return an error instead of an access token. Error responses use HTTP status codes (like 400 or 401) and an HTTP reason. <br>

1. For example, if a Required parameter (ex. `client_id`) is missing:

   >

   ```http
   HTTP/1.1 400 Bad Request
   Content-Type: text/plain; charset=utf-8
   Date: Thu, 27 Mar 2025 18:58:24 GMT
   Server: Kestrel
   Set-Cookie: ARRAffinity=SOME_VALUE;Path=/;HttpOnly;Secure;Domain=demo.aicrisk.com, ARRAffinitySameSite=SOME_OTHER_VALUE;Path=/;HttpOnly;SameSite=None;Secure;Domain=demo.aicrisk.com
   Transfer-Encoding: chunked
   Strict-Transport-Security: max-age=2592000
   Request-Context: appId=SOME_APP_ID

   Invalid client_id
   ```
2. Or if the provided `code` is not valid (e.g., already used or expired):

   >

   ```http
   HTTP/1.1 400 Bad Request
   Content-Type: text/plain; charset=utf-8
   Date: Thu, 27 Mar 2025 19:17:38 GMT
   Server: Kestrel
   Set-Cookie: ARRAffinity=SOME_VALUE;Path=/;HttpOnly;Secure;Domain=demo.aicrisk.com, ARRAffinitySameSite=SOME_VALUE;Path=/;HttpOnly;SameSite=None;Secure;Domain=demo.aicrisk.com
   Transfer-Encoding: chunked
   Strict-Transport-Security: max-age=2592000
   Request-Context: appId=cid-v1:SOME_APP

   Invalid Code
   ```
3. Or if the provided Client ID isn't valid: \
   Full HTTP Response:&#x20;

   ```http
   HTTP/1.1 401 Unauthorized
   Content-Type: application/problem+json; charset=utf-8
   Date: Thu, 27 Mar 2025 19:09:27 GMT
   Server: Kestrel
   Set-Cookie: ARRAffinity=SOME_VALUE;Path=/;HttpOnly;Secure;Domain=demo.aicrisk.com, ARRAffinitySameSite=SOME_VALUE;Path=/;HttpOnly;SameSite=None;Secure;Domain=demo.aicrisk.com
   Transfer-Encoding: chunked
   Strict-Transport-Security: max-age=2592000
   Request-Context: appId=SOME_APP

   {
       "type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
       "title": "Unauthorized",
       "status": 401,
       "traceId": "00-de61877dd04ebafff7c1676b5b6e9532-5debd224217a8b45-00"
   }
   ```

   > Response Content JSON (ex...)
   >
   > ```python
   > resp = requests.post(OAUTH_TOKEN_ENDPOINT, data=data)
   > resp.json()
   > print(resp)
   > ```

   ```json
   {
       "type":"https://tools.ietf.org/html/rfc9110#section-15.5.2",
       "title":"Unauthorized",
       "status":401,
       "traceId":"00-b7704abbc12b42d34f405553b712bac7-2885ab6a78ee25ea-00"
   }
   ```
4. Or if the client\_secret is missing

   ```http
   HTTP/1.1 400 Bad Request
   Content-Type: text/plain; charset=utf-8
   Date: Thu, 27 Mar 2025 19:03:52 GMT
   Server: Kestrel
   Set-Cookie: ARRAffinity=SOME_VALUE;Path=/;HttpOnly;Secure;Domain=demo.aicrisk.com, ARRAffinitySameSite=SOME_VALUE;Path=/;HttpOnly;SameSite=None;Secure;Domain=demo.aicrisk.com
   Transfer-Encoding: chunked
   Strict-Transport-Security: max-age=2592000
   Request-Context: appId=SOME_APP_ID

   Invalid client_secret
   ```
5. Or if the client\_secret is invalid

   ```http
   HTTP/1.1 401 Unauthorized
   Content-Type: application/problem+json; charset=utf-8
   Date: Thu, 27 Mar 2025 19:22:06 GMT
   Server: Kestrel
   Set-Cookie: ARRAffinity=SOME_VALUE;Path=/;HttpOnly;Secure;Domain=demo.aicrisk.com, ARRAffinitySameSite=SOME_VALUE;Path=/;HttpOnly;SameSite=None;Secure;Domain=demo.aicrisk.com
   Transfer-Encoding: chunked
   Strict-Transport-Security: max-age=2592000
   Request-Context: appId=cid-v1:SOME_APP

   {
       "type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
       "title": "Unauthorized",
       "status": 401,
       "traceId": "00-c6bf787605465c8897a36d5957f00fb5-fd988d1c79cd27ed-00"
   }
   ```

Each error response with JSON content includes an "status\_code" code and a human-readable "reason" (Is only not JSON if entirely not passing in a necessary parameter. Even if invalid, it will return JSON decode-able content).\
The error responses without JSON content only have their status code number (ex. 404 in

```http
HTTP/1.1 401 Unauthorized
...
Invalid client_secret
```

), the status name e.g. "Unauthorized", and the error message e.g "Invalid client\_secret".

Always check for an error response and handle it gracefully in your application (e.g., in your app interacting with an AIRisk instance, prompt the user to re-authenticate if the code is no longer valid).

## Generic vs. User-Specific Tokens

When using the AIRisk API’s OAuth2 flow, you have the flexibility to obtain tokens that are (conceptually) either generic (not tied to any user) or user-specific. The difference comes down to whether you supply the optional `userid` parameter in the flow:

1. **Generic Access Tokens** (*Application-Only*, not attached to particular `ApplicationUser` although retrieved using credentials from valid `APIUser`): If you do not include `userid` in the authorization and token requests, the resulting access\_token represents your application itself, not any particular user. *These tokens are useful for accessing endpoints or performing actions that are not user-specific*.
   * For example, accessing public data or using client-wide privileges. The token is still scoped to what your client application is allowed to do, but no specific user's data is directly implicated. In practical terms, when your app uses this token, the AIRisk API will see the request as coming from your application in a general context, only identifying the client by `client_id` which is tied to an `APIUser` in which may be used via arbitrary `ApplicationUser` instances.
     * In our case, one may access `/api/Users/ListUsers` if using a generic token as the bearer token in the HTTP headers. In this case, one may fetch the list of known user ids and then select one with which to further log in as.
2. **User-Specific Access Tokens** (On-Behalf-of User): If you do include a `userid` in the requests to `/api/oauth2` and `/api/api/oauth2/Token`, the flow will produce a token associated with that user. This typically means the user has explicitly authorized your app to act on their behalf. The token carries the identity or context of the specified user. Use these tokens to access or modify data that the user has access to in the AIRisk API.

### Important distinctions:

* A generic token might grant only limited, generic scope access, whereas a user token can grant permissions to read or write to database tables which they can access.
* You cannot use a generic token to access user-specific endpoints that require a user context. Similarly, a user-specific token only grants access to the scope of their existing access; you would need separate tokens for different users.
  * However, you may use an a user-specific access token to access any endpoints accessible via just a generic token.
* The `userid` parameter should be treated carefully. The user must correspond to an actual `ApplicationUser` in the AIRisk system, and the authorization server will not ensure that the person approving the request is that user, and therefore provides a means of impersonating other users. (In many OAuth systems, you don't manually specify the user – the user logs in themselves. AIRisk’s `userid` parameter might be used in scenarios where the user identity is known ahead of time or to streamline first-party integrations. Always follow AIRisk’s guidelines on how to obtain or use this parameter.)
* In summary, use a generic token when your app needs to perform application-level actions or maintenance tasks that aren't tied to a single user (such as listing available users). Use a user-specific token when your app is performing actions on behalf of a specific user, such as accessing that user's data or acting in their name within the AIRisk platform.

### Example Usage Flow / Reasons

1. **Two-Step Authentication Process**
   * **Description**: You have an app using the remote AIRisk instance in which only admins / trusted persons use.
   * **Use Case / Needs**:
     * See Available Users
     * Select One & Authenticate as them
       * i.e. Impersonation
     * Access endpoints and/or switch user (authenticating) as needed
   * **Methodology**
     * First, use `CLIENT_ID` & `CLIENT_SECRET` from the local environment
     * Invoke the authentication process for a generic token and store that to be used as the bearer token (for now)
     * Have a local endpoint that calls the remote endpoint `/api/ListUsers`, collects the returned JSONdata, and then redirects to a "Select User" page, listing the available users names & ids
       * If having a `USER_ID` in the environment for example, you could have the form default to it
       * Common practice for this in `flask` would be to redirect to an endpoint for select\_user which, when called via a GET request renders the form page (with the form action pointing to the same URL), or when called via a POST request collecting the submitted form data from the same page and then redirecting as needed.
     * When a user is selected, set that as the active user id in the session and **invoke the authentication process again but this time also passing `userid={SELECTED_USER_ID}`** as respective parameter types for the respective Authentication endpoints. Then save the returned `access_token` as the active bearer token to use in the headers of future GET requests.
2. **Single-User App**
   * **Description**: You have an App with a custom interface and which interacts with a remote AIRisk project instance. You will be the only person running this program, and only will do so on your own secure device.
   * **Use Case / Needs**
     * Load `APIUser` related `CLIENT_ID` and `CLIENT_SECRET` from a `.env` file
     * Load `ApplicationUser` related `id` also from a `.env` file in which belongs to your user account
     * Assuming both of the above exist and are valid.
   * **Methodology**
     * Load from `.env`
     * Store the `USER_ID` environment variable as `userid` in the session / local memory for usage across views/pages.
     * Invoke the authentication process but include `userid` as a parameter for both respective endpoints (encoding it in the URL for the GET request to `/api/oauth2`. Passing as a parameter in the POST request to `/api/oauth2/Token`)
     * Use the access token as the bearer token across the app.

## Usage of the Access Token

Regardless of token type, once you have an access\_token, your application will include it in API requests to AIRisk to authenticate. Most commonly, this is done by adding an HTTP header:

`Authorization: Bearer <ACCESS_TOKEN>`

For example, using a placeholder user token (where `/api/GetConversations` is only accessible by users, thus requiring a user token):

```bash
curl -H "Authorization: Bearer AAABBBCCC111222333444..." \
     "https://demo.aicrisk.com/api/GetConversations"
```

or in python:

```python
import requests

ACCESS_TOKEN = "AABBCCDD...access token here"
response = requests.get(
    "https://demo.aicrisk.com/api/GetConversations",
    headers = {
        "Authorization": f"Bearer {ACCESS_TOKEN}", "Accept": "application/json"
    }
)
if response.status_code != 200:
    return f"Data Retrieved Invalid: {response.status_code} - {response.reason}", response.status_code
users = response.json() # data retrieved
```

The AIRisk API will verify the token (and the user context, if any) and then process the request if the token is valid and has the required permissions. If the token is expired or invalid, the API will return an HTTP 401 Unauthorized error (with a `status_code` (401) and `reason`).

## Conclusion

By following the above OAuth2 authorization flow, your application can securely obtain access tokens from AIRisk API. This design ensures that users can grant or revoke access as needed and that your app never needs to handle raw passwords. It also allows AIRisk to scope the issued tokens to specific users or to your application overall, depending on the presence of the `userid` parameter.

* Furthermore, it allows AIRisk admins to define sets of credentials and allowed redirect uris (corresponding to `APIUser` instances), and then share the client id, secret, and the redirect uris as well as set them accordingly. These are all one needs to be able to get a generic token via the authentication process.
* Moreover, the admins also have control over the `ApplicationUser` instances (each with a unique `userid`, corresponding with an actual user of the platform), the agents they can access, particular chat-related settings, as well as their `Company` to which they belong to further scope their permissions.

For more information on [OAuth2](https://oauth.net/2/) and best practices, you may refer to external resources like the [official OAuth 2.0 framework specification RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) for in-depth details, or approachable guides such as [OAuth 2 Simplified](https://aaronparecki.com/oauth-2-simplified/) and [the DigitalOcean tutorial "An Introduction to OAuth 2"](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) for a conceptual overview. Using this information to properly authenticate and acquire tokens, you may now explore the AIRisk API endpoints with authenticated requests, knowing that the OAuth2 standard is handling the authorization securely under the hood. Happy building!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aicrisk.com/api-overview/airisk-api-oauth2-authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
