[PR #24365] refactor: replace localStorage with HTTP-only cookies for auth tokens #30598

Closed
opened 2026-02-21 20:47:51 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langgenius/dify/pull/24365

State: closed
Merged: Yes


Important

  1. Make sure you have read our contribution guidelines
  2. Ensure there is an associated issue and you have been assigned to it
  3. Use the correct syntax to link this PR: Fixes #<issue number>.

Summary

Fixes #24364

This PR refactors the authentication token storage mechanism from localStorage to HTTP-only cookies for improved security against XSS attacks.

Changes Made:

  • Backend (API):

    • Modified login endpoints to set HTTP-only cookies for tokens instead of returning them in response body
    • Updated authentication middleware to read tokens from cookies first, with fallback to Authorization header
    • Added proper security attributes (HttpOnly, Secure, SameSite) to cookies
    • Login & refresh endpoints now set csrf token.
  • Frontend (Web):

    • Removed all localStorage usage for authentication tokens
    • Updated API request handling to rely on automatic cookie inclusion
    • Modified refresh token logic to work with cookies
    • Frontend appends csrf token to headers as X-CSRF-TOKEN when making requests.

Security Benefits:

  • XSS Protection: Tokens are no longer accessible to JavaScript, preventing theft via XSS attacks
  • Automatic Expiration: Cookies have proper max-age settings
  • CSRF Protection: SameSite=Lax attribute provides CSRF protection, and extra CSRF token protects GET methods.
  • Secure Transmission: Secure flag ensures cookies are only sent over HTTPS in production

Backward Compatibility:

  • API clients can still use Authorization header for authentication
  • The system gracefully handles both cookie-based and header-based authentication

💥 Breaking Changes:

  • Access token in url is no longer accepted since it's extremely insecure. Custom sso implementations may break if they depend on passing token via url.
  • Method of logout APIs is changed to POST.

⚠️ Special considerations of HTTP deployment

Although using HTTP in a production environment is strongly discouraged, this pull request aims to preserve existing functionality. As a result, certain trade-offs have been made to maintain compatibility.

  • Console: The token is set with SameSite=Lax and HttpOnly attributes, which allows it to function correctly in an HTTP environment.
  • Web App: Using SameSite=Lax is not practical because embedded UIs may require cross-site requests.
    • In Community and SaaS editions, the web app token is returned via the HTTP response and stored in local storage.
    • In the Enterprise edition, the token is sent as an HttpOnly cookie with SameSite=None when HTTPS is enabled. If HTTPS is disabled, the token is instead included in the URL during redirection, then saved to local storage.

Screenshots

Before After
Tokens stored in localStorage (visible in DevTools) Tokens stored in HTTP-only cookies (not accessible via JavaScript)

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran dev/reformat(backend) and cd web && npx lint-staged(frontend) to appease the lint gods
**Original Pull Request:** https://github.com/langgenius/dify/pull/24365 **State:** closed **Merged:** Yes --- > [!IMPORTANT] > > 1. Make sure you have read our [contribution guidelines](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) > 1. Ensure there is an associated issue and you have been assigned to it > 1. Use the correct syntax to link this PR: `Fixes #<issue number>`. ## Summary Fixes #24364 This PR refactors the authentication token storage mechanism from localStorage to HTTP-only cookies for improved security against XSS attacks. ### Changes Made: - **Backend (API)**: - Modified login endpoints to set HTTP-only cookies for tokens instead of returning them in response body - Updated authentication middleware to read tokens from cookies first, with fallback to Authorization header - Added proper security attributes (HttpOnly, Secure, SameSite) to cookies - Login & refresh endpoints now set csrf token. - **Frontend (Web)**: - Removed all localStorage usage for authentication tokens - Updated API request handling to rely on automatic cookie inclusion - Modified refresh token logic to work with cookies - Frontend appends csrf token to headers as X-CSRF-TOKEN when making requests. ### Security Benefits: - **XSS Protection**: Tokens are no longer accessible to JavaScript, preventing theft via XSS attacks - **Automatic Expiration**: Cookies have proper max-age settings - **CSRF Protection**: SameSite=Lax attribute provides CSRF protection, and extra CSRF token protects GET methods. - **Secure Transmission**: Secure flag ensures cookies are only sent over HTTPS in production ### Backward Compatibility: - API clients can still use Authorization header for authentication - The system gracefully handles both cookie-based and header-based authentication ### 💥 Breaking Changes: - Access token in url is no longer accepted since it's extremely insecure. Custom sso implementations may break if they depend on passing token via url. - Method of logout APIs is changed to POST. ### ⚠️ Special considerations of HTTP deployment Although using HTTP in a production environment is strongly discouraged, this pull request aims to preserve existing functionality. As a result, certain trade-offs have been made to maintain compatibility. - Console: The token is set with SameSite=Lax and HttpOnly attributes, which allows it to function correctly in an HTTP environment. - Web App: Using SameSite=Lax is not practical because embedded UIs may require cross-site requests. - In Community and SaaS editions, the web app token is returned via the HTTP response and stored in local storage. - In the Enterprise edition, the token is sent as an HttpOnly cookie with SameSite=None when HTTPS is enabled. If HTTPS is disabled, the token is instead included in the URL during redirection, then saved to local storage. ## Screenshots | Before | After | |--------|-------| | Tokens stored in localStorage (visible in DevTools) | Tokens stored in HTTP-only cookies (not accessible via JavaScript) | ## Checklist - [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) - [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) - [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. - [x] I've updated the documentation accordingly. - [x] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods
yindo added the pull-request label 2026-02-21 20:47:51 -05:00
yindo closed this issue 2026-02-21 20:47:51 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#30598