[AIDE-104] Authentication - #82
Merged
Merged
Conversation
…e trusted origins - Derive the credential database encryption key from APP_SECRET, required in all environments, so database storage is always encrypted; support rotation via APP_SECRET_PREVIOUS with re-encryption at startup - Consolidate the server origin allowlist (BETTER_AUTH_URL, ALLOWED_DEV_ORIGINS) into src/lib/app-origins.ts, resolving origins per request and validating wildcard entries - Complete mobile OAuth on the trusted origin the device actually reached instead of bouncing to the canonical origin
…uest Replace the hard production error for missing APP_ORIGINS with an inferred mode: when neither APP_ORIGINS nor PUBLIC_BASE_URL is set, the server trusts the host each request arrived on and derives canonical links from it instead of failing at startup. This restores the behavior of unconfigured deployments while keeping the CSRF check intact, since browsers set Host to the real destination. - AppOrigins gains an "inferred" mode with a null canonical origin; isTrustedOrigin accepts any host-shaped value in that mode - OAuth start falls back to the request's own origin when no base URL is configured, so mobile sign-in completes on the host the device actually reached - Emit a one-time warning explaining how to pin origins back down - Update auth runtime config and tests accordingly
Evaluate trustedOrigins per request only when the allowlist is unconfigured, so a forged Host header can no longer add a foreign origin to the trusted set. Document the security implications in .env.example.
State an explicit cross-origin rule for endpoints authenticated by the session cookie alone (auth management, GraphQL, tool calls) instead of relying on SameSite's browser default, reject cross-site WebSocket handshakes at the server, and bind the mobile OAuth completion leg to a single-use state cookie so an ambient session cannot be traded for a one-time token by a top-level cross-site navigation. Also derive credential encryption from APP_SECRET with APP_SECRET_PREVIOUS rotation, batch startup encryption and rotation into transactions that stay inside SQLite's timeout, keep registration-status reads read-only, and drop admin endpoints that would silently no-op.
Add AUTH_OAUTH_REQUIRE_ISSUER_VALIDATION to control OAuth issuer validation instead of hardcoding it, defaulting to false. Update .env.example docs and the standalone credentials smoke test to use APP_SECRET/APP_ORIGINS, and cover the new setting with tests.
Resolve sessions server-side in a new auth layout instead of optimistically redirecting session cookies in the proxy, so revoked or stale cookies no longer send users to the wrong page. The mobile OAuth start route now builds its completion callback from the trusted forwarded origin, and managed user deletion runs as one conditional SQL statement so concurrent deletes can never empty the user table. Add tests for the layout, OAuth start, and key management, and drop Better Auth's non-atomic removal endpoint.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds authentication to the control plane with Better Auth, replacing the previous no-auth model. Users sign in with email/password and optionally a generic OIDC provider; the first account bootstraps the instance and registration is locked down afterwards. Every request is now resolved to a principal (user session, API key, agent credential, or anonymous) that guards the dashboard, the GraphQL API, the agent WebSocket, and the tools/MCP endpoints. Secret handling is unified around a single
APP_SECRETroot, and database-stored credentials are now always encrypted with support for key rotation.Changes
src/services/auth/): lazy singleton auth instance over Prisma with email/password, optional generic OIDC OAuth, admin plugin with roles, API keys (aide_prefix), bearer sessions, one-time tokens, and next-cookies.registration.ts): the first registered user claims a short-lived TTL slot; afterwards registration stays disabled until an admin re-enables it via the users page.principal.ts):resolveRequestPrincipalaccepts exactly one credential (session cookie,Bearer, orX-API-Key) and returns a typed principal, rejecting mixed credentials.enrollAgentmutation.authorizeToolRequestnow requires a user session or API key instead of the sharedTOOLS_API_TOKEN.CREDENTIAL_ENCRYPTION_KEYis gone; the key is derived fromAPP_SECRET, there is no plaintext mode, legacy plaintext rows are swept atomically at startup, and rows sealed underAPP_SECRET_PREVIOUSkeys are re-encrypted on start.app-secret.ts): Better Auth secret, credential encryption key, and OTA artifact-token secret are all HKDF-derived fromAPP_SECRET; build-time uses an ephemeral placeholder that cannot touch stored data. OTA links now survive restarts.app-origins.ts): oneAPP_ORIGINSlist now drives CSRF trust, Better Auth base URL/trusted origins, and the Next.js dev-server allowlist, with per-request resolution, wildcards allowed only outside production, and an inferred mode when nothing is configured./sign-in(preservingreturnTo); dashboard routes moved into a(dashboard)group with a server-side session check; new sign-in/register pages and sharedAuthForm; the app shell shows the signed-in user with sign-out./api/auth/management/*REST endpoints.start/complete/redeemendpoints let native apps sign in through the browser and recover a session token via anaide-auth://deep link.User,Session,Account,Verification,ApiKey, andAuthSettingsmodels plus migrations (including cascading API-key deletes).Ticket: AIDE-104