Authentication
Authentication
Section titled “Authentication”The Gateway API uses OAuth2 for authentication, supporting multiple grant types to accommodate different integration scenarios. All API requests must include a valid access token in the Authorization header.
Authentication Methods
Section titled “Authentication Methods”The Gateway supports three OAuth2 grant types:
1. Password Grant (User-based Authentication)
Section titled “1. Password Grant (User-based Authentication)”Use Case: User-specific operations, web applications with user login
Middleware: auth:api
Access To:
/api/1.0/*- Legacy v1.0 endpoints/api/2.0/user/*- User management endpoints
Token Request:
POST /oauth/tokenContent-Type: application/json
{ "grant_type": "password", "client_id": "<YOUR_CLIENT_ID>", "client_secret": "<YOUR_CLIENT_SECRET>", "username": "<USER_EMAIL>", "password": "<USER_PASSWORD>", "scope": "*"}Response:
{ "token_type": "Bearer", "expires_in": 31536000, "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", "refresh_token": "def50200..."}2. Client Credentials Grant (Application Authentication)
Section titled “2. Client Credentials Grant (Application Authentication)”Use Case: Server-to-server integrations, background jobs, app-level operations
Middleware: client_credentials + auth.app
Access To:
/api/2.0/app/*- Application-level endpoints
Token Request:
POST /oauth/tokenContent-Type: application/json
{ "grant_type": "client_credentials", "client_id": "<YOUR_CLIENT_ID>", "client_secret": "<YOUR_CLIENT_SECRET>", "scope": "*"}Response:
{ "token_type": "Bearer", "expires_in": 31536000, "access_token": "eyJ0eXAiOiJKV1QiLC..."}[!NOTE] Client Credentials tokens do NOT include a refresh token. Generate a new token when needed.
3. Account-based Authentication (Web Components)
Section titled “3. Account-based Authentication (Web Components)”Use Case: Embedded widgets, account-scoped operations
Middleware: auth.account:webcomponent:*
Access To:
/api/2.0/webcomponent/*- Account-scoped endpoints
Token Generation: Account tokens are generated via intent tokens or direct account authorization flows.
Using Access Tokens
Section titled “Using Access Tokens”Include the access token in the Authorization header:
GET /api/2.0/app/integrationsHost: <GATEWAY_URL>Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...Accept: application/jsoncURL Example:
curl -X GET https://<GATEWAY_URL>/api/2.0/app/integrations \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Accept: application/json"Token Expiration
Section titled “Token Expiration”| Grant Type | Expires In | Refresh Available |
|---|---|---|
| Password Grant | 1 year (31536000s) | ✅ Yes |
| Client Credentials | 1 year (31536000s) | ❌ No |
| Account Token | Variable | Depends on implementation |
Refreshing Tokens (Password Grant Only)
Section titled “Refreshing Tokens (Password Grant Only)”Use the refresh token to obtain a new access token without re-authenticating:
POST /oauth/tokenContent-Type: application/json
{ "grant_type": "refresh_token", "refresh_token": "<YOUR_REFRESH_TOKEN>", "client_id": "<YOUR_CLIENT_ID>", "client_secret": "<YOUR_CLIENT_SECRET>", "scope": "*"}Response:
{ "token_type": "Bearer", "expires_in": 31536000, "access_token": "<NEW_ACCESS_TOKEN>", "refresh_token": "<NEW_REFRESH_TOKEN>"}[!IMPORTANT] The old refresh token becomes invalid after use. Always store the new refresh token.
Scopes and Permissions
Section titled “Scopes and Permissions”OAuth2 Scopes
Section titled “OAuth2 Scopes”Tokens can be issued with specific scopes to limit access:
*- Full access (default)app:account-read- Read account dataapp:account-create- Create accountsapp:integration-read- Read integrationsapp:integration-action- Execute integration actionsapp:integration-claim- Claim integrationsapp:integration-update- Update integrationsapp:integration-delete- Delete integrationswebcomponent:integration-read- Read integrations (account scope)webcomponent:integration-event- Execute integration events (account scope)
Request specific scopes:
{ "grant_type": "client_credentials", "client_id": "your-client-id", "client_secret": "your-client-secret", "scope": "app:account-read app:integration-read"}Permission-Based Access Control
Section titled “Permission-Based Access Control”Some endpoints require specific permissions beyond scopes:
Common Permissions:
read-user- View user datacreate-user- Create usersupdate-user- Modify usersdelete-user- Delete usersread-integration- View integrationscreate-integration- Create integrationsupdate-integration- Modify integrationsdelete-integration- Delete integrationsread-schedule- View schedulescreate-schedule- Create schedulesupdate-schedule- Modify schedulesdelete-schedule- Delete schedules
Middleware Example:
['middleware' => ['auth:api', 'permission:read-integration']]Authentication Errors
Section titled “Authentication Errors”401 Unauthorized
Section titled “401 Unauthorized”Cause: Invalid, missing, or expired token
Response:
{ "message": "Unauthenticated."}Solutions:
- Verify token is included in
Authorizationheader - Check token hasn’t expired
- Refresh token (password grant) or generate new token (client credentials)
403 Forbidden
Section titled “403 Forbidden”Cause: Authenticated but lacking required permissions or scopes
Response:
{ "message": "This action is unauthorized."}Solutions:
- Verify token has required scopes
- Check user has necessary permissions
- Ensure resource ownership validation passes
400 Bad Request (Token Endpoint)
Section titled “400 Bad Request (Token Endpoint)”Cause: Invalid credentials or malformed request
Response:
{ "error": "invalid_client", "error_description": "Client authentication failed", "message": "Client authentication failed"}Solutions:
- Verify
client_idandclient_secretare correct - Check request payload format
- Ensure
Content-Type: application/jsonheader is set
Security Best Practices
Section titled “Security Best Practices”1. Store Tokens Securely
Section titled “1. Store Tokens Securely”[!CAUTION] Never commit tokens to version control or expose them in client-side code!
Environment Variables:
GATEWAY_ACCESS_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...GATEWAY_CLIENT_ID=your-client-idGATEWAY_CLIENT_SECRET=your-client-secretEncrypt at Rest:
// Laravel example$encrypted = encrypt($accessToken);$decrypted = decrypt($encrypted);2. Use HTTPS Only
Section titled “2. Use HTTPS Only”All API requests must use HTTPS to protect tokens in transit.
❌ Never: http://<GATEWAY_URL>/api/...
✅ Always: https://<GATEWAY_URL>/api/...
3. Rotate Credentials Regularly
Section titled “3. Rotate Credentials Regularly”- Refresh tokens before expiration
- Rotate client secrets periodically
- Revoke compromised tokens immediately
4. Minimize Token Scope
Section titled “4. Minimize Token Scope”Request only the scopes your application needs:
❌ Avoid: "scope": "*"
✅ Better: "scope": "app:integration-read app:account-read"
5. Implement Token Refresh Logic
Section titled “5. Implement Token Refresh Logic”Automatically refresh tokens before expiration:
class GatewayClient { constructor(clientId, clientSecret) { this.clientId = clientId; this.clientSecret = clientSecret; this.token = null; this.tokenExpiry = null; }
async getToken() { if (this.token && this.tokenExpiry > Date.now() + 60000) { return this.token; }
const response = await axios.post('/oauth/token', { grant_type: 'client_credentials', client_id: this.clientId, client_secret: this.clientSecret, scope: '*' });
this.token = response.data.access_token; this.tokenExpiry = Date.now() + (response.data.expires_in * 1000);
return this.token; }}Code Examples
Section titled “Code Examples”PHP (Laravel)
Section titled “PHP (Laravel)”use Illuminate\Support\Facades\Http;
// Get access token$response = Http::post('https://<GATEWAY_URL>/oauth/token', [ 'grant_type' => 'client_credentials', 'client_id' => config('gateway.client_id'), 'client_secret' => config('gateway.client_secret'), 'scope' => '*']);
$token = $response->json()['access_token'];
// Make authenticated request$integrations = Http::withToken($token) ->get('https://<GATEWAY_URL>/api/2.0/app/integrations') ->json();JavaScript (Node.js)
Section titled “JavaScript (Node.js)”const axios = require('axios');
async function authenticate() { const response = await axios.post('https://<GATEWAY_URL>/oauth/token', { grant_type: 'client_credentials', client_id: process.env.GATEWAY_CLIENT_ID, client_secret: process.env.GATEWAY_CLIENT_SECRET, scope: '*' });
return response.data.access_token;}
async function getIntegrations() { const token = await authenticate();
const response = await axios.get( 'https://<GATEWAY_URL>/api/2.0/app/integrations', { headers: { 'Authorization': `Bearer ${token}` } } );
return response.data;}Python
Section titled “Python”import requestsimport os
def get_access_token(): response = requests.post( 'https://<GATEWAY_URL>/oauth/token', json={ 'grant_type': 'client_credentials', 'client_id': os.getenv('GATEWAY_CLIENT_ID'), 'client_secret': os.getenv('GATEWAY_CLIENT_SECRET'), 'scope': '*' } ) return response.json()['access_token']
def get_integrations(): token = get_access_token() response = requests.get( 'https://<GATEWAY_URL>/api/2.0/app/integrations', headers={'Authorization': f'Bearer {token}'} ) return response.json()Testing Authentication
Section titled “Testing Authentication”Test your authentication setup using the interactive Swagger documentation:
- Click the “Authorize” button
- Enter your
client_idandclient_secret - Select desired scopes
- Click “Authorize”
- Test endpoints with authenticated requests