Skip to content

Security: OAuth tokens stored in localstorage #30

Description

@ovaar

Security: Avoid storing OAuth refresh tokens in localStorage

Summary

@elfsquad/authentication currently stores OAuth tokens, including the refresh_token, in localStorage.

Because localStorage is accessible to any JavaScript executing in the application's origin, a successful Cross-Site Scripting (XSS) vulnerability could allow an attacker to extract the refresh token and impersonate the user for an extended period. This significantly increases the impact of XSS vulnerabilities.

Current Behavior

  • Access token stored in localStorage
  • Refresh token stored in localStorage

Security Risk

A refresh token typically has a much longer lifetime than an access token. If it is stored in localStorage, any injected or compromised script can read and exfiltrate it.

Example:

const refreshToken = localStorage.getItem("refresh_token");
// Attacker exfiltrates the token

This could enable:

  • Long-lived account/session takeover
  • Token refresh outside the user's browser
  • Continued access until the token is revoked or expires

Recommendation

Prefer one of the following approaches:

Preferred

  • Store the refresh token in an HttpOnly, Secure, SameSite cookie.
  • Keep the access token in memory and obtain a new one through a refresh endpoint when needed.

Alternative

If the architecture permits, adopt a Backend-for-Frontend (BFF) pattern so OAuth tokens never reach the browser.

Benefits

  • Prevents JavaScript from directly reading refresh tokens.
  • Reduces the impact of XSS vulnerabilities.
  • Aligns with current OAuth security best practices for browser-based applications.

Notes

If browser storage must be used due to architectural constraints, additional mitigations such as short-lived access tokens, refresh token rotation with reuse detection, a strict Content Security Policy (CSP), and strong XSS defenses should be implemented. However, these measures do not eliminate the risk of token theft from localStorage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions