Skip to main content

OAuth 2.0 Authentication

OAuth 2.0 is the industry standard for API authentication, used by most enterprise APIs. Reqon supports OAuth 2.0 with automatic token refresh.

Configuration

Mission file

source Xero {
auth: oauth2,
base: "https://api.xero.com/api.xro/2.0"
}

Credentials file

{
"Xero": {
"type": "oauth2",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"accessToken": "current-access-token",
"refreshToken": "current-refresh-token",
"tokenUrl": "https://identity.xero.com/connect/token",
"scopes": ["accounting.transactions.read", "accounting.contacts.read"],
"expiresAt": "2024-01-20T10:30:00Z"
}
}

Credential options

FieldRequiredDescription
typeYesMust be "oauth2"
clientIdYesOAuth client ID
clientSecretYesOAuth client secret
accessTokenYesCurrent access token
refreshTokenYesToken used to get new access tokens
tokenUrlYesToken endpoint URL
scopesNoRequested scopes
expiresAtNoWhen current token expires

Token refresh

Automatic refresh

When expiresAt is set and token expires, Reqon automatically:

  1. Calls tokenUrl with refresh token
  2. Updates access token
  3. Retries the failed request
{
"Xero": {
"type": "oauth2",
"clientId": "...",
"clientSecret": "...",
"accessToken": "old-token",
"refreshToken": "refresh-token",
"tokenUrl": "https://identity.xero.com/connect/token",
"expiresAt": "2024-01-20T10:30:00Z"
}
}

On 401 response

Even without expiresAt, Reqon refreshes on 401:

action FetchData {
get "/data"
// If 401, automatic refresh attempt
}

Manual refresh pattern

For non-standard APIs:

action FetchData {
get "/data"

match response {
{ error: _, code: 401 } -> jump RefreshToken then retry,
_ -> continue
}
}

action RefreshToken {
post "/oauth/token" {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: {
grant_type: "refresh_token",
refresh_token: env("REFRESH_TOKEN"),
client_id: env("CLIENT_ID"),
client_secret: env("CLIENT_SECRET")
}
}

// Store new tokens
store {
accessToken: response.access_token,
refreshToken: response.refresh_token
} -> tokens
}

Common OAuth2 providers

Xero

{
"Xero": {
"type": "oauth2",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"accessToken": "...",
"refreshToken": "...",
"tokenUrl": "https://identity.xero.com/connect/token"
}
}

QuickBooks

{
"QuickBooks": {
"type": "oauth2",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"accessToken": "...",
"refreshToken": "...",
"tokenUrl": "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
}
}

Salesforce

{
"Salesforce": {
"type": "oauth2",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"accessToken": "...",
"refreshToken": "...",
"tokenUrl": "https://login.salesforce.com/services/oauth2/token"
}
}

Google APIs

{
"Google": {
"type": "oauth2",
"clientId": "your-client-id.apps.googleusercontent.com",
"clientSecret": "your-client-secret",
"accessToken": "...",
"refreshToken": "...",
"tokenUrl": "https://oauth2.googleapis.com/token"
}
}

Microsoft Graph

{
"Microsoft": {
"type": "oauth2",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"accessToken": "...",
"refreshToken": "...",
"tokenUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/token"
}
}

Token storage

File-based (development)

Tokens are stored in the credentials file. Reqon updates them after refresh.

Secure storage (production)

For production, use secure storage:

import { execute } from 'reqon';
import { getSecureTokens, saveSecureTokens } from './secure-storage';

const tokens = await getSecureTokens('Xero');

const result = await execute(source, {
auth: {
Xero: {
type: 'oauth2',
...tokens
}
},
onTokenRefresh: async (source, newTokens) => {
await saveSecureTokens(source, newTokens);
}
});

Scopes

Request specific scopes:

{
"API": {
"type": "oauth2",
"scopes": [
"read:users",
"write:users",
"read:orders"
]
}
}

Additional headers

Some APIs require extra headers:

source API {
auth: oauth2,
base: "https://api.example.com",
headers: {
"Xero-Tenant-Id": env("XERO_TENANT_ID")
}
}

Handling multi-tenant

For APIs like Xero with multiple organizations:

mission XeroSync {
source Xero {
auth: oauth2,
base: "https://api.xero.com/api.xro/2.0",
headers: {
"Xero-Tenant-Id": env("XERO_TENANT_ID")
}
}
}

Or iterate over tenants:

action SyncAllTenants {
get "/connections"

for tenant in response {
// Each tenant request
get concat("/", tenant.tenantId, "/invoices") {
headers: { "Xero-Tenant-Id": tenant.tenantId }
}
}
}

Troubleshooting

"invalid_grant" Error

Refresh token is invalid or expired. Re-authenticate:

  1. Go through OAuth flow again
  2. Get new access and refresh tokens
  3. Update credentials file

"Token expired" but no refresh

Ensure refreshToken and tokenUrl are set:

{
"API": {
"refreshToken": "must-be-present",
"tokenUrl": "must-be-present"
}
}

"Invalid client" error

Check clientId and clientSecret are correct.

Scope issues

Ensure requested scopes are authorized for your app.