API Authentication
NopeSight API supports multiple authentication methods to ensure secure access to your data. This guide covers all available authentication options and best practices.
Authentication Methods
API Key Authentication
The simplest method for server-to-server communication.
# Using API Key in header
curl -X GET https://api.nopesight.com/v1/cmdb/servers \
-H "X-API-Key: your-api-key-here"
# Using API Key in query parameter (not recommended)
curl -X GET https://api.nopesight.com/v1/cmdb/servers?api_key=your-api-key-here
Generating API Keys
// Generate API key via API
const response = await fetch('https://api.nopesight.com/v1/auth/api-keys', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + userToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Integration Key',
description: 'Key for automated scripts',
scopes: ['cmdb.read', 'cmdb.write', 'reports.read'],
expiresIn: '90d'
})
});
const { apiKey } = await response.json();
// Returns: { apiKey: 'nsa_k_1234567890abcdef...', id: 'key_id', expiresAt: '2024-04-20T00:00:00Z' }
JWT Bearer Token
For user-based authentication and web applications.
// Login to get JWT token
const loginResponse = await fetch('https://api.nopesight.com/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'user@company.com',
password: 'secure-password'
})
});
const { token, refreshToken } = await loginResponse.json();
// Use token in subsequent requests
const dataResponse = await fetch('https://api.nopesight.com/v1/cmdb/servers', {
headers: {
'Authorization': 'Bearer ' + token
}
});
Token Refresh
// Refresh expired token
const refreshResponse = await fetch('https://api.nopesight.com/v1/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
refreshToken: savedRefreshToken
})
});
const { token, refreshToken: newRefreshToken } = await refreshResponse.json();
OAuth 2.0
For third-party integrations and SSO.
// OAuth 2.0 Authorization Code Flow
// Step 1: Redirect user to authorization endpoint
const authUrl = new URL('https://auth.nopesight.com/oauth/authorize');
authUrl.searchParams.append('client_id', 'your-client-id');
authUrl.searchParams.append('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', 'cmdb.read reports.read');
authUrl.searchParams.append('state', 'random-state-string');
window.location.href = authUrl.toString();
// Step 2: Exchange authorization code for token
const tokenResponse = await fetch('https://auth.nopesight.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: 'https://yourapp.com/callback',
client_id: 'your-client-id',
client_secret: 'your-client-secret'
})
});
const { access_token, refresh_token, expires_in } = await tokenResponse.json();
Service Account Authentication
For automated systems and CI/CD pipelines.
import jwt
import requests
from datetime import datetime, timedelta
class ServiceAccountAuth:
def __init__(self, account_id, private_key_path):
self.account_id = account_id
self.private_key = self.load_private_key(private_key_path)
self.token = None
self.token_expires = None
def get_token(self):
"""Get or refresh service account token"""
if self.token and self.token_expires > datetime.utcnow():
return self.token
# Create JWT assertion
now = datetime.utcnow()
claims = {
'iss': self.account_id,
'sub': self.account_id,
'aud': 'https://api.nopesight.com',
'iat': now,
'exp': now + timedelta(hours=1)
}
assertion = jwt.encode(claims, self.private_key, algorithm='RS256')
# Exchange assertion for access token
response = requests.post(
'https://api.nopesight.com/v1/auth/service-account/token',
data={
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': assertion
}
)
token_data = response.json()
self.token = token_data['access_token']
self.token_expires = now + timedelta(seconds=token_data['expires_in'])
return self.token
Security Headers
Required Headers
# All authenticated requests must include
Authorization: Bearer <token>
# OR
X-API-Key: <api-key>
# Additional security headers
X-Request-ID: <unique-request-id>
X-Client-Version: 1.0.0
CORS Configuration
// CORS headers for browser-based applications
const corsHeaders = {
'Access-Control-Allow-Origin': 'https://app.nopesight.com',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Authorization, Content-Type, X-API-Key',
'Access-Control-Max-Age': '86400'
};
Rate Limiting
Rate Limit Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
X-RateLimit-Reset-After: 3600
Handling Rate Limits
import time
import requests
def make_api_request_with_retry(url, headers):
"""Make API request with automatic retry on rate limit"""
max_retries = 3
retry_count = 0
while retry_count < max_retries:
response = requests.get(url, headers=headers)
if response.status_code == 429: # Too Many Requests
# Get retry after time
retry_after = int(response.headers.get('X-RateLimit-Reset-After', 60))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
retry_count += 1
continue
return response
raise Exception("Max retries exceeded")
API Scopes
Available Scopes
scopes:
# CMDB Scopes
cmdb.read: Read configuration items
cmdb.write: Create and update configuration items
cmdb.delete: Delete configuration items
# Discovery Scopes
discovery.read: View discovery results
discovery.execute: Run discovery scans
discovery.configure: Manage discovery settings
# Reports Scopes
reports.read: View reports
reports.create: Create custom reports
reports.export: Export report data
# Admin Scopes
admin.users: Manage users
admin.settings: Manage system settings
admin.audit: View audit logs
Scope-based Authorization
// Check if token has required scope
function hasScope(token, requiredScope) {
const decoded = jwt.decode(token);
return decoded.scopes && decoded.scopes.includes(requiredScope);
}
// Middleware to check scopes
function requireScope(scope) {
return (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token || !hasScope(token, scope)) {
return res.status(403).json({
error: 'Insufficient permissions',
required_scope: scope
});
}
next();
};
}
// Usage
app.get('/api/v1/admin/users', requireScope('admin.users'), (req, res) => {
// Handle request
});
Multi-Factor Authentication
TOTP Setup
import pyotp
import qrcode
def setup_totp(user_email):
"""Set up TOTP for user"""
# Generate secret
secret = pyotp.random_base32()
# Create TOTP URI
totp_uri = pyotp.totp.TOTP(secret).provisioning_uri(
name=user_email,
issuer_name='NopeSight'
)
# Generate QR code
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(totp_uri)
qr.make(fit=True)
return {
'secret': secret,
'qr_code': qr,
'manual_entry_key': secret
}
def verify_totp(secret, token):
"""Verify TOTP token"""
totp = pyotp.TOTP(secret)
return totp.verify(token, valid_window=1)
Using MFA with API
// Login with MFA
const loginResponse = await fetch('https://api.nopesight.com/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'user@company.com',
password: 'secure-password',
totp_code: '123456' // 6-digit TOTP code
})
});
Best Practices
1. Token Security
// Store tokens securely
// Bad: localStorage.setItem('token', token);
// Good: Use secure, httpOnly cookies or encrypted storage
// Set short expiration times
const tokenConfig = {
accessTokenExpiry: '15m',
refreshTokenExpiry: '7d',
requireTokenRotation: true
};
2. API Key Management
api_key_policy:
rotation_period: 90_days
key_format: "nsa_[env]_[random]"
minimum_length: 32
allowed_characters: "[a-zA-Z0-9]"
restrictions:
- One key per application
- Separate keys for environments
- IP whitelisting when possible
- Scope limitations
3. Secure Communication
# Enforce HTTPS
server {
listen 443 ssl http2;
# Strong SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers on;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
4. Error Handling
// Don't expose sensitive information in errors
// Bad
{
"error": "Invalid token: eyJhbGciOiJIUzI1NiIs...",
"user": "admin@company.com"
}
// Good
{
"error": "Authentication failed",
"code": "AUTH_FAILED",
"request_id": "req_123456"
}
Troubleshooting
Common Authentication Issues
issues:
invalid_token:
error: "401 Unauthorized: Invalid token"
causes:
- Token expired
- Token malformed
- Wrong environment
solutions:
- Refresh token
- Check token format
- Verify API endpoint
insufficient_permissions:
error: "403 Forbidden: Insufficient permissions"
causes:
- Missing required scope
- Resource access denied
- Account disabled
solutions:
- Check token scopes
- Verify resource permissions
- Contact administrator
rate_limited:
error: "429 Too Many Requests"
causes:
- Exceeded rate limit
- Burst limit reached
solutions:
- Implement exponential backoff
- Cache responses
- Use pagination
Code Examples
Python Client
import requests
from urllib.parse import urljoin
class NopeSightClient:
def __init__(self, api_key=None, token=None, base_url='https://api.nopesight.com/v1/'):
self.base_url = base_url
self.session = requests.Session()
if api_key:
self.session.headers['X-API-Key'] = api_key
elif token:
self.session.headers['Authorization'] = f'Bearer {token}'
else:
raise ValueError("Either api_key or token must be provided")
def get(self, endpoint, **kwargs):
url = urljoin(self.base_url, endpoint)
response = self.session.get(url, **kwargs)
response.raise_for_status()
return response.json()
def post(self, endpoint, data=None, **kwargs):
url = urljoin(self.base_url, endpoint)
response = self.session.post(url, json=data, **kwargs)
response.raise_for_status()
return response.json()
# Usage
client = NopeSightClient(api_key='your-api-key')
servers = client.get('cmdb/servers')
Node.js Client
const axios = require('axios');
class NopeSightClient {
constructor({ apiKey, token, baseURL = 'https://api.nopesight.com/v1/' }) {
this.client = axios.create({
baseURL,
headers: {
...(apiKey && { 'X-API-Key': apiKey }),
...(token && { 'Authorization': `Bearer ${token}` })
}
});
// Add request/response interceptors
this.setupInterceptors();
}
setupInterceptors() {
// Request interceptor
this.client.interceptors.request.use(
config => {
config.headers['X-Request-ID'] = generateRequestId();
return config;
},
error => Promise.reject(error)
);
// Response interceptor
this.client.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// Handle token refresh
return this.refreshTokenAndRetry(error.config);
}
return Promise.reject(error);
}
);
}
async get(endpoint, params) {
return this.client.get(endpoint, { params });
}
async post(endpoint, data) {
return this.client.post(endpoint, data);
}
}
// Usage
const client = new NopeSightClient({ apiKey: 'your-api-key' });
const servers = await client.get('cmdb/servers');