Fix login persistence with AUTH_SEND_JWT_HEADER enabled#1929
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1929 +/- ##
========================================
Coverage 62.19% 62.19%
========================================
Files 132 132
Lines 3026 3026
Branches 721 764 +43
========================================
Hits 1882 1882
+ Misses 1140 1030 -110
- Partials 4 114 +110 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
size-limit report 📦
|
Pull Request Test Coverage Report for Build 14742789946Details
💛 - Coveralls |
There was a problem hiding this comment.
Pull Request Overview
This PR fixes login persistence when AUTH_SEND_JWT_HEADER is enabled by ensuring that the JWT token is stored in a client-side cookie and that the related XSRF token is extracted from the JWT and stored in its own cookie.
- Updated the documentation for the auth.send-jwt-header parameter to reflect the new behavior.
- Modified the fetcher.ts to persist the JWT token and extract the XSRF token from the JWT payload.
- Revised the server.go parameter description to clarify the behavior of sending the JWT header.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| site/src/docs/configuration/parameters/index.md | Updated parameter description to document the new client-side cookie behavior. |
| frontend/apps/remark42/app/common/fetcher.ts | Added code to parse the JWT token, extract the JWT ID for XSRF, and store both in cookies for persistence. |
| backend/app/cmd/server.go | Revised the command parameter description to align with frontend changes. |
91044bc to
bd166b6
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR improves login persistence when AUTH_SEND_JWT_HEADER is enabled by ensuring that JWT tokens received in headers are stored in client-side cookies and that the corresponding XSRF token is set accordingly.
- Updated documentation to clarify the behavior of AUTH_SEND_JWT_HEADER.
- Added logic in the frontend fetcher to parse and persist the JWT token and XSRF token via cookies.
- Expanded tests to verify cookie storage, Secure flag handling, error handling when setting cookies, and proper reset of in-memory tokens on 401/403 responses.
- Revised the backend flag description to match the new frontend behavior.
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| site/src/docs/configuration/parameters/index.md | Documentation update for the AUTH_SEND_JWT_HEADER parameter. |
| frontend/apps/remark42/app/common/fetcher.ts | Introduced cookie persistence logic for JWT and XSRF tokens. |
| frontend/apps/remark42/app/common/fetcher.test.ts | Added tests to cover cookie setting, secure flag handling, error path, etc. |
| backend/app/cmd/server.go | Adjusted flag description to align with the frontend cookie storage behavior. |
There was a problem hiding this comment.
Overview
PR #1929 implements a solution for login persistence when using AUTH_SEND_JWT_HEADER=true. The change stores JWT tokens received from server headers in client-side cookies, allowing login sessions to persist across page reloads.
Key Findings
Security Concerns
-
XSS Vulnerability Risk (High)
- JWTs are stored in client-accessible cookies withoutHttpOnlyflag
- This makes tokens vulnerable to theft via XSS attacks
- This is an inherent tradeoff with theAUTH_SEND_JWT_HEADERapproach -
Cookie Security Settings
- ✅SameSite=Strictis correctly used for CSRF mitigation
- ✅Secureflag is added for HTTPS connections
- ❌ Missing__Host-prefix on cookies for additional security -
CSRF Protection
- ✅ JWT ID (jti) is extracted and used as XSRF token
- ✅ Implementation follows Double Submit Cookie pattern
Code Quality
-
Hardcoded Values
- 30-day cookie expiration is hardcoded (60*60*24*30)
- This may not align with server-side JWT TTL configuration -
Error Handling
- ✅ Comprehensive try/catch blocks for token parsing and cookie setting
- ✅ Errors are logged but silently fail for users -
Documentation
- ✅ Updated in server.go and parameters documentation
- ❌ Missing explicit security warning about XSS risks
Recommendations
-
Security Enhancements
- Add__Host-prefix to cookies when on HTTPS
- Add explicit documentation warning about XSS risks
- Consider aligning cookie expiration with JWT expiry -
Code Improvements
- Extract cookie setting to a helper function
- Use a constant for the cookie expiration time
- Use a more robust JWT parsing method that handles URL-safe base64 -
Cleanup Handling
- Add explicit cookie cleanup on auth errors/logoutThis implementation provides the needed functionality, but the security tradeoffs should be clearly documented for users enabling this feature.
bd166b6 to
dd5bc97
Compare
f599a64 to
af6fe6f
Compare
af6fe6f to
19937b6
Compare
With AUTH_SEND_JWT_HEADER=true, frontend now properly handles JWT authentication: - Store JWT token in client-side cookie named 'JWT' - Extract and store XSRF token from JWT payload - Set Secure flag automatically when on HTTPS connection - Update documentation to clarify this behavior This fixes an issue where login state would be lost after page reload when using header-based JWT authentication.
bb9525b to
ddb490b
Compare
Fix login persistence with AUTH_SEND_JWT_HEADER enabled Squashed commits Romanian language added Delete frontend/package-lock.json
Summary
First part of the fix for #1877, pre-requirement for fixing OAUTH login in #241
Technical Details
The issue was occurring because the JWT token was received in X-JWT header with AUTH_SEND_JWT_HEADER=true, but the token was only stored in memory. When the page reloaded, the token would be lost, causing users to be logged out.
This implementation allows the frontend to handle authentication consistently regardless of whether AUTH_SEND_JWT_HEADER is enabled or not, making it transparent to users.
Demo
Before:
before.mov
After:
after.mov
Concerns
Stored cookie is not HTTPOnly as it's written from JS, so could be read from JS on the page as well. I thought it would be better than storing it in local storage, at least we know that the only sensitive stuff is still in the cookie.