Skip to content

Authentication

PromptGate has two distinct auth surfaces that don’t intersect:

  • Web UI — session-based login. One admin account in Community Edition.
  • Public API — Bearer tokens (pg_live_… or pg_test_…) with scopes. Per-project.

Navigate to /login and submit email + password.

Email: admin@promptgate.dev
Password: admin

The seeder creates this account on first migrate. Change the password immediately via top-right user menu → Profile.

The login endpoint is rate-limited to prevent brute-force:

  • 5 attempts per email per minute.
  • Subsequent attempts return 429 + Retry-After.
  • Successful login resets the counter.

Failed attempts are logged in audit_logs (event: auth.login_failed).

After successful login:

  • Laravel creates a server-side session.
  • Storage driver is SESSION_DRIVER (default: database; recommend redis in prod).
  • Idle lifetime: SESSION_LIFETIME (default 120 min).
  • Cookie is HttpOnly and SameSite=Lax. Secure is added when serving over HTTPS.
  • CSRF is enforced on every state-changing form by Laravel’s middleware.

Top-right user menu → Logout. Invalidates the session server-side.

In Community Edition, there’s no password-reset email flow (single user, you typically have console access). To reset:

Terminal window
docker compose exec app php artisan tinker
\App\Models\User::query()->where('email', 'admin@promptgate.dev')
->update(['password' => Hash::make('your-new-password')]);

Community Edition is single-user — one admin account, full access to everything. The login system technically supports multiple users (the users table is normal), but no UI for invitations, no roles, no permissions. Multi-user / RBAC / SSO are Cloud Edition features (see Editions).

Public API routes (/api/{uuid}/..., /api/control/mcp) use Bearer tokens.

Authorization: Bearer pg_live_abcdef01234567...

Tokens are project-scoped and carry scopes that gate which routes they can hit:

ScopeRoutes
chatPOST /api/{uuid}/{slug} (AI Gateway), POST /api/{uuid}/v1/chat/completions (Wrapper)
modelsGET /api/{uuid}/models
adminGET /api/{uuid}/info, /endpoints, /tokens, POST /api/control/mcp
proxyANY /api/{uuid}/proxy/{slug}/{any?} (API Gateway)
mcpPOST /api/{uuid}/mcp (Bridge or Gateway)

A token can have multiple scopes. A single token can be e.g. [chat, models] for an app that needs both.

Issuing, rotating, and revoking tokens is documented in Client Tokens.

SurfaceStorageWhat’s stored
Web loginLaravel’s bcryptusers.password is bcrypt with BCRYPT_ROUNDS cost (12 in prod).
API tokensSHA-256api_tokens.token_hash is the SHA-256 of the plaintext. Plaintext shown once at create / rotate, never again.

Web sessions store an opaque session ID server-side. The cookie just carries the session ID; the session payload (user ID, CSRF token, etc.) lives in the session store.

Behind a reverse proxy that terminates TLS, the Laravel trustProxies middleware (set to '*' in bootstrap/app.php) honours X-Forwarded-Proto. PromptGate generates https://… URLs in OAuth callbacks, MCP bridge URLs, and curl examples whenever APP_URL is https://….

If you don’t configure HTTPS, don’t ship a production gateway. Bearer tokens transit in headers; without TLS, network observers see them.

  • Change the default password before exposing the gateway anywhere.
  • HTTPS only in production.
  • Redis sessions in prod for performance and session-store consistency across replicas.
  • Short SESSION_LIFETIME if the UI is exposed beyond your VPN (e.g. 30 min).
  • Audit the audit log weekly — the auth.login_failed events are an early signal for brute-force attempts that slipped through rate limits.
  • Token rotation — rotate API tokens on a cadence (every 90 days is a fine starting point).

Next: Client Tokens.


© Akyros Labs LLC. All rights reserved.