Security & Auth
NeuralRepo is designed with security at every layer. This page covers authentication, authorization, encryption, and data isolation.
OAuth Authentication
Section titled “OAuth Authentication”NeuralRepo supports four sign-in methods, all implemented using the Arctic OAuth library:
| Provider | Protocol | Library |
|---|---|---|
| GitHub | OAuth 2.0 | Arctic |
| OpenID Connect | Arctic | |
| Apple | Sign In with Apple (OAuth 2.0) | Custom (Apple requires server-side JWT) |
| Magic Link | Email-based | Custom |
OAuth Flow
Section titled “OAuth Flow”- User clicks “Sign in with GitHub” (or Google/Apple).
- The server generates a state parameter and redirects to the provider.
- The provider authenticates the user and redirects back with an authorization code.
- The server exchanges the code for an access token and retrieves the user profile.
- If the email matches an existing user, the account is linked. Otherwise, a new user is created.
- A session is created and returned as an HttpOnly cookie.
Magic Links
Section titled “Magic Links”For users who prefer email-based auth:
- User enters their email address.
- The server generates a one-time token, stores its SHA-256 hash in the
magic_linkstable, and sends the token via email. - The user clicks the link, which contains the token.
- The server hashes the token, looks up the matching record, validates expiry (15 minutes), and creates a session.
- The magic link record is deleted after use.
Session Management
Section titled “Session Management”Sessions are the primary authentication mechanism for the web app.
| Property | Value |
|---|---|
| Token format | 32 random bytes, hex-encoded (64 characters) |
| Storage | SHA-256 hash stored in D1 sessions table |
| Expiry | 30 days from creation |
| Cookie flags | HttpOnly, Secure, SameSite=Lax, Path=/ |
| Auto-refresh | Sessions within 7 days of expiry are extended on active use |
The raw session token is only ever sent to the client as a cookie. The server stores and compares only the SHA-256 hash. This means a database breach does not expose usable session tokens.
API Keys
Section titled “API Keys”API keys provide programmatic access for the CLI, Siri Shortcuts, CI/CD, and custom integrations.
| Property | Value |
|---|---|
| Format | nrp_ prefix + 32 random bytes hex-encoded (64 hex chars) |
| Storage | SHA-256 hash stored in D1 api_keys table |
| Display | Only the last 4 characters are shown after creation |
| Revocation | Immediate — delete the key record |
When a request includes an X-API-Key header:
- The server hashes the provided key with SHA-256.
- It looks up the hash in the
api_keystable. - If found, the request is authenticated as the key’s owner.
- The
last_used_attimestamp is updated.
The full key is shown only once at creation time. It cannot be retrieved later.
BYOK Encryption
Section titled “BYOK Encryption”User-provided AI keys (Anthropic, OpenAI, OpenRouter) are encrypted before storage:
| Property | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key source | ENCRYPTION_KEY environment variable (Workers secret) |
| IV | Random 12-byte IV generated per encryption |
| Storage | Base64-encoded {iv}:{ciphertext}:{authTag} in D1 |
The encryption key is a Workers secret that never appears in code or logs. Decryption happens in-memory only when making an AI request, and the plaintext key is never written to disk or returned via API.
MCP OAuth with PKCE
Section titled “MCP OAuth with PKCE”The MCP integration uses a full OAuth 2.0 with PKCE flow to authorize Claude’s access:
| Property | Value |
|---|---|
| Grant type | Authorization Code with PKCE |
| Challenge method | S256 (SHA-256) |
| Authorization code expiry | 10 minutes |
| Access token expiry | 1 hour |
| Refresh token expiry | 90 days |
| Scopes | ideas:read, ideas:write |
The PKCE flow prevents authorization code interception attacks. The MCP client generates a random code_verifier, hashes it to create a code_challenge, and sends the challenge with the authorization request. When exchanging the code for tokens, the server verifies the original verifier matches the stored challenge.
Refresh tokens are stored as SHA-256 hashes in the oauth_refresh_tokens table.
CORS Middleware
Section titled “CORS Middleware”The API uses strict CORS configuration:
| Header | Value |
|---|---|
Access-Control-Allow-Origin | https://neuralrepo.com (production) |
Access-Control-Allow-Methods | GET, POST, PATCH, PUT, DELETE, OPTIONS |
Access-Control-Allow-Headers | Content-Type, Authorization, X-API-Key |
Access-Control-Allow-Credentials | true |
Access-Control-Max-Age | 86400 (24 hours) |
The origin is restricted to the NeuralRepo web app domain. API key-authenticated requests (CLI, Siri, CI) do not use cookies and are not subject to CORS.
Rate Limiting
Section titled “Rate Limiting”Rate limits are enforced using Cloudflare KV as a counter store:
| Endpoint Type | Limit |
|---|---|
| General API | 60 requests/minute/user |
| Search | 30 requests/minute/user |
| Auth (login, magic link) | 10 requests/minute/IP |
| MCP tools | 60 requests/minute/user |
When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.
KV’s global replication ensures rate limits are consistent across edge locations. The counter key format is rate:{user_id}:{endpoint_group}:{minute_bucket} with a 120-second TTL.
Data Isolation
Section titled “Data Isolation”Every database query in NeuralRepo is scoped by user_id:
SELECT * FROM ideas WHERE user_id = ? AND deleted_at IS NULLThere are no admin super-queries that bypass this filter in the production API. Each user’s data is completely isolated — you cannot access, search, or modify another user’s ideas regardless of the authentication method.