Authentication
Epure uses three auth mechanisms. Pick one per request — they do not stack.
What is DSN vs session vs PAT?
The DSN authenticates ingest. Dashboard /api/v1/* uses a session cookie and RLS app.current_org_id. MCP and epure-cli use a personal access token on the same /api/v1/* paths (plus /api/v1/agent/* shortcuts), gated by scopes.
| Surface | Paths | Credential | Scope |
|---|---|---|---|
| Ingest | /api/{project_id}/envelope/, /store/, /releases/…/files/, /user-feedback/ | DSN public key (sentry_key) | Project (and parent org) from the key |
| Dashboard (browser) | /api/v1/* (except public auth routes) | Session cookie | Org from session; RLS app.current_org_id |
| Automation (MCP / CLI) | /api/v1/* including /api/v1/agent/* | Authorization: Bearer epure_pat_… | read:agent, optional write:triage, write:admin |
Sending a session cookie to ingest does nothing. Sending DSN headers to /api/v1/issues returns 401. Bearer PAT on ingest returns 401.
Create PATs at Settings → Agent tokens: MCP and epure-cli · Agent API.
How does DSN ingest auth work?
Credentials come from a project DSN key. Create or rotate one in the UI, or via /api/v1/projects/{id}/dsn-keys. Copy the DSN from Settings → SDK connection.
Official Sentry SDKs use a public-key-only DSN. Epure (v0.1.5+) accepts sentry_key alone. You do not need a secret key in Sentry.init or in curl.
Preferred outside browser SDKs:
X-Sentry-Auth: Sentry sentry_version=7, sentry_key={public_key}DSN URL shape
http://{public_key}@{host}/{project_id}The path {project_id} must match the key’s project (numeric segment for official SDKs).
| Status | error | What to do |
|---|---|---|
403 | project_mismatch | Use the project id from the DSN |
403 | dsn_revoked | Create or rotate a key |
401 | invalid_dsn | Check sentry_key / header spelling. On builds older than v0.1.5, upgrade — pre-v0.1.5 rejected public-key-only auth |
CORS
Ingest routes answer OPTIONS for browser SDKs. Allowed request headers include X-Sentry-Auth, Content-Type, Content-Encoding. Origins: Configuration.
How does session auth work?
After password or Google login, the server sets an HttpOnly cookie backed by Postgres tower-sessions.
| Mode | Cookie name |
|---|---|
Secure (EPURE_SESSION_SECURE=1, default prod) | __Host-epure.sid |
Local HTTP (EPURE_SESSION_SECURE=0) | Epure.sid |
Attributes: HttpOnly, SameSite=Lax, Path=/, inactivity expiry 14 days.
No bearer tokens in localStorage.
Login
curl -sS -c cookies.txt -D - -o /dev/null -X POST \
"http://localhost:8080/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"your-password"}'| Result | Meaning | What to do |
|---|---|---|
204 + Set-Cookie | Session established | Reuse -b cookies.txt on /api/v1/* |
| 401 | Bad email/password | Register, or ./scripts/seed.sh --email … --password … |
403 on POST /register | EPURE_REGISTRATION is closed | Sign in, or register with an invite_token. Configuration |
| 429 plain text | 20 req / IP / 60s | Wait |
Public /api/v1/auth routes
| Method | Path | Notes |
|---|---|---|
GET | /api/v1/auth/config | { google_enabled, password_enabled, registration_enabled } |
POST | /api/v1/auth/register | email + password (+ optional invite_token). 403 when registration is closed and there is no invite |
POST | /api/v1/auth/login | email + password → 204 |
GET | /api/v1/auth/google | OIDC start |
GET | /api/v1/auth/google/callback | OIDC callback |
GET | /api/v1/auth/me | current session (requires cookie) |
POST | /api/v1/auth/logout | clear session |
POST | /api/v1/auth/accept-invitation | { invite_token } |
PATCH | /api/v1/auth/profile | { display_name } |
PATCH | /api/v1/auth/password | change password |
DELETE | /api/v1/auth/account | delete account |
Login body:
{ "email": "you@example.com", "password": "your-password", "invite_token": null }Login succeeds but later /api/v1 calls are 401?
The browser rejected the cookie over an HTTPS vs EPURE_SESSION_SECURE mismatch. Local HTTP needs 0 (Epure.sid); HTTPS needs 1 (__Host-epure.sid). DSN headers on this path also return 401, so use the cookie. See Troubleshooting.
Can I use the DSN as a bearer token?
No, and there are no bearer tokens in localStorage either. The DSN covers ingest only: envelope, store, release files, user feedback. Issues and Events need the login cookie. The tables above list which header belongs on which path.
Do I need a DSN secret key?
No for official Sentry SDKs. Sentry treats the secret as deprecated; modern clients send only sentry_key. Epure v0.1.5+ matches that. Paste the public-key DSN from Settings → SDK connection into Sentry.init.