# OAuth2

### Introduction <a href="#introduction" id="introduction"></a>

Before diving into configuration details, it is useful to first understand at a [high level how OAuth2 works](https://youtu.be/CPbvxxslDTU).\
This helps with troubleshooting and understanding the process flow.

### Redirect URI <a href="#redirect-uri" id="redirect-uri"></a>

The redirect URI is always `https://api.locoia.com/v1/oauth2/callback/connectorname`

* For one-word Connectors, use the lowercase connector name
* For multi-word Connectors, replace spaces with `%20`

Examples:

* **HubSpot:**

  `https://api.locoia.com/v1/oauth2/callback/hubspot`
* **Teamwork CRM:**

  `https://api.locoia.com/v1/oauth2/callback/teamwork%20crm`

### OAuth2 Header <a href="#header" id="header"></a>

The OAuth2 access token is typically sent in the header using this format:

```json
{
  "encode": false,
  "token_in_header": true,
  "content_type": "application/json",
  "header_key": "Authorization",
  "token_format": "{{token}}",
  "token_prefix": "Bearer {{token_format}}"
}
```

### Authentication Configuration (Standard) <a href="#authentication-configuration" id="authentication-configuration"></a>

For APIs following the standard OAuth2 flow, the configuration looks like:

```json
{
  "oauth2": {
    "auth_form": [],
    "config": {
      "client_id": "CLIENT_ID for OAuth2 app",
      "client_secret": "CLIENT_SECRET for OAuth2 app",
      "extra_authorization_url": {
        "response_type": "code"
      },
      "extra_access_url": {
        "grant_type": "authorization_code"
      },
      "step1_authorize_url": "https://.../authorize",
      "step3_access_url": "https://.../token",
      "token_refresh_url": "https://.../token"
    }
  }
}
```

### OAuth2 URLs Explained

* `step1_authorize_url`  URL for user authorization page (GET request)
* `step3_access_url`  URL for exchanging the code for an access token (POST by default with content-type `application/x-www-form-urlencoded`)
* `token_refresh_url`  Used for token refresh if different  from the regular `step3_access_url` (Deprecated for standard flows)

### Additional Query/Body Parameters

* extra\_authorization\_url: Add query parameters to the authorization request.
* extra\_access\_url: Add body parameters to the token exchange request.

```json
"scope": "scope1 scope2"
```

If scopes are required, add them in the extra\_authorization\_url:

### **Dynamic base domains**

To handle dynamic subdomains or environments (like production/sandbox), use **Jinja rendering** inside URLs as needed.

{% code title="Example" %}

```
https://{{ environment }}.example.com/oauth/token
```

{% endcode %}

### Handling OAuth2 deviations <a href="#handling-oauth2-deviations" id="handling-oauth2-deviations"></a>

For APIs that deviate from the standard OAuth2 flow, additional config options are available:

| `client_id_and_secret_in_headers` | Send client credentials as Basic Auth instead of body.              |
| --------------------------------- | ------------------------------------------------------------------- |
| `alternative_code_key`            | Rename the key for the authorization code.                          |
| `alternative_client_id_key`       | Rename the key for client ID.                                       |
| `alternative_client_secret_key`   | Rename the key for client secret.                                   |
| `grant_type`                      | Add custom grant\_type (Deprecated).                                |
| `accept_header`                   | Set a custom Accept header.                                         |
| `content_type`                    | Change Content-Type (e.g., to application/json).                    |
| `request_method`                  | Force GET instead of POST for token request.                        |
| `access_token_path`               | Define a custom JSON path to find the access token in the response. |

#### Deprecated auth\_form Fields

Additional fields that can be exposed to users:

| alternative\_scope | User-defined scope input (overwrites default scopes).              |
| ------------------ | ------------------------------------------------------------------ |
| client\_id         | Allows user to provide their own Client ID, overriding config.     |
| client\_secret     | Allows user to provide their own Client Secret, overriding config. |

Other custom fields can also be added manually if required.

***

### Examples <a href="#examples-1" id="examples-1"></a>

#### HubSpot - Standard with Scopes <a href="#hubspot-standard-with-scopes" id="hubspot-standard-with-scopes"></a>

The Authentication Configuration is [the standard one](#authentication-configuration), with specific scopes:

```json
{
  "oauth2": {
    "auth_form": [
      {
        "name": "scope",
        "title": "Additional scopes",
        "type": "text",
        "required": false,
        "hideInEmbed": true,
        "placeholder": "crm.schemas.companies.write crm.schemas.contacts.write",
        "info": "Adds scopes to the default: crm.schemas.companies.write crm.schemas.contacts.write crm.schemas.deals.read crm.schemas.deals.write files crm.objects.contacts.write e-commerce crm.objects.companies.write crm.objects.companies.read crm.objects.deals.read crm.schemas.contacts.read crm.objects.deals.write crm.objects.contacts.read crm.schemas.companies.read communication_preferences.read_write crm.objects.owners.read crm.lists.write crm.lists.read"
      }
    ],
    "config": {
      "client_id": "CLIENT_ID for OAuth2 app",
      "client_secret": "CLIENT_SECRET for OAuth2 app",
      "exchange_request_body_type": "json",
      "extra_access_url": {
        "grant_type": "authorization_code"
      },
      "extra_authorization_url": {
        "response_type": "code",
        "scope": "communication_preferences.read_write crm.lists.read crm.lists.write crm.objects.companies.read crm.objects.companies.write crm.objects.contacts.read crm.objects.contacts.write crm.objects.deals.read crm.objects.deals.write crm.objects.owners.read crm.schemas.companies.read crm.schemas.companies.write crm.schemas.contacts.read crm.schemas.contacts.write crm.schemas.deals.read crm.schemas.deals.write e-commerce files settings.users.read tickets{% if scope is defined %} {{ scope }}{% endif %}"
      },
      "or_exchange_request_body_type": "data",
      "step1_authorize_url": "https://app.hubspot.com/oauth/authorize",
      "step3_access_url": "https://api.hubapi.com/oauth/v1/token",
      "token_refresh_url": "https://api.hubapi.com/oauth/v1/token"
    }
  }
}
```

The header is the [standard header](#header).

#### TikTok - Alternative client keys and access token path <a href="#tiktok-alternative-client-keys-and-access-token-path" id="tiktok-alternative-client-keys-and-access-token-path"></a>

TikTok has a few deviations from the OAuth2 standard:

* The client id key is `app_id` instead of `client_id`, thus `alternative_client_id_key` needs to be set accordingly
* The client id key is `secret` instead of `client_secret`, thus `alternative_client_secret_key` needs to be set accordingly
* The access token is returned inside a dictionary called `data`, instead of in the 'root' dictionary, thus `access_token_path` needs to be set accordingly
* The base domain for the access url, is always `https://business-api.tiktok.com/`, even though the base domain for the endpoints itself is based on the environment: `https://{% if environment is defined and environment == 'Sandbox' %}sandbox{% else %}business{% endif %}-api.tiktok.com/open_api/v1.2/`

The Authentication Configuration thus needs to be setup like this:

```json
{
  "oauth2": {
    "auth_form": [
      {
        "name": "environment",
        "title": "Environment",
        "type": "select",
        "required": false,
        "default": "Production",
        "enum": [
          "Production",
          "Sandbox"
        ]
      }
    ],
    "config": {
      "access_token_path": "data.access_token",
      "alternative_client_id_key": "app_id",
      "alternative_client_secret_key": "secret",
      "alternative_code_key": "auth_code",
      "client_id": "CLIENT_ID for OAuth2 app",
      "client_secret": "CLIENT_SECRET for OAuth2 app",
      "exchange_request_body_type": "json",
      "extra_access_url": {
        "grant_type": "authorization_code"
      },
      "extra_authorization_url": {
        "response_type": "code"
      },
      "or_exchange_request_body_type": "data",
      "step1_authorize_url": "https://ads.tiktok.com/marketing_api/auth",
      "step3_access_url": "https://business-api.tiktok.com/open_api/v1.2/oauth2/access_token/",
      "token_refresh_url": "https://business-api.tiktok.com/open_api/v1.2/oauth2/access_token/"
    }
  }
}
```

[<br>](https://locoia.gitbook.io/connectors/authentication-types)


---

# 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.locoia.com/connectors/authentication/oauth2.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.
