Skip to main content

Overview

The integration platform supports 4 authentication strategies. Choose based on what the third-party service offers.

Quick Comparison

TypeWhen to UseUser SetupPlatform SetupToken Refresh
OAuth 2.0Service has OAuth1-click connectAdmin configures OAuth appAuto
API KeyService uses API keysUser enters keyNoneN/A (keys don’t expire)
Basic AuthService uses username/passwordUser enters credsNoneN/A
CustomComplex multi-field authUser enters multiple fieldsNoneN/A

OAuth 2.0

Best for: GitHub, Google, Linear, Vercel, most modern SaaS

Configuration

auth: {
  type: 'oauth2',
  config: {
    // Required
    authorizeUrl: 'https://provider.com/oauth/authorize',
    tokenUrl: 'https://provider.com/oauth/token',
    scopes: ['read:user', 'read:org'],

    // Optional
    pkce: false,                    // Use PKCE flow? (default: false)
    clientAuthMethod: 'body',       // 'body' or 'header' (default: 'body')
    supportsRefreshToken: true,     // Can tokens be refreshed? (default: true)

    // Additional params sent during authorization
    authorizationParams: {
      access_type: 'offline',      // Request refresh token
      prompt: 'consent',           // Force consent screen
    },

    // Additional params sent during token exchange
    tokenParams: {
      grant_type: 'authorization_code',
    },

    // Admin setup instructions (Markdown)
    setupInstructions: `...`,

    // URL to create OAuth app
    createAppUrl: 'https://provider.com/apps/new',

    // Custom settings (advanced)
    customSettings: [
      {
        id: 'app_name',
        label: 'App Name',
        type: 'text',
        required: true,
        token: '{APP_NAME}', // Replaces in authorizeUrl
      },
    ],
  },
},

Key Options Explained

pkce (Proof Key for Code Exchange)

When to use: Some providers require it (GitHub does NOT, Auth0 does)
pkce: true; // Generates code_verifier and code_challenge

clientAuthMethod

How to send client credentials during token exchange:
  • 'body' (default): Send as form fields
    POST /token
    client_id=...&client_secret=...
    
  • 'header': Send as Basic Auth header
    POST /token
    Authorization: Basic base64(client_id:client_secret)
    
Use header for: OAuth providers that require Basic Auth (some enterprise providers)

supportsRefreshToken

Controls token refresh behavior:
  • true (default): Platform auto-refreshes tokens before expiry
  • false: Tokens are long-lived (e.g., GitHub Personal Access Tokens)
Set to false if: Provider doesn’t support refresh tokens or tokens never expire

customSettings

For providers with non-standard OAuth requirements: Example: Rippling needs app name in authorize URL:
authorizeUrl: 'https://api.rippling.com/connect/authorize/{APP_NAME}',
customSettings: [
  {
    id: 'app_name',
    label: 'Rippling App Name',
    type: 'text',
    required: true,
    token: '{APP_NAME}',
  },
],
Platform admin enters app_name → Platform replaces {APP_NAME} in URL.

Access Token in Checks

run: async (ctx: CheckContext) => {
  // Access token is automatically available
  const token = ctx.accessToken;

  // ctx.fetch() automatically adds Bearer token
  const data = await ctx.fetch('/api/endpoint');

  // For custom headers
  const response = await fetch(url, {
    headers: {
      Authorization: `Bearer ${ctx.accessToken}`,
    },
  });
};

API Key

Best for: Simple REST APIs with static API keys (e.g., Stripe, SendGrid)

Configuration

auth: {
  type: 'api_key',
  config: {
    in: 'header',           // or 'query'
    name: 'X-API-Key',      // Header name or query param name
    prefix: 'Bearer ',      // Optional prefix (e.g., "Bearer ", "Token ")
  },
},

Key Options

in - Where to send the API key

Header (recommended):
in: 'header',
name: 'X-API-Key',

// Results in:
// X-API-Key: your-api-key-value
Query parameter:
in: 'query',
name: 'api_key',

// Results in:
// GET /endpoint?api_key=your-api-key-value
Use query for: APIs that don’t accept custom headers (rare)

prefix - Add a prefix to the key

prefix: 'Bearer ',   // Common for tokens
prefix: 'Token ',    // Some APIs use this
prefix: '',          // No prefix (default)

// Example with Bearer:
// Authorization: Bearer sk_live_abc123...

Credential Field

User will see a form field to enter the API key:
// Automatically generated credential field
{
  id: 'api_key',
  label: 'API Key',
  type: 'password',
  required: true,
  placeholder: 'sk_live_...',
  helpText: 'Found in Settings → API Keys',
}

Access in Checks

run: async (ctx: CheckContext) => {
  // API key is automatically added to requests
  const data = await ctx.fetch('/endpoint');

  // Or access it directly
  const apiKey = ctx.credentials.api_key;
};

Basic Auth

Best for: APIs using username/password authentication (e.g., some internal tools)

Configuration

auth: {
  type: 'basic',
  config: {
    usernameField: 'username',  // Default, can customize
    passwordField: 'password',  // Default, can customize
  },
},

Credential Fields

Users will see two fields:
// Automatically generated
[
  {
    id: 'username',
    label: 'Username',
    type: 'text',
    required: true,
  },
  {
    id: 'password',
    label: 'Password',
    type: 'password',
    required: true,
  },
];

Custom Field Names

auth: {
  type: 'basic',
  config: {
    usernameField: 'email',      // Use 'email' instead of 'username'
    passwordField: 'api_token',  // Use 'api_token' instead of 'password'
  },
},

Access in Checks

run: async (ctx: CheckContext) => {
  // Basic Auth header is automatically added
  // Authorization: Basic base64(username:password)
  const data = await ctx.fetch('/endpoint');

  // Or access credentials directly
  const username = ctx.credentials.username;
  const password = ctx.credentials.password;
};

Custom Auth

Best for: Complex authentication requiring multiple fields (AWS IAM Role, Azure Service Principal, GCP Service Account)

Configuration

auth: {
  type: 'custom',
  config: {
    description: 'AWS IAM Role for cross-account access',

    credentialFields: [
      {
        id: 'roleArn',
        label: 'IAM Role ARN',
        type: 'text',
        required: true,
        placeholder: 'arn:aws:iam::123456789012:role/MyRole',
        helpText: 'The ARN of the IAM role to assume',
      },
      {
        id: 'externalId',
        label: 'External ID',
        type: 'password',
        required: true,
        helpText: 'Unique identifier for security',
      },
      {
        id: 'region',
        label: 'AWS Region',
        type: 'combobox',
        required: true,
        placeholder: 'Select or type...',
        helpText: 'Primary region for API calls',
        options: [
          { value: 'us-east-1', label: 'US East (N. Virginia)' },
          { value: 'eu-west-1', label: 'Europe (Ireland)' },
        ],
      },
    ],

    setupInstructions: `## Setup Guide

1. Create IAM role in AWS
2. Copy the Role ARN
3. Generate External ID
4. Enter credentials below`,
  },
},

Field Types

TypeUI ComponentWhen to UseExample
textText inputSingle-line textProject ID, Account ID
passwordPassword input (hidden)Secrets, tokensAPI keys, passwords
textareaMulti-line textareaLong text, JSONService account JSON key
selectDropdown (fixed options)Choose from listEnvironment (prod/dev)
comboboxDropdown + custom inputChoose or typeAWS region, timezone
numberNumber inputNumeric valuesPort, timeout
urlURL input (with validation)Web addressesWebhook URL, API endpoint

Field Options

{
  id: 'field_name',           // Unique identifier (snake_case)
  label: 'Field Label',       // Shown to user
  type: 'text',              // See table above
  required: true,            // Is this field mandatory?
  placeholder: 'example...',  // Placeholder text
  helpText: 'Explain where to find this', // Help text below field

  // For select/combobox only:
  options: [
    { value: 'val1', label: 'Option 1' },
    { value: 'val2', label: 'Option 2' },
  ],
}

Access in Checks

run: async (ctx: CheckContext) => {
  // All fields available as key-value pairs
  const roleArn = ctx.credentials.roleArn;
  const externalId = ctx.credentials.externalId;
  const region = ctx.credentials.region;

  // Custom authentication logic
  const client = await createCustomClient({
    roleArn,
    externalId,
    region,
  });
};

Choosing an Auth Type

Decision Tree

Does the service have OAuth?
├─ YES → Use OAuth 2.0
│   └─ Best UX, auto token refresh, secure

└─ NO

    Does it use a single API key?
    ├─ YES → Use API Key
    │   └─ Simple, user just pastes key

    └─ NO

        Does it use username + password?
        ├─ YES → Use Basic Auth
        │   └─ Two fields, encoded automatically

        └─ NO → Use Custom Auth
            └─ Multiple fields, full control

Comparison: OAuth vs API Key

AspectOAuth 2.0API Key
User ExperienceBest (1-click)OK (find & paste key)
SecurityTokens expire & refreshLong-lived secrets
Platform SetupAdmin must configureNone needed
RevocationUser revokes in providerUser must regenerate key
Scope ControlRequest specific permissionsKey has fixed permissions
Recommendation: Use OAuth if the service supports it.

Comparison: Basic Auth vs Custom

AspectBasic AuthCustom Auth
Fields2 (username/password)Any number
EncodingAuto (Base64)Manual in check code
Use CaseSimple APIsComplex auth (AWS, Azure)
SetupMinimal configDefine all fields
Use Basic Auth for: Simple username/password APIs
Use Custom Auth for: Multi-field credentials, service accounts, IAM roles

Examples by Service Type

Cloud Providers (AWS, Azure, GCP)

Use: Custom Auth (service accounts, IAM roles) Why: These services use multi-field credentials that need custom authentication logic. OAuth is possible but service accounts are the recommended approach for programmatic access.

SaaS Tools (GitHub, Linear, Notion)

Use: OAuth 2.0 Why: Best user experience, tokens auto-refresh, users can revoke access easily.

Internal Tools / Legacy APIs

Use: Basic Auth or API Key Why: Simpler services often use these methods. Choose based on what the API supports.

Advanced OAuth Options

PKCE (Proof Key for Code Exchange)

What it is: Extra security for OAuth (prevents authorization code interception) When to enable:
pkce: true; // Enable for: Auth0, Okta, some enterprise providers
pkce: false; // Default for: GitHub, Google, Linear
How it works: Platform generates a random code_verifier, hashes it to create code_challenge, sends challenge during authorization, sends verifier during token exchange.

Client Authentication Method

How platform authenticates itself to the provider: Body (default):
POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&client_id=...&client_secret=...
Header (Basic Auth):
POST /token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...
Use header when: Provider documentation explicitly requires Basic Auth for client authentication.

Custom Authorization Parameters

Add extra params to the authorization URL:
authorizationParams: {
  access_type: 'offline',    // Google: Request refresh token
  prompt: 'consent',         // Force consent screen (always request scopes)
  response_mode: 'query',    // How to return auth code
}
Common use cases:
  • access_type: 'offline' - Google APIs (get refresh token)
  • prompt: 'consent' - Always show consent screen (good for testing)
  • Custom provider-specific params

Security Best Practices

API Keys

  • Use type: 'password' for sensitive fields (hidden input)
  • Store in credential vault (auto-encrypted)
  • Never log API keys
  • Add help text explaining where to find/generate the key

OAuth

  • Request minimum scopes needed
  • Use access_type: 'offline' for refresh tokens
  • Set supportsRefreshToken: true to auto-refresh
  • Explain scope requirements in setup instructions

Basic Auth

  • Both username and password are required
  • Password field is auto-hidden
  • Credentials are auto-encoded to Base64
  • Never log passwords

Custom Auth

  • Mark sensitive fields as type: 'password'
  • Validate all required fields in checks
  • Handle missing fields gracefully
  • Don’t expose credential values in logs/errors

Complete OAuth Example

auth: {
  type: 'oauth2',
  config: {
    authorizeUrl: 'https://github.com/login/oauth/authorize',
    tokenUrl: 'https://github.com/login/oauth/access_token',
    scopes: ['repo', 'read:org', 'read:user'],
    pkce: false,
    clientAuthMethod: 'body',
    supportsRefreshToken: false, // GitHub tokens don't expire
    authorizationParams: {
      allow_signup: 'false', // Don't show sign-up on consent
    },
    setupInstructions: `## Create GitHub OAuth App

1. Go to https://github.com/settings/developers
2. Click "New OAuth App"
3. Enter:
   - Application name: Your app name
   - Homepage URL: https://yourapp.com
   - Callback URL: [shown below]
4. Click "Register application"
5. Copy Client ID and generate Client Secret
6. Paste both below`,
    createAppUrl: 'https://github.com/settings/apps/new',
  },
},

Testing Your Auth

OAuth

  1. Configure platform credentials in /admin/integrations
  2. Go to /integrations and click Connect
  3. Should redirect to provider OAuth screen
  4. Authorize → Should redirect back successfully
  5. Check connection is active

API Key / Basic / Custom

  1. Go to /integrations and click Connect
  2. Form should show your credential fields
  3. Enter test credentials
  4. Connection should be created successfully
  5. Run a check to verify credentials work

Debug OAuth Issues

Check API logs for:
  • OAuth completed for [provider] - Success
  • OAuth callback error - Authorization failed
  • Token exchange failed - Bad client credentials
  • Invalid state - State mismatch (check callback URL)

Summary

Choose your auth type:
  • Service has OAuth? → Use OAuth 2.0
  • Single API key? → Use API Key
  • Username/password? → Use Basic Auth
  • Multiple fields or complex? → Use Custom Auth
OAuth is best when available - better UX, security, and user control.