Authentication
GLACIS uses API key authentication for management operations and bearer tokens for attestation operations.
API Keys
API keys are used for all dashboard and management API operations.
Creating API Keys
- Log into the GLACIS Dashboard
- Navigate to Settings → API Keys
- Click Create API Key
- Copy the key (you won’t be able to see it again)
API keys have the format: glc_live_... or glc_test_...
Using API Keys
Include the API key in the Authorization header:
curl https://api.glacis.io/api/v1/orgs/org_xyz/controls \ -H "Authorization: Bearer glc_live_abc123..."const response = await fetch('https://api.glacis.io/api/v1/orgs/org_xyz/controls', { headers: { 'Authorization': 'Bearer glc_live_abc123...' }});import requests
response = requests.get( 'https://api.glacis.io/api/v1/orgs/org_xyz/controls', headers={'Authorization': 'Bearer glc_live_abc123...'})API Key Scopes
| Scope | Description | Endpoints |
|---|---|---|
read:controls | Read control library | GET /controls |
write:evidence | Manage evidence | POST/PUT /evidence |
read:compliance | View compliance scores | GET /compliance |
write:attestations | Submit attestations | POST /attestations |
admin | Full access | All endpoints |
Bearer Tokens
Bearer tokens are epoch-bound tokens used by sidecars for attestation submission.
Obtaining Bearer Tokens
Sidecars obtain bearer tokens from the witness service:
const response = await fetch('https://witness.glacis.io/api/v1/s3p/heartbeat', { method: 'POST', headers: { 'Authorization': 'Bearer glc_live_abc123...', 'Content-Type': 'application/json' }, body: JSON.stringify({ sidecarId: 'sidecar_abc123', organizationId: 'org_xyz789' })});
const { bearerToken, epochId, expiresAt } = await response.json();Using Bearer Tokens
Bearer tokens are used for attestation submission:
const response = await fetch('https://receipts.glacis.io/api/v1/attestations', { method: 'POST', headers: { 'Authorization': 'Bearer wt_abc123...', // Bearer token, NOT API key 'Content-Type': 'application/json' }, body: JSON.stringify({ epochId: 'epoch_2024010112', level: 'L0', // ... attestation data })});Token Expiration
Bearer tokens expire with their epoch (default: 1 hour). Handle expiration gracefully:
class TokenManager { private token: string | null = null; private expiresAt: number = 0;
async getToken(): Promise<string> { // Refresh 5 minutes before expiration if (!this.token || Date.now() > this.expiresAt - 300000) { const { bearerToken, expiresAt } = await this.refreshToken(); this.token = bearerToken; this.expiresAt = expiresAt; } return this.token; }
private async refreshToken() { const response = await fetch('https://witness.glacis.io/api/v1/s3p/heartbeat', { method: 'POST', headers: { 'Authorization': 'Bearer ' + process.env.GLACIS_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ sidecarId: process.env.SIDECAR_ID, organizationId: process.env.GLACIS_ORG_ID }) }); return response.json(); }}Session Authentication
The dashboard uses session-based authentication for browser access.
Login
const response = await fetch('https://app.glacis.io/api/v1/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'user@example.com', password: 'secure_password' }), credentials: 'include' // Important for session cookie});Session Cookies
After login, the session cookie is automatically included in subsequent requests:
- Cookie name:
glacis_session - HttpOnly: Yes
- Secure: Yes (HTTPS only)
- SameSite: Strict
Webhook Signatures
GLACIS signs webhook payloads for verification:
import { createHmac } from 'crypto';
function verifyWebhookSignature( payload: string, signature: string, secret: string): boolean { const expected = createHmac('sha256', secret) .update(payload) .digest('hex');
return signature === `sha256=${expected}`;}
// In your webhook handlerapp.post('/webhooks/glacis', (req, res) => { const signature = req.headers['x-glacis-signature']; const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); }
// Process webhook...});Rate Limits
| Endpoint Type | Rate Limit | Window |
|---|---|---|
| Management API | 1,000 req/min | Per API key |
| Attestation API | 10,000 req/min | Per organization |
| Witness Heartbeat | 100 req/min | Per sidecar |
| Dashboard API | 100 req/min | Per session |
Rate limit headers are included in responses:
X-RateLimit-Limit: 1000X-RateLimit-Remaining: 847X-RateLimit-Reset: 1704106860Error Responses
Authentication errors return standard HTTP status codes:
| Status | Error | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid credentials |
| 403 | FORBIDDEN | Valid credentials, insufficient scope |
| 429 | RATE_LIMITED | Too many requests |
{ "error": { "code": "UNAUTHORIZED", "message": "Invalid API key", "details": { "hint": "Check that your API key is correct and not expired" } }}Security Best Practices
- Rotate keys regularly: Create new keys and deprecate old ones quarterly
- Use minimal scopes: Only request the permissions you need
- Monitor usage: Review API key activity in the dashboard
- Environment variables: Never hardcode keys in source code
- Separate environments: Use different keys for test and production