Authentication Guide
Learn how to authenticate with the Connix API using API keys and OAuth2. This guide covers everything from getting your first API key to implementing secure authentication in production applications.
Authentication Methods
Section titled “Authentication Methods”Connix supports two primary authentication methods:
- API Key Authentication - Simple and secure for server-to-server communication
- OAuth2 Authentication - Industry standard for user-facing applications
API Key Authentication
Section titled “API Key Authentication”API key authentication is the simplest and most common method for accessing the Connix API.
Getting Your API Key
Section titled “Getting Your API Key”- Sign in to console.connix.io
- Navigate to Settings → API Keys
- Click Generate New API Key
- Copy and securely store your API key
- Name your key for easy identification
Using API Keys
Section titled “Using API Keys”Include your API key in the X-API-Key
header:
curl -X GET https://api.connix.io/api/v1/projects \ -H "X-API-Key: cx_1234567890abcdef1234567890abcdef"
const response = await fetch('https://api.connix.io/api/v1/projects', { headers: { 'X-API-Key': 'cx_1234567890abcdef1234567890abcdef' }});
import requests
headers = { 'X-API-Key': 'cx_1234567890abcdef1234567890abcdef'}
response = requests.get( 'https://api.connix.io/api/v1/projects', headers=headers)
req, _ := http.NewRequest("GET", "https://api.connix.io/api/v1/projects", nil)req.Header.Set("X-API-Key", "cx_1234567890abcdef1234567890abcdef")
client := &http.Client{}resp, err := client.Do(req)
Environment Variables
Section titled “Environment Variables”Store API keys securely using environment variables:
# Add to ~/.bashrc or ~/.zshrcexport CONNIX_API_KEY="cx_1234567890abcdef1234567890abcdef"
# Reload your shellsource ~/.bashrc
# PowerShell$env:CONNIX_API_KEY="cx_1234567890abcdef1234567890abcdef"
# Or permanently via System Properties# System Properties → Environment Variables
# In DockerfileENV CONNIX_API_KEY=cx_1234567890abcdef1234567890abcdef
# Or with docker rundocker run -e CONNIX_API_KEY=cx_1234567890abcdef... myapp
# .env file (add to .gitignore!)CONNIX_API_KEY=cx_1234567890abcdef1234567890abcdefCONNIX_BASE_URL=https://api.connix.io
SDK Configuration
Section titled “SDK Configuration”Most Connix SDKs automatically detect environment variables:
import { ConnixClient } from '@connix/sdk';
// Auto-detects CONNIX_API_KEYconst client = new ConnixClient();
// Or explicitconst client = new ConnixClient({ apiKey: process.env.CONNIX_API_KEY});
import conniximport os
# Auto-detects CONNIX_API_KEYclient = connix.Client()
# Or explicitclient = connix.Client(os.getenv('CONNIX_API_KEY'))
import "github.com/connix-io/connix-go"
// Auto-detects CONNIX_API_KEYclient := connix.NewClientFromEnv()
// Or explicitclient := connix.NewClient(os.Getenv("CONNIX_API_KEY"))
import io.connix.sdk.ConnixClient;
// ExplicitConnixClient client = new ConnixClient( System.getenv("CONNIX_API_KEY"));
OAuth2 Authentication
Section titled “OAuth2 Authentication”OAuth2 is ideal for user-facing applications where you need to act on behalf of users.
OAuth2 Flow Overview
Section titled “OAuth2 Flow Overview”- Authorization Request - Redirect user to Connix authorization server
- User Consent - User grants permissions to your application
- Authorization Code - Connix redirects back with authorization code
- Token Exchange - Exchange code for access token
- API Requests - Use access token for authenticated requests
OAuth2 Configuration
Section titled “OAuth2 Configuration”Register your application at console.connix.io:
- Go to Settings → OAuth Applications
- Click New OAuth Application
- Fill in your application details:
- Application Name: Your app name
- Homepage URL: Your app’s homepage
- Callback URL: Where users return after authorization
- Note your Client ID and Client Secret
Authorization URLs
Section titled “Authorization URLs”Authorization URL:
https://console.connix.io/oauth2/authorize
Token URL:
https://console.connix.io/oauth2/token
Scopes
Section titled “Scopes”Available OAuth2 scopes:
Scope | Description |
---|---|
read | Read access to user’s projects and agents |
write | Full access to create, update, and delete resources |
Implementation Examples
Section titled “Implementation Examples”const express = require('express');const app = express();
const CLIENT_ID = 'your-client-id';const CLIENT_SECRET = 'your-client-secret';const REDIRECT_URI = 'http://localhost:3000/callback';
// Step 1: Redirect to authorizationapp.get('/auth', (req, res) => { const authUrl = new URL('https://console.connix.io/oauth2/authorize'); authUrl.searchParams.set('client_id', CLIENT_ID); authUrl.searchParams.set('redirect_uri', REDIRECT_URI); authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('scope', 'read write'); authUrl.searchParams.set('state', 'random-state-string');
res.redirect(authUrl.toString());});
// Step 2: Handle callbackapp.get('/callback', async (req, res) => { const { code, state } = req.query;
// Verify state parameter (important for security) if (state !== 'random-state-string') { return res.status(400).send('Invalid state'); }
// Exchange code for token const tokenResponse = await fetch('https://console.connix.io/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code: code, redirect_uri: REDIRECT_URI }) });
const tokens = await tokenResponse.json();
// Store tokens securely (session, database, etc.) req.session.accessToken = tokens.access_token; req.session.refreshToken = tokens.refresh_token;
res.redirect('/dashboard');});
// Use access token for API requestsapp.get('/api/projects', async (req, res) => { const accessToken = req.session.accessToken;
const response = await fetch('https://api.connix.io/api/v1/projects', { headers: { 'Authorization': `Bearer ${accessToken}` } });
const projects = await response.json(); res.json(projects);});
from flask import Flask, request, redirect, session, jsonifyimport requestsimport secrets
app = Flask(__name__)app.secret_key = 'your-secret-key'
CLIENT_ID = 'your-client-id'CLIENT_SECRET = 'your-client-secret'REDIRECT_URI = 'http://localhost:5000/callback'
@app.route('/auth')def auth(): state = secrets.token_urlsafe(32) session['oauth_state'] = state
auth_url = ( 'https://console.connix.io/oauth2/authorize' f'?client_id={CLIENT_ID}' f'&redirect_uri={REDIRECT_URI}' f'&response_type=code' f'&scope=read write' f'&state={state}' )
return redirect(auth_url)
@app.route('/callback')def callback(): code = request.args.get('code') state = request.args.get('state')
# Verify state if state != session.get('oauth_state'): return 'Invalid state', 400
# Exchange code for token token_response = requests.post( 'https://console.connix.io/oauth2/token', data={ 'grant_type': 'authorization_code', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'code': code, 'redirect_uri': REDIRECT_URI } )
tokens = token_response.json()
# Store tokens session['access_token'] = tokens['access_token'] session['refresh_token'] = tokens['refresh_token']
return redirect('/dashboard')
@app.route('/api/projects')def projects(): access_token = session.get('access_token')
response = requests.get( 'https://api.connix.io/api/v1/projects', headers={'Authorization': f'Bearer {access_token}'} )
return jsonify(response.json())
// Configurationconst CLIENT_ID = 'your-client-id';const REDIRECT_URI = 'http://localhost:3000/callback.html';
// Initiate OAuth flowfunction initiateOAuth() { const state = generateRandomState(); localStorage.setItem('oauth_state', state);
const authUrl = new URL('https://console.connix.io/oauth2/authorize'); authUrl.searchParams.set('client_id', CLIENT_ID); authUrl.searchParams.set('redirect_uri', REDIRECT_URI); authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('scope', 'read write'); authUrl.searchParams.set('state', state);
window.location.href = authUrl.toString();}
// Handle callback (in callback.html)function handleCallback() { const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state');
// Verify state if (state !== localStorage.getItem('oauth_state')) { throw new Error('Invalid state parameter'); }
// Note: Token exchange should happen on your backend // This is just for demonstration exchangeCodeForToken(code);}
// Make authenticated API requestsasync function makeAuthenticatedRequest(endpoint) { const accessToken = localStorage.getItem('access_token');
const response = await fetch(`https://api.connix.io/api/v1/${endpoint}`, { headers: { 'Authorization': `Bearer ${accessToken}` } });
return response.json();}
function generateRandomState() { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);}
Token Refresh
Section titled “Token Refresh”Access tokens expire after 1 hour. Use refresh tokens to get new access tokens:
async function refreshAccessToken(refreshToken) { const response = await fetch('https://console.connix.io/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'refresh_token', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, refresh_token: refreshToken }) });
const tokens = await response.json();
// Update stored tokens localStorage.setItem('access_token', tokens.access_token); if (tokens.refresh_token) { localStorage.setItem('refresh_token', tokens.refresh_token); }
return tokens.access_token;}
// Automatic token refresh on API callsasync function makeAuthenticatedRequest(url, options = {}) { let accessToken = localStorage.getItem('access_token');
const response = await fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${accessToken}` } });
// If token expired, refresh and retry if (response.status === 401) { const refreshToken = localStorage.getItem('refresh_token'); accessToken = await refreshAccessToken(refreshToken);
// Retry request with new token return fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${accessToken}` } }); }
return response;}
import requestsfrom datetime import datetime, timedelta
class ConnixOAuthClient: def __init__(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret self.access_token = None self.refresh_token = None self.token_expires_at = None
def refresh_access_token(self): response = requests.post( 'https://console.connix.io/oauth2/token', data={ 'grant_type': 'refresh_token', 'client_id': self.client_id, 'client_secret': self.client_secret, 'refresh_token': self.refresh_token } )
tokens = response.json() self.access_token = tokens['access_token'] if 'refresh_token' in tokens: self.refresh_token = tokens['refresh_token']
# Set expiration time (typically 1 hour) expires_in = tokens.get('expires_in', 3600) self.token_expires_at = datetime.now() + timedelta(seconds=expires_in)
return self.access_token
def get_valid_token(self): # Check if token is expired or will expire soon if (not self.access_token or not self.token_expires_at or datetime.now() >= self.token_expires_at - timedelta(minutes=5)):
if self.refresh_token: return self.refresh_access_token() else: raise Exception("No valid token and no refresh token available")
return self.access_token
def make_authenticated_request(self, url, method='GET', **kwargs): token = self.get_valid_token()
headers = kwargs.get('headers', {}) headers['Authorization'] = f'Bearer {token}' kwargs['headers'] = headers
return requests.request(method, url, **kwargs)
Security Best Practices
Section titled “Security Best Practices”API Key Security
Section titled “API Key Security”- Never expose API keys in client-side code
- Use environment variables, never hardcode keys
- Rotate keys regularly (every 90 days)
- Use different keys for different environments
- Monitor key usage for unusual activity
- Revoke compromised keys immediately
OAuth2 Security
Section titled “OAuth2 Security”- Always validate the
state
parameter - Use HTTPS for all OAuth flows
- Store tokens securely (encrypted if possible)
- Implement proper token refresh logic
- Use short-lived access tokens
- Validate redirect URIs strictly
- Implement CSRF protection
Production Considerations
Section titled “Production Considerations”// Implement exponential backoff for rate limitsasync function makeRequestWithBackoff(url, options, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { const response = await fetch(url, options);
if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, i) * 1000;
console.log(`Rate limited, retrying in ${delay}ms`); await new Promise(resolve => setTimeout(resolve, delay)); continue; }
return response; }
throw new Error('Max retries exceeded');}
import requestsfrom requests.adapters import HTTPAdapterfrom requests.packages.urllib3.util.retry import Retry
class ConnixClient: def __init__(self, api_key): self.api_key = api_key self.session = self._create_session()
def _create_session(self): session = requests.Session()
# Configure retries retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter)
# Set default headers session.headers.update({ 'X-API-Key': self.api_key, 'User-Agent': 'MyApp/1.0 (connix-python-client)' })
return session
def make_request(self, method, endpoint, **kwargs): url = f"https://api.connix.io/api/v1/{endpoint}"
try: response = self.session.request(method, url, **kwargs) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise AuthenticationError("Invalid API key") elif e.response.status_code == 429: raise RateLimitError("Rate limit exceeded") else: raise APIError(f"API error: {e.response.status_code}") except requests.exceptions.RequestException as e: raise NetworkError(f"Network error: {str(e)}")
package main
import ( "context" "log" "time"
"github.com/connix-io/connix-go")
type MonitoredClient struct { client *connix.Client logger *log.Logger}
func NewMonitoredClient(apiKey string) *MonitoredClient { return &MonitoredClient{ client: connix.NewClient(apiKey), logger: log.New(os.Stdout, "[CONNIX] ", log.LstdFlags), }}
func (m *MonitoredClient) CreateProject(ctx context.Context, req *connix.CreateProjectRequest) (*connix.Project, error) { start := time.Now()
project, err := m.client.CreateProject(ctx, req)
duration := time.Since(start)
if err != nil { m.logger.Printf("CreateProject failed: %v (duration: %v)", err, duration) return nil, err }
m.logger.Printf("CreateProject succeeded: %s (duration: %v)", project.Name, duration) return project, nil}
Testing Authentication
Section titled “Testing Authentication”Testing API Keys
Section titled “Testing API Keys”# Test API keycurl -X GET https://api.connix.io/api/v1/hello?name=Test \ -H "X-API-Key: $CONNIX_API_KEY"
# Expected response:# {"message": "Hello Test! Welcome to Connix API v1"}
// Test API key validityasync function testApiKey(apiKey) { try { const response = await fetch('https://api.connix.io/api/v1/hello?name=Test', { headers: { 'X-API-Key': apiKey } });
if (response.ok) { const data = await response.json(); console.log('✅ API key valid:', data.message); return true; } else { console.log('❌ API key invalid:', response.status); return false; } } catch (error) { console.log('❌ Network error:', error.message); return false; }}
import requests
def test_api_key(api_key): response = requests.get( 'https://api.connix.io/api/v1/hello', params={'name': 'Test'}, headers={'X-API-Key': api_key} )
if response.status_code == 200: data = response.json() print(f'✅ API key valid: {data["message"]}') return True else: print(f'❌ API key invalid: {response.status_code}') return False
Testing OAuth2
Section titled “Testing OAuth2”Create a simple test application to verify your OAuth2 setup:
// Simple OAuth2 test serverconst express = require('express');const app = express();
app.get('/', (req, res) => { res.send(` <h1>Connix OAuth2 Test</h1> <a href="/auth">Sign in with Connix</a> `);});
app.get('/auth', (req, res) => { const authUrl = new URL('https://console.connix.io/oauth2/authorize'); authUrl.searchParams.set('client_id', process.env.CLIENT_ID); authUrl.searchParams.set('redirect_uri', 'http://localhost:3000/callback'); authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('scope', 'read'); authUrl.searchParams.set('state', 'test-state');
res.redirect(authUrl.toString());});
app.get('/callback', async (req, res) => { const { code, state } = req.query;
if (state !== 'test-state') { return res.status(400).send('Invalid state'); }
try { // Exchange code for token const tokenResponse = await fetch('https://console.connix.io/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, code: code, redirect_uri: 'http://localhost:3000/callback' }) });
const tokens = await tokenResponse.json();
res.json({ message: 'OAuth2 flow successful!', token_type: tokens.token_type, expires_in: tokens.expires_in, scope: tokens.scope }); } catch (error) { res.status(500).json({ error: error.message }); }});
app.listen(3000, () => { console.log('OAuth2 test server running on http://localhost:3000');});
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”Invalid API Key (401)
- Verify the API key is correct and active
- Check that you’re using the
X-API-Key
header - Ensure the key hasn’t been revoked
Rate Limiting (429)
- Implement exponential backoff
- Check rate limit headers
- Consider upgrading your plan
OAuth2 State Mismatch
- Verify the state parameter matches
- Ensure proper session management
- Check for URL encoding issues
Token Expired
- Implement automatic token refresh
- Check token expiration times
- Ensure refresh tokens are stored securely
Support
Section titled “Support”If you encounter authentication issues:
- Check the API status page
- Review your API key permissions
- Test with the hello endpoint first
- Contact engineering@connix.io
Next Steps
Section titled “Next Steps”- API Reference - Complete API documentation
- Quick Start - Get started in 5 minutes
- Building Your First Agent - Create your first agent