Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop using token authentication in the UI #8289

Merged
merged 1 commit into from
Aug 22, 2024

Conversation

SpecLad
Copy link
Contributor

@SpecLad SpecLad commented Aug 9, 2024

Motivation and context

Currently, the UI authenticates with the server using two parallel methods:

  • A cookie set by the /api/auth/login endpoint.
  • A token returned by the same endpoint.

This is redundant and confusing, and also causes several usability & security issues:

  • If a user creates 2 or more concurrent sessions (e.g. logs in on two computers), and then logs out of one of them, it will effectively log them out of all other sessions too. This happens because:

    1. The same token is shared between all sessions.
    2. Logging out destroys the token in the DB.
    3. The server tries to authenticate the browser using the token first, so if a browser presents a token that's no longer present in the DB, the server responds with a 401 (even if the cookie is still valid).
  • When a user changes their password, Django invalidates all of that user's other sessions... except that doesn't work, because the user's token remains valid. This is bad, because if an attacker steals a user's password and logs in, the most obvious recourse (changing the password) will not work - the attacker will stay logged in.

  • Sessions effectively last forever, because, while Django's session data expires after SESSION_COOKIE_AGE, the token never expires.

  • The token is stored in local storage, so it could be stolen in an XSS attack. The session cookie is not susceptible to this, because it's marked HttpOnly.

The common theme in all these problems is the token, so by ceasing to use it we can fix them all.

Note that this patch does not remove the server-side token creation & authentication logic, or remove the token from the output of the /api/auth/login endpoint. This is because that would break the Client.login method in the SDK. However, I believe that in the future we should get rid of the whole "generate token on login" logic, and let users create API tokens explicitly if (and only if) they wish to use the SDK.

How has this been tested?

Manual testing.

Checklist

  • I submit my changes into the develop branch
  • I have created a changelog fragment
  • [ ] I have updated the documentation accordingly
  • [ ] I have added tests to cover my changes
  • [ ] I have linked related issues (see GitHub docs)
  • [ ] I have increased versions of npm packages if it is necessary
    (cvat-canvas,
    cvat-core,
    cvat-data and
    cvat-ui)

License

  • I submit my code changes under the same MIT License that covers the project.
    Feel free to contact the maintainers if that's a concern.

Summary by CodeRabbit

  • New Features

    • Enhanced session management, allowing multiple active sessions across devices without interruptions.
    • Automatic session expiration after two weeks of inactivity for improved security.
    • All user sessions are invalidated except the current one upon password change.
  • Bug Fixes

    • Simplified authentication handling by removing reliance on local storage for tokens, enhancing security.
  • Documentation

    • Updated authentication mechanisms to align with standard practices, replacing custom classes with standard implementations.

Copy link
Contributor

coderabbitai bot commented Aug 9, 2024

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces major enhancements to the application's user session management and authentication processes. Key improvements include refined session logout behavior, automatic session expiration after inactivity, and enhanced security measures upon password changes. Additionally, the authentication mechanisms have been streamlined by removing deprecated methods and classes, promoting a more secure and simplified flow for managing user sessions and authentication data.

Changes

Files Change Summary
cvat-core/src/api-implementation.ts, cvat-core/src/api.ts, cvat-core/src/index.ts, cvat-core/src/server-proxy.ts Removed removeAuthData functionality across multiple files, simplifying authentication management and eliminating reliance on local storage for tokens.
cvat/apps/iam/authentication.py Removed TokenAuthenticationEx class, streamlining authentication logic by eliminating custom session handling in token authentication.
cvat/settings/base.py Updated authentication classes to use the standard TokenAuthentication from Django REST framework, replacing the customized version to align with best practices.
changelog.d/20240815_143525_roman_no_token_in_ui_2.md Provided a comprehensive summary of the changes, focusing on usability and security improvements related to user sessions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Server
    participant Auth

    User->>Server: Login Request
    Server->>Auth: Validate Credentials
    Auth-->>Server: Credentials Valid
    Server-->>User: Session Token

    User->>Server: Change Password
    Server->>Auth: Invalidate Other Sessions
    Auth-->>Server: Sessions Invalidated
    Server-->>User: Confirmation
Loading

🐰 “In a world where sessions roam free,
A rabbit hops with glee!
No more tokens left in the past,
Security and joy built to last.
With every logout, freedom’s song,
A safer journey, where we belong!”
🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@SpecLad
Copy link
Contributor Author

SpecLad commented Aug 9, 2024

/check

Copy link
Contributor

github-actions bot commented Aug 9, 2024

✔️ All checks completed successfully
📄 See logs here

Currently, the UI authenticates with the server using two parallel methods:

* A cookie set by the `/api/auth/login` endpoint.
* A token returned by the same endpoint.

This is redundant and confusing, and also causes several usability &
security issues:

* If a user creates 2 or more concurrent sessions (e.g. logs in on two
  computers), and then logs out of one of them, it will effectively log
  them out of all other sessions too. This happens because:

  1. The same token is shared between all sessions.
  2. Logging out destroys the token in the DB.
  3. The server tries to authenticate the browser using the token first,
     so if a browser presents a token that's no longer present in the DB,
     the server responds with a 401 (even if the cookie is still valid).

* When a user changes their password, Django invalidates all of that user's
  other sessions... except that doesn't work, because the user's token
  remains valid. This is bad, because if an attacker steals a user's
  password and logs in, the most obvious recourse (changing the password)
  will not work - the attacker will stay logged in.

* Sessions effectively last forever, because, while Django's session data
  expires after `SESSION_COOKIE_AGE`, the token never expires.

* The token is stored in local storage, so it could be stolen in an XSS
  attack. The session cookie is not susceptible to this, because it's marked
  `HttpOnly`.

The common theme in all these problems is the token, so by ceasing to use it
we can fix them all.

Note that this patch does not remove the server-side token creation &
authentication logic, or remove the token from the output of the
`/api/auth/login` endpoint. This is because that would break the
`Client.login` method in the SDK. However, I believe that in the future we
should get rid of the whole "generate token on login" logic, and
let users create API tokens explicitly if (and only if) they wish
to use the SDK.
Copy link

sonarcloud bot commented Aug 15, 2024

@SpecLad SpecLad marked this pull request as ready for review August 15, 2024 12:52
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between e0a60d8 and cb8babb.

Files selected for processing (7)
  • changelog.d/20240815_143525_roman_no_token_in_ui_2.md (1 hunks)
  • cvat-core/src/api-implementation.ts (1 hunks)
  • cvat-core/src/api.ts (1 hunks)
  • cvat-core/src/index.ts (1 hunks)
  • cvat-core/src/server-proxy.ts (6 hunks)
  • cvat/apps/iam/authentication.py (2 hunks)
  • cvat/settings/base.py (1 hunks)
Additional comments not posted (9)
changelog.d/20240815_143525_roman_no_token_in_ui_2.md (1)

1-15: Changelog entry is clear and informative.

The changelog effectively summarizes the key changes and improvements made in the PR, providing links to the relevant pull request for further details.

cvat/apps/iam/authentication.py (1)

Line range hint 1-67:
Removal of TokenAuthenticationEx aligns with PR objectives.

The removal of the TokenAuthenticationEx class and its authenticate method simplifies the authentication logic, aligning with the goal of eliminating token-based authentication in the UI.

cvat-core/src/index.ts (1)

Line range hint 1-178:
Removal of removeAuthData aligns with PR objectives.

The removal of removeAuthData simplifies the CVATCore interface, aligning with the goal to streamline authentication management.

However, verify if there are any dependencies on removeAuthData in the codebase.

Verification successful

No Dependencies Found for removeAuthData

The removeAuthData method is not used elsewhere in the codebase, confirming that its removal from the CVATCore interface does not impact other parts of the system. This aligns with the PR's objective to streamline authentication management.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if there are any dependencies on `removeAuthData` in the codebase.

# Test: Search for the usage of `removeAuthData`. Expect: No occurrences.
rg --type ts 'removeAuthData'

Length of output: 29


Script:

#!/bin/bash
# Re-verify if there are any dependencies on `removeAuthData` in the codebase.

# Search for the usage of `removeAuthData` in TypeScript files.
rg 'removeAuthData' --type ts

Length of output: 29

cvat-core/src/api.ts (1)

Line range hint 1-1:
Removal of removeAuthData method approved.

The removal of the removeAuthData method simplifies the API. Ensure that this change does not negatively impact other parts of the application that might have relied on this functionality.

Verification successful

Removal of removeAuthData method is safe.

The removeAuthData function is not referenced or declared anywhere else in the codebase, indicating that its removal does not negatively impact other parts of the application.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of `removeAuthData` in the codebase.

# Test: Search for any remaining references to `removeAuthData`.
rg --type javascript 'removeAuthData'

Length of output: 72


Script:

#!/bin/bash
# Description: Verify the usage and declaration of `removeAuthData` in the codebase.

# Search for the declaration of `removeAuthData` function
ast-grep --lang javascript --pattern $'function removeAuthData($$$) { $$$ }'

# Search for any references to `removeAuthData`
rg 'removeAuthData'

Length of output: 97

cvat-core/src/api-implementation.ts (1)

Line range hint 1-1:
Removal of removeAuthData implementation mixin approved.

The removal aligns with the changes in cvat-core/src/api.ts. Ensure that this removal does not impact other parts of the application that might have relied on this functionality.

Verification successful

No remaining references to removeAuthData found. The removal of the removeAuthData implementation does not impact other parts of the application, as there are no references to it in the codebase.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of `removeAuthData` in the codebase.

# Test: Search for any remaining references to `removeAuthData`.
rg --type javascript 'removeAuthData'

Length of output: 72


Script:

#!/bin/bash
# Description: Verify the usage of `removeAuthData` in the codebase.

# Test: Search for any remaining references to `removeAuthData`.
rg --type js 'removeAuthData'

Length of output: 29

cvat/settings/base.py (1)

137-137: Replacement of TokenAuthenticationEx with TokenAuthentication approved.

Switching to the standard TokenAuthentication simplifies the authentication setup. Verify that this change does not disrupt any custom behavior previously handled by TokenAuthenticationEx.

Verification successful

No custom behavior found for TokenAuthenticationEx.

The search for TokenAuthenticationEx returned no results, indicating it is not present in the codebase. This confirms that replacing it with TokenAuthentication does not disrupt any custom behavior.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage and implementation of `TokenAuthenticationEx` to ensure no custom behavior is lost.

# Test: Search for the definition and usage of `TokenAuthenticationEx`.
rg --type python 'TokenAuthenticationEx'

Length of output: 71


Script:

#!/bin/bash
# Corrected command to search for the definition and usage of `TokenAuthenticationEx` in Python files.

# Test: Search for the definition and usage of `TokenAuthenticationEx`.
rg 'TokenAuthenticationEx' --glob '*.py' -A 5

Length of output: 45

cvat-core/src/server-proxy.ts (3)

Line range hint 366-372: Approve simplified setAuthData function.

The function now focuses solely on handling cookies, aligning with the shift away from token-based authentication. Verify that all authentication logic is consistent with cookie-based authentication.


361-364: Approve removal of token from local storage.

This change improves security by preventing unnecessary credential exposure. Ensure that all related code referencing token storage is also updated or removed.


Line range hint 643-654: Approve updated authenticated function.

The function now relies on server-side validation for authentication status, which simplifies the process. Ensure that server-side logic correctly handles authentication without tokens.

Verification successful

Server-side Authentication Without Tokens Verified

The search results indicate that while tokens are referenced in various parts of the codebase, there is no direct evidence that server-side logic relies on tokens for authentication in the context of the updated authenticated function. The changes appear to be correctly implemented, relying on server-side validation without token checks.

  • Files with token references: Mostly in tests and UI components, not directly related to server-side authentication logic.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that server-side logic correctly handles authentication without tokens.

# Test: Search for server-side authentication logic that might reference tokens. Expect: No results.
rg --type javascript --type typescript 'token' --glob '!src/server-proxy.ts'

Length of output: 111


Script:

#!/bin/bash
# Description: Verify that server-side logic correctly handles authentication without tokens.

# Test: Search for server-side authentication logic that might reference tokens. Expect: No results.
rg --type js --type ts 'token' --glob '!src/server-proxy.ts'

Length of output: 7160

Copy link
Member

@bsekachev bsekachev left a comment

Choose a reason for hiding this comment

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

LGTM

@Marishka17
Copy link
Contributor

@SpecLad, Looks like, we do not need in that case client endpoint /auth/login-with-token/:token

@SpecLad
Copy link
Contributor Author

SpecLad commented Aug 20, 2024

@SpecLad, Looks like, we do not need in that case client endpoint /auth/login-with-token/:token

That's a good point, but... do you know why we needed it in the first place? I don't see it being used anywhere, except for one test.

@bsekachev
Copy link
Member

@SpecLad Initially it was implemented for a customer. Then it was used in integration with Toloka.

Internally we do not use it somehow. But the endpoint may be used by 3rdparty integrations.

@SpecLad
Copy link
Contributor Author

SpecLad commented Aug 20, 2024

Hmm... I'll have to think about the impact of removing this. I'll switch this to draft status for now.

@SpecLad SpecLad marked this pull request as draft August 20, 2024 11:04
SpecLad added a commit to SpecLad/cvat that referenced this pull request Aug 22, 2024
There are several problems with this feature:

1. To use it, you have to put the user's token in the URL. This token lasts
   forever (unless the user explicitly logs out), so it is nearly as
   sensitive as the user's password. Embedding such sensitive information in
   the URL is problematic, because URLs are saved in the browser history,
   dumped to server logs and displayed on the screen, none of which are
   secure locations. A user could also accidentally share a URL with an
   embedded token.

2. If an attacker can get a user to follow a malicious link, they could
   forcibly log that user into the attacker's account (AKA "login CSRF").
   This by itself is just a nuisance, but the attacker could potentially use
   this to trick the victim into, for example, uploading confidential data
   to the attacker's account.

3. By design, it requires the use of token authentication, whose drawbacks I
   have explained in cvat-ai#8289.

   In fairness, when originally implemented, this feature set the session
   cookie rather than the token, but this cannot work if the user is already
   logged in, as the `sessionid` cookie is marked `HTTPOnly` and cannot be
   overridden by JavaScript. So the only way for this feature to work in all
   circumstances is to set the token.

Generally, the use cases of this feature are better served by single sign-on
protocols, which don't suffer from these drawbacks.
SpecLad added a commit to SpecLad/cvat that referenced this pull request Aug 22, 2024
There are several problems with this feature:

1. To use it, you have to put the user's token in the URL. This token lasts
   forever (unless the user explicitly logs out), so it is nearly as
   sensitive as the user's password. Embedding such sensitive information in
   the URL is problematic, because URLs are saved in the browser history,
   dumped to server logs and displayed on the screen, none of which are
   secure locations. A user could also accidentally share a URL with an
   embedded token.

2. If an attacker can get a user to follow a malicious link, they could
   forcibly log that user into the attacker's account (AKA "login CSRF").
   This by itself is just a nuisance, but the attacker could potentially use
   this to trick the victim into, for example, uploading confidential data
   to the attacker's account.

3. By design, it requires the use of token authentication, whose drawbacks I
   have explained in cvat-ai#8289.

   In fairness, when originally implemented, this feature set the session
   cookie rather than the token, but this cannot work if the user is already
   logged in, as the `sessionid` cookie is marked `HTTPOnly` and cannot be
   overridden by JavaScript. So the only way for this feature to work in all
   circumstances is to set the token.

Generally, the use cases of this feature are better served by single sign-on
protocols, which don't suffer from these drawbacks.
SpecLad added a commit that referenced this pull request Aug 22, 2024
There are several problems with this feature:

1. To use it, you have to put the user's token in the URL. This token
lasts forever (unless the user explicitly logs out), so it is nearly as
sensitive as the user's password. Embedding such sensitive information
in the URL is problematic, because URLs are saved in the browser
history, dumped to server logs and displayed on the screen, none of
which are secure locations. A user could also accidentally share a URL
with an embedded token.

2. If an attacker can get a user to follow a malicious link, they could
forcibly log that user into the attacker's account (AKA "login CSRF").
This by itself is just a nuisance, but the attacker could potentially
use this to trick the victim into, for example, uploading confidential
data to the attacker's account.

3. By design, it requires the use of token authentication, whose
drawbacks I have explained in #8289.

In fairness, when originally implemented, this feature set the session
cookie rather than the token, but this cannot work if the user is
already logged in, as the `sessionid` cookie is marked `HTTPOnly` and
cannot be overridden by JavaScript. So the only way for this feature to
work in all circumstances is to set the token.

Generally, the use cases of this feature are better served by single
sign-on protocols, which don't suffer from these drawbacks.
@SpecLad SpecLad marked this pull request as ready for review August 22, 2024 11:54
@SpecLad
Copy link
Contributor Author

SpecLad commented Aug 22, 2024

I removed login-with-token in #8336.

@SpecLad SpecLad merged commit c5ff8bc into cvat-ai:develop Aug 22, 2024
62 checks passed
@SpecLad SpecLad deleted the no-token-in-ui-2 branch August 22, 2024 13:57
@cvat-bot cvat-bot bot mentioned this pull request Aug 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants