Skip to content

Fix login persistence with AUTH_SEND_JWT_HEADER enabled#1929

Merged
umputun merged 2 commits intomasterfrom
fix/auth-send-jwt-header
Jul 6, 2025
Merged

Fix login persistence with AUTH_SEND_JWT_HEADER enabled#1929
umputun merged 2 commits intomasterfrom
fix/auth-send-jwt-header

Conversation

@paskal
Copy link
Copy Markdown
Collaborator

@paskal paskal commented Apr 27, 2025

Summary

  • When using AUTH_SEND_JWT_HEADER=true, the JWT token in header needed to be persisted to maintain login after page reload
  • The frontend now properly handles JWT tokens received in headers by:
    • Storing them in client-side cookies with the name 'JWT' to match what backend expects
    • Extracting and storing the JWT ID as XSRF token in 'XSRF-TOKEN' cookie
    • Setting cookies with Secure flag when on HTTPS connections
  • Documentation has been updated to clarify that with this flag enabled, the frontend stores the JWT in a client-side cookie

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.

@paskal paskal requested a review from umputun as a code owner April 27, 2025 21:47
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.19%. Comparing base (1f3a868) to head (abdca90).
⚠️ Report is 72 commits behind head on master.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 27, 2025

size-limit report 📦

Path Size
public/embed.mjs 2.03 KB (0%)
public/remark.mjs 73.84 KB (+0.44% 🔺)
public/remark.css 8.26 KB (0%)
public/last-comments.mjs 36.12 KB (+1.03% 🔺)
public/last-comments.css 3.75 KB (-0.03% 🔽)
public/deleteme.mjs 12.45 KB (+3.14% 🔺)
public/counter.mjs 751 B (0%)

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 27, 2025

Pull Request Test Coverage Report for Build 14742789946

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-0.03%) to 84.471%

Files with Coverage Reduction New Missed Lines %
backend/app/providers/telegram.go 2 87.88%
Totals Coverage Status
Change from base Build 14725791312: -0.03%
Covered Lines: 6049
Relevant Lines: 7161

💛 - Coveralls

@paskal paskal requested review from akellbl4 and Copilot April 27, 2025 21:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@paskal paskal force-pushed the fix/auth-send-jwt-header branch 2 times, most recently from 91044bc to bd166b6 Compare April 27, 2025 22:37
@paskal paskal requested a review from Copilot April 27, 2025 22:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. XSS Vulnerability Risk (High)
    - JWTs are stored in client-accessible cookies without HttpOnly flag
    - This makes tokens vulnerable to theft via XSS attacks
    - This is an inherent tradeoff with the AUTH_SEND_JWT_HEADER approach

  2. Cookie Security Settings
    - ✅ SameSite=Strict is correctly used for CSRF mitigation
    - ✅ Secure flag is added for HTTPS connections
    - ❌ Missing __Host- prefix on cookies for additional security

  3. CSRF Protection
    - ✅ JWT ID (jti) is extracted and used as XSRF token
    - ✅ Implementation follows Double Submit Cookie pattern

Code Quality

  1. Hardcoded Values
    - 30-day cookie expiration is hardcoded (60*60*24*30)
    - This may not align with server-side JWT TTL configuration

  2. Error Handling
    - ✅ Comprehensive try/catch blocks for token parsing and cookie setting
    - ✅ Errors are logged but silently fail for users

  3. Documentation
    - ✅ Updated in server.go and parameters documentation
    - ❌ Missing explicit security warning about XSS risks

Recommendations

  1. Security Enhancements
    - Add __Host- prefix to cookies when on HTTPS
    - Add explicit documentation warning about XSS risks
    - Consider aligning cookie expiration with JWT expiry

  2. 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

  3. Cleanup Handling
    - Add explicit cookie cleanup on auth errors/logout

    This implementation provides the needed functionality, but the security tradeoffs should be clearly documented for users enabling this feature.

@paskal paskal force-pushed the fix/auth-send-jwt-header branch from bd166b6 to dd5bc97 Compare April 28, 2025 00:10
@paskal paskal marked this pull request as draft April 28, 2025 02:00
@paskal paskal force-pushed the fix/auth-send-jwt-header branch 3 times, most recently from f599a64 to af6fe6f Compare April 28, 2025 05:50
@paskal paskal marked this pull request as ready for review April 28, 2025 05:51
@paskal paskal requested a review from Mavrin as a code owner April 28, 2025 05:51
@paskal paskal requested a review from umputun April 28, 2025 05:52
@paskal paskal force-pushed the fix/auth-send-jwt-header branch from af6fe6f to 19937b6 Compare April 29, 2025 08:03
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.
@umputun umputun merged commit eaa64ba into master Jul 6, 2025
11 checks passed
@umputun umputun deleted the fix/auth-send-jwt-header branch July 6, 2025 22:55
notmalicik pushed a commit to notmalicik/remark42 that referenced this pull request Nov 17, 2025
Fix login persistence with AUTH_SEND_JWT_HEADER enabled

Squashed commits

Romanian language added

Delete frontend/package-lock.json
@paskal paskal added this to the v1.15.0 milestone Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants