Merged
Conversation
Design document for migrating from session-based auth to industry-standard OAuth2 refresh/access token pattern. Key changes: - Short-lived access tokens (15 min) validated by signature only - Long-lived refresh tokens (30 days) stored in user_setting - Zero DB queries for normal authenticated requests - Context propagation for user claims 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates the auth redesign plan to include: - Two authentication types: User Session (refresh+access) and PAT - PAT storage in user_setting with SHA-256 hashed tokens - Interceptor logic handling both token types - PAT management API endpoints - Migration strategy for existing ACCESS_TOKENS - Security considerations for PATs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive implementation plan with 20 bite-sized tasks covering: - Proto definitions for RefreshTokens and PersonalAccessTokens - Token generation (AccessTokenV2, RefreshToken, PAT) - Store layer methods for token management - Updated authenticator and interceptor - PAT management API endpoints - Frontend token refresh interceptor - Test coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add GetUserByPATHash method to Driver interface and implement for SQLite, MySQL, and PostgreSQL using database-specific JSON queries for efficient token hash lookup. - SQLite: Use json_each with EXISTS for array filtering - MySQL: Use JSON_SEARCH for token hash lookup - PostgreSQL: Use jsonb_array_elements with EXISTS Store wrapper method fetches complete user info and returns PATQueryResult with user details and token metadata. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements Task 8 of auth redesign plan: - Add AuthenticateByAccessTokenTokenV2 method for stateless JWT validation - Add AuthenticateByRefreshToken method for refresh token validation against DB - Add AuthenticateByPAT method for Personal Access Token validation - Update AuthResult struct to include Claims field for stateless auth - Update Authenticate method to try new token types first (priority order) - Maintains backward compatibility with legacy session and JWT auth The new authentication flow: 1. Try Access Token V2 (stateless, no DB queries) 2. Try PAT (validates hash against DB, updates last_used async) 3. Legacy: Try session cookie 4. Legacy: Try JWT access token Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update WrapUnary to check AuthResult.Claims for stateless access tokens - Set UserClaims in context when authenticated via Access Token V2 - Set UserID in context for both stateless and stateful auth paths - Handle PAT authentication through existing SetUserInContext path - Maintain backward compatibility with legacy session/JWT auth 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ExtractRefreshTokenFromCookie helper to auth/extract.go - Implement RefreshToken RPC in auth_service.go - Extracts refresh token from memos_refresh cookie - Validates refresh token using authenticator.AuthenticateByRefreshToken - Generates new access token using auth.GenerateAccessTokenV2 - Returns access token and expiry in response - Add ConnectRPC wrapper for RefreshToken in connect_services.go - All error cases handled with appropriate status codes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two critical authentication fixes: 1. **MetadataInterceptor**: Forward Cookie header to gRPC metadata - RefreshToken extracts refresh token from cookie via metadata - Previously only User-Agent, X-Forwarded-For, X-Real-Ip were forwarded - Added cookie forwarding to enable token refresh flow 2. **ACL Configuration**: Add RefreshToken to PublicMethods - RefreshToken must be accessible when access token has expired - Without this, clients cannot refresh expired access tokens - Now properly listed as public endpoint alongside CreateSession These fixes enable the token refresh flow where clients can obtain new access tokens using their refresh token cookie without re-authenticating. Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements Task 11 from auth redesign implementation plan: - Modified doSignIn to generate both refresh and access tokens - Store refresh token metadata in user_setting using AddUserRefreshToken - Set refresh token as HttpOnly cookie (memos_refresh) - Return access token and expiry in CreateSessionResponse - Added buildRefreshTokenCookie helper with SameSite=Lax - Updated clearAuthCookies to clear both session and refresh cookies - Maintained backward compatibility with legacy session cookie The refresh token is stored as an HttpOnly cookie with: - Path=/ - HttpOnly (prevent JavaScript access) - SameSite=Lax (CSRF protection) - Secure (in HTTPS mode only) - 30-day expiration The access token is returned in the response body with 15-minute expiration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update DeleteSession to support both new token-based auth and legacy session auth: - Extract refresh token from cookie (memos_refresh) if authenticated via new token flow - Parse refresh token to get token_id claim - Remove refresh token from user_setting using store.RemoveUserRefreshToken - Clear both refresh token and legacy session cookies via clearAuthCookies - Handle gracefully if no refresh token exists (legacy sessions) - Maintain backward compatibility with legacy session-based logout This completes Task 12 of the auth redesign implementation plan. Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates Personal Access Token (PAT) management endpoints in user_service.proto to align with Task 13 of the auth implementation plan: Proto changes: - Update CreateUserAccessToken RPC to return CreateUserAccessTokenResponse - Add CreateUserAccessTokenResponse message with access_token metadata and token value - Update CreateUserAccessTokenRequest to include description and expires_in_days - Update UserAccessToken message to remove access_token field (metadata only) - Rename issued_at to created_at in UserAccessToken - Add last_used_at field to UserAccessToken for future PAT last-use tracking Implementation changes: - Update CreateUserAccessToken handler to return new response type - Update ListUserAccessTokens to use new field names (created_at vs issued_at) - Update convertUserSettingToStore to prevent access token updates via UpdateUserSetting - Update connect service handler signature for CreateUserAccessToken - Regenerate proto code for Go, TypeScript, and OpenAPI Security improvements: - Token value only returned on creation (not in list responses) - Access tokens managed via dedicated create/delete endpoints only - Prevents accidental token disclosure through settings updates Generated code updates: - Go: proto/gen/api/v1/* - TypeScript: web/src/types/proto/api/v1/user_service_pb.ts - OpenAPI: proto/gen/openapi.yaml All tests pass. Ready for PAT backend implementation in subsequent tasks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update PAT management endpoints to use new auth infrastructure: - ListUserAccessTokens now uses store.GetUserPersonalAccessTokens() - CreateUserAccessToken now uses auth.GeneratePersonalAccessToken() and auth.HashPersonalAccessToken() for secure storage - DeleteUserAccessToken now uses store.RemoveUserPersonalAccessToken() - All endpoints now support last_used_at timestamp - Switched from legacy ACCESS_TOKENS to PERSONAL_ACCESS_TOKENS - Removed deprecated UpsertAccessTokenToStore() function - Removed unused jwt import Token format: memos_pat_xxx (32 random chars) Storage: SHA-256 hash stored in PERSONAL_ACCESS_TOKENS user setting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create accessToken storage module (web/src/auth-state.ts) - Store access token in memory for security - Provide get/set/clear functions - Track token expiration with 30s safety buffer - Add auth interceptor to ConnectRPC transport (web/src/connect.ts) - Attach access token to requests via Authorization header - Handle 401 errors by refreshing token - Call authServiceClient.refreshToken() on 401 - Update stored access token and retry original request - Prevent concurrent refresh attempts - Redirect to login on refresh failure - Update user store to handle access tokens (web/src/store/user.ts) - Store access token from CreateSession response on login - Clear token on logout - Add logout helper function - Update login flows to store access token - PasswordSignInForm.tsx: Store token on password login - AuthCallback.tsx: Store token on SSO login - SignUp.tsx: Store token on signup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update initialUserStore to work with refresh token flow - Add comprehensive error handling for auth failures - Improve logout to clear all state including settings and stats - Add detailed comments explaining auth flow with interceptor - Ensure state is properly cleared on auth failures 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for Task 18 of auth implementation plan covering: - Token generation functions (access, refresh, PAT) - Token parsing and validation - Token hashing (PAT) - Authenticator methods for new token types - Store methods for refresh tokens and PATs All tests verify: - Happy path: valid token generation and authentication - Error cases: invalid tokens, wrong secrets, expired tokens - Edge cases: archived users, revoked tokens, non-expiring PATs - Store operations: CRUD for refresh tokens and PATs Test results: All 42 test cases pass successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update CreateAccessTokenDialog to use description and expiresInDays fields - Update AccessTokenSection to use createdAt instead of issuedAt - Remove token display column (PATs don't store token values) - Auto-copy token to clipboard on creation - Fix TypeScript and formatting issues
This commit removes support for legacy authentication methods: - Legacy session cookie authentication (SESSIONS user setting) - Legacy JWT access token authentication (ACCESS_TOKENS user setting) No data migration is provided - old SESSIONS and ACCESS_TOKENS data will be ignored and eventually cleaned up by users. Changes: - Removed AuthenticateBySession and AuthenticateByJWT from Authenticator - Removed session/access token methods from store (GetUserSessions, AddUserSession, RemoveUserSession, UpdateUserSessionLastAccessed, GetUserAccessTokens, RemoveUserAccessToken, GetUserSessionByID) - Removed GetUserSessionByID from Driver interface and all DB drivers - Removed legacy session tracking from doSignIn in auth_service.go - Removed legacy session removal from DeleteSession - Removed buildSessionCookie and trackUserSession helpers - Removed ListUserSessions and RevokeUserSession API endpoints - Updated GetCurrentSession to not update session last accessed time - Updated getCurrentUser in fileserver to use new auth methods only - Removed SESSIONS and ACCESS_TOKENS cases from user setting conversions The system now only supports: - Access Token V2 (short-lived, stateless JWT) - Personal Access Tokens (PAT, long-lived with revocation) - Refresh Tokens (for obtaining new Access Token V2) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix gRPC-Gateway Access Token V2 support (handle Claims vs User) - Add JWT audience and issuer validation for access/refresh tokens - Remove unused 100-year expiry code from doSignIn - Fix nil user check in GetUserByPATHash to prevent panics - Add logging for PAT last-used update failures - Fix linter issues (godot, goimports, appendAssign, staticcheck) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rename API endpoints to follow OAuth2/OIDC conventions: - GetCurrentSession → GetCurrentUser (GET /api/v1/auth/me) - CreateSession → SignIn (POST /api/v1/auth/signin) - DeleteSession → SignOut (POST /api/v1/auth/signout) - RefreshToken unchanged (POST /api/v1/auth/refresh) Changes: - Update proto definitions with new method names and endpoints - Regenerate Go and TypeScript proto files - Update service implementations and Connect handlers - Update ACL config for public/protected methods - Update frontend API calls - Rename internal helper to fetchCurrentUser to avoid naming conflict 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename UserAccessToken to PersonalAccessToken for clarity - ListUserAccessTokens -> ListPersonalAccessTokens - CreateUserAccessToken -> CreatePersonalAccessToken - DeleteUserAccessToken -> DeletePersonalAccessToken - Update URL pattern: /accessTokens -> /personalAccessTokens - Rename UserSession to Session for consistency - ListUserSessions -> ListSessions - RevokeUserSession -> RevokeSession - Implement ListSessions and RevokeSession methods - Update all generated proto files, Go services, and frontend code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove docs/plans/*.md (design documents) - Remove memos binary file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.
No description provided.