Repository navigation
feat(auth): OAuth sign-in for API and MCP clients - #18
Merged
Merged
Conversation
Lens is an OAuth 2.1 authorization server: apps register themselves (RFC 7591), send people to the consent page with a PKCE challenge (S256, required) and swap the code for tokens that act as the person, with their roles, read only or read and write. Discovery is at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource, on the web app's address too. Tokens are random and kept as hashes; refresh tokens rotate, and one that comes back after it was swapped ends the access. People see the apps they allowed under API tokens and revoke them there. API: /api/v1/oauth/register, authorize (GET and POST), token, revoke, grants; tables oauth_client, oauth_code, oauth_grant, oauth_token; the bearer path takes lo_ tokens; settings tokens.oauth_access_minutes and tokens.oauth_refresh_days; audited as oauth.client.register, oauth.grant and oauth.revoke. An admin's app has the admin's roles in every namespace but not the administration. After a security review: redirect addresses with a user name or a backslash are refused (a loopback match could otherwise be steered to another host), as are the browser's and the system's own schemes; discovery names the web app's address only as a trusted proxy reports it, else FRONTEND_URL; tokens of a revoked grant are dead even if minted during the revoke. Web app: /oauth/authorize (consent, signing in first), Apps with access on the API tokens page, the two lifetimes in Settings, and the proxy passes the browser's Host on. Checked in the browser as a viewer and an admin, in dark mode and at 390 px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zJqF1Ht8p2VjBi4j11jfg
adeelahmad
force-pushed
the
claude/project-thread-icgrqy
branch
from
October 2, 2026 12:57
e884d13 to
26f13ea
Compare
…ming A code that comes back after it was swapped ends the access it gave, and a rotated refresh token that comes back within the grace minute is refused instead of minting another pair. Expired codes are swept when a new one is made. A 401 that asks for a bearer token names the protected resource metadata (RFC 9728), as MCP clients expect. The consent page can't be framed. The web app passes a browser's own X-Forwarded-Host and -Proto on to the API only with TRUST_PROXY_HEADERS=true; otherwise it reports Host. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HFiySoDnqG9PX5c3WLPz5C
adeelahmad
force-pushed
the
claude/project-thread-icgrqy
branch
from
October 2, 2026 12:57
26f13ea to
bc57ea2
Compare
Regenerated openapi.json and the API client from the merged backend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SNbRrpHTCqLewRKdkmd45X
adeelahmad
marked this pull request as ready for review
October 2, 2026 13:31
This was referenced Oct 2, 2026
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.
Requested by Adeel · project thread
Description
Before: apps and MCP clients could only reach the Lens API with a hand-made
la_…API key.After: Lens is an OAuth 2.1 authorization server. An app registers itself (RFC 7591), sends the person to the web app's consent page with a PKCE S256 challenge, and swaps the code for tokens that act as that person with their roles, read only or read and write. People see and revoke the apps they allowed under API tokens.
How:
app/domain/oauth.pyhandles clients, one-time codes, grants and rotating refresh tokens, and stores every token as a hash.app/api/v1/routes/oauth.pyserves/api/v1/oauth/register,authorize,token,revokeandgrants, plus/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource. The bearer path acceptslo_tokens. An admin's app gets the admin's namespace roles but not the administration. New settings:tokens.oauth_access_minutesandtokens.oauth_refresh_days. Web app: the/oauth/authorizeconsent page, "Apps with access", the settings fields and a/.well-knownproxy.Review fixes (second commit):
resource_metadata=(RFC 9728)./oauth/*sendsframe-ancestors 'none'andX-Frame-Options: DENY.X-Forwarded-Host/-Protoonly withTRUST_PROXY_HEADERS=true, and usesHostotherwise.The Docker fixes commit that sat on the Mac session's branch is not here; it goes in #15.
Motivation and Context
Backlog item #45. The MCP server (#44) needs this first, because its clients expect OAuth discovery.
Steps to reproduce (if appropriate):
cd fastapi_backend && uv run pytest tests/api/test_oauth.py;cd nextjs-frontend && pnpm exec jest __tests__/oauth-* __tests__/backend-proxy.test.ts.Checked in a cloud container:
next buildpasses, andnext startserves the frame headers on/oauth/authorize.test_conversion.py::test_office_and_text_files_become_documents, which fails on main in that container too because of how LibreOffice is installed there.Types of changes
Checklist:
🤖 Generated with Claude Code
https://claude.ai/code/session_01HFiySoDnqG9PX5c3WLPz5C