Skip to content

Detect auth scope changes across app updates#1353

Draft
silverbucket wants to merge 2 commits intomasterfrom
feature/scope-change-reauth
Draft

Detect auth scope changes across app updates#1353
silverbucket wants to merge 2 commits intomasterfrom
feature/scope-change-reauth

Conversation

@silverbucket
Copy link
Member

@silverbucket silverbucket commented Mar 14, 2026

Summary

This adds first-class detection for auth scope changes after an app has already been authorized.

When an app loads with a different claimed scope than the one last authorized for the current backend, rs.js now stores that mismatch as library state and emits a sticky scope-change-required event with a reauthorize() helper.

Problem

Today, if a user is already authenticated and an app ships an update that changes the scope it claims, rs.js does not detect that scope drift proactively.

That creates a poor experience for both cases:

  • If the app now needs more access, the user may only discover it later through generic unauthorized failures.
  • If the app now needs less access, the user is never prompted to refresh authorization to align their stored grant with the app's current permissions.

Apps using the remoteStorage widget should not have to build their own detection logic for this. The core library should know when the currently requested scope differs from the last authorized one.

What this changes

  • Persists the last authorized scope for the active backend in localStorage.
  • Persists the pending requested scope before OAuth redirect, then promotes it to the authorized scope after successful authorization.
  • Normalizes scope strings before storing/comparing them, but intentionally treats any difference as requiring reauthorization.
  • Emits a sticky scope-change-required event so UIs attaching later can still react.
  • Adds a reauthorize() alias for reconnect() to make the permission refresh flow more explicit.
  • Adds tests covering startup mismatch detection and authorization completion.

Why this approach

The goal here is not to interpret whether a scope change is broader or narrower. The important signal is simply that the app's current requested scope no longer matches the last authorization state. Keeping that comparison simple makes the behavior predictable and lets widget-based apps handle the UX centrally without requiring per-app code.

Non-widget apps

Apps that do not use the widget can hook into this directly with the new event surface:

remoteStorage.on('scope-change-required', ({ authorizedScope, requestedScope, reauthorize }) => {
  console.log('Authorization scope changed', { authorizedScope, requestedScope });
  // Show your own prompt/banner/dialog, then call:
  reauthorize();
});

That means custom UIs do not need to implement their own scope persistence or comparison logic. They only need to decide how to present the message to the user.

Widget users

The intention is that widget users should not need any app changes for this once the widget adopts the new core event. The widget can listen for scope-change-required and show a built-in refresh-permissions message/action.

Testing

  • ./node_modules/.bin/tsc --pretty false
  • ./node_modules/.bin/mocha test/unit/remotestorage.test.mjs test/unit/authorize.test.mjs

Follow-up

This PR adds the core detection and event surface in rs.js. The built-in user-facing message for widget users should be implemented in the separate remotestorage-widget repo by listening for scope-change-required and offering a refresh-permissions action.

Copy link

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

Adds core support in rs.js for detecting when an app’s currently claimed access scope differs from the last authorized scope for the active backend, and exposes that state via a sticky event and helper.

Changes:

  • Persist “pending” and “authorized” OAuth scopes in localStorage, normalize them, and compare against the currently claimed scope.
  • Emit a sticky scope-change-required event (with a reauthorize() helper) and expose scopeChangeRequired state.
  • Add unit tests covering scope drift detection and promotion of pending → authorized scope on successful authorization.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/remotestorage.ts Implements scope normalization, persistence, drift detection, sticky event emission, and reauthorize() alias.
src/authorize.ts Stores pending scope before redirect and completes scope persistence on successful auth; clears pending on error paths.
src/access.ts Notifies RemoteStorage when claimed scopes change so drift detection can run immediately.
test/unit/remotestorage.test.mjs Adds tests for sticky scope-change-required event and clearing drift state after auth completion.
test/unit/authorize.test.mjs Adds tests for pending-scope storage on authorize and authorized-scope promotion on token exchange completion.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@silverbucket silverbucket self-assigned this Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants