# Custom OAuth 2.0

Connect any OAuth 2.0 Authorization Code provider by supplying your own credentials.

The **Custom OAuth 2.0** option lets you connect any service that supports the standard [Authorization Code grant flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1). You supply the Authorization URL, Token URL, Client ID, Client Secret, and scopes. JsWorkflows handles the redirect, token exchange, and storage.

## What is supported

- Standard OAuth 2.0 Authorization Code flow
- Token exchange via `application/x-www-form-urlencoded` POST to your Token URL
- Token storage encrypted at rest
- Automatic token refresh at expiry, **only if the provider returns a `refresh_token` and `expires_in` in the token response**

## What is not supported

- PKCE (code challenge/verifier)
- Token endpoints that require JSON body instead of form-encoded body
- Non-standard grant types (client credentials, device code, etc.)
- Providers that do not follow the standard `access_token` response field name
- Providers that require a different redirect URI

## Setup

### 1. Create an OAuth app at the provider

In the provider's developer console, create an OAuth 2.0 application. Note:

- **Grant type** must be **Authorization Code**
- Add `https://oauth.jsworkflows.com/oauth2/callback` as an authorized redirect URI (exact match required)
- Copy the **Client ID**, **Client Secret**, **Authorization URL**, and **Token URL**

### 2. Add the connection in JsWorkflows

Go to **Settings → OAuth2 Tokens** and click **Connect to Service**. Select **Custom (OAuth 2.0)** from the service list.

Fill in the fields:

| Field | Description |
| --- | --- |
| **OAuth Redirect URL** | Pre-filled. Copy and add it to your provider's redirect URI list |
| **Client ID** | From your OAuth app |
| **Client Secret** | From your OAuth app |
| **Authorization URL** | The provider's OAuth authorization endpoint |
| **Token URL** | The provider's token exchange endpoint |
| **OAuth Name** | A friendly label visible only to you |
| **Handle** | The unique identifier used in `api.getOAuthToken('your-handle')`. Use lowercase letters, numbers, and hyphens only. It cannot be changed after creation |
| **Scopes** | Space-separated scopes to request (e.g. `read:data write:data`) |

Click **Generate and Authorize**. A popup window opens the provider's authorization page. After you grant access, the popup closes and the connection appears in your list.

## Usage in workflows

```js
export class Workflow {
  async start(_data, _headers, api) {
    const { token, error } = await api.getOAuthToken('my-custom-handle');
    if (error || !token) throw new Error(error || 'Missing OAuth token');

    const res = await fetch('https://api.example.com/resource', {
      headers: { Authorization: `Bearer ${token}` },
    });

    if (!res.ok) {
      throw new Error(`API ${res.status}: ${await res.text()}`);
    }

    const json = await res.json();
    console.log(`Response: ${JSON.stringify(json)}`);
  }
}
```

Many OAuth providers use `Authorization: Bearer <token>` for API calls, but some APIs require a different header format. Always follow the provider's API documentation for resource requests.

## Token refresh

JsWorkflows automatically refreshes the access token when it expires, using the `refresh_token` returned by the provider. The refresh POST uses the same Token URL you provided during setup, with standard `refresh_token` grant parameters.

**If the provider does not return a `refresh_token` in the initial token response, automatic refresh is not possible.** In that case, the token will stop working after it expires and you will need to reconnect the integration manually.

## Editing a connection

When you edit a Custom OAuth connection, the **Authorization URL** and **Token URL** are pre-filled from the stored values. The **Client Secret** is shown as a placeholder — leave it unchanged to keep the existing secret, or enter a new one to replace it.

Re-saving triggers a new OAuth authorization popup so the provider can issue a fresh token with the updated credentials or scopes.

## Limitations

- **No PKCE support**: providers that require PKCE (common for public clients and mobile apps) will not work.
- **No JSON token body**: the token request always uses `application/x-www-form-urlencoded`. Providers that require a JSON body are not compatible.
- **Refresh requires `refresh_token`**: if the provider only issues short-lived access tokens without a refresh token, the token will expire and need manual reconnection.
- **Standard `access_token` field required**: the token response must contain an `access_token` field at the top level of the JSON response.
- **One redirect URI**: the fixed redirect URI is `https://oauth.jsworkflows.com/oauth2/callback`. Providers that do not allow this exact URI cannot be used.