API authentication
Pentestas accepts four credential types across the HTTP + WebSocket surfaces.
At a glance
| Credential | Prefix | Used by | Scope |
|---|---|---|---|
| JWT (Bearer) | eyJ⦠|
Browser + mobile clients | Logged-in user in their tenant |
| User API key | aa_β¦ |
CI, scripts, CLI tools | Same as user JWT β tenant + permissions |
| Agent key | pa_β¦ |
Deployed agents | Tenant β no user, no API surface |
| OAuth cookie | pt=β¦ |
Browser only | Same as JWT; HttpOnly cookie |
JWT
Issued by POST /api/auth/login or OAuth callback. Bearer-style Authorization: Bearer <JWT> header, or stored as the pt HttpOnly cookie by browser clients.
- Signing:
HS256with a server-wide 32-byte secret. - Lifetime: 24h default, 7d "remember me".
- Contains:
sub(user ID),tenant_id,role,email,exp.
JWTs cannot be revoked server-side short of rotating the secret. Use API keys for long-lived integrations where you need revocation.
User API keys (aa_β¦)
Generate at /api/auth/api-key (or in Settings β API keys). Opaque random string. Send as either:
Authorization: Bearer aa_...
or
X-API-Key: aa_...
Both work on every HTTP route. API keys are:
- Immediately revocable (delete β authentication fails within one request).
- Audit-logged on every use.
- Tied to the user who created them β permissions inherit.
- Hashed at rest (we store a bcrypt hash, not the key itself).
Creating
curl -X POST https://app.pentestas.com/api/auth/api-key \
-H "Authorization: Bearer <your_jwt>" \
-d '{"name":"CI pipeline"}'
Response shows the key exactly once. Save it immediately; you can't recover it.
Rotating
Create a new key, update your integration, delete the old key. Pentestas stores the creation time per key so you can see ordering.
Agent keys (pa_β¦)
Issued when you create a NetworkAgent in Settings β Agents. Used only over the agent WebSocket at /api/agents/ws/{agent_key}. Cannot be used for HTTP API calls β they grant exactly one capability: connect as this agent.
- Locked to IP allowlist (admin-configurable).
- Disable / re-enable / delete from the UI or
PUT /api/agents/{id}. - Stored in DPAPI (Windows) or chmod 0600 env file (Linux) on the agent host.
OAuth (Google, Microsoft)
For browser sign-in only. Completes with a redirect to the app + set-cookie. Doesn't issue API keys directly; use the app's Settings β API keys to mint one after signing in.
Rate limits
| Endpoint group | Limit |
|---|---|
| Auth endpoints (login, signup, password reset) | 5 / min per IP |
| Scan endpoints | 30 / s per IP (burst 60) |
| Public read-only endpoints (subdomain, cloudscan anonymous) | 10β30 / hour per IP |
| Agent WebSocket | 1 open connection per agent_key; 5 failed handshakes / hour blocks the IP |
Tenant scoping
Every authenticated request is scoped to the caller's tenant. You cannot read scans / findings / agents belonging to a different tenant β the API returns 404 (not 403, to avoid leaking existence). Row-Level Security policies in Postgres provide defence in depth.