Skip to content

fix: dedupe Tokens Studio OAuth sync providers#3878

Open
six7 wants to merge 1 commit into
mainfrom
six7/six7-dedupe-studio-oauth-sync-providers
Open

fix: dedupe Tokens Studio OAuth sync providers#3878
six7 wants to merge 1 commit into
mainfrom
six7/six7-dedupe-studio-oauth-sync-providers

Conversation

@six7

@six7 six7 commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Problem

Some users on the new Studio (OAuth) sync see the same workspace listed twice under Settings → Sync providers, and sync silently uses a stale entry.

Repro

  1. Connect to a Tokens Studio (OAuth) sync provider.
  2. Close the plugin.
  3. Open again, switch to a different branch.
  4. Close and reopen the plugin → the workspace now appears twice.

Root cause

OAuth sync providers (TOKENS_STUDIO_OAUTH) are designed to be derived live on every plugin load from the user's organizations (SyncSettings.tsxstudioProviders memo). They should never live in figma.clientStorage.

BranchSelector.tsx calls AsyncMessageTypes.CREDENTIALS on every branch switch — including for OAuth providers — which routes through updateCredentials and persists the OAuth credential into apiProviders clientStorage. On the next load:

  • studioProviders re-derives the OAuth row from organizations (correct, live).
  • apiProviders is loaded from clientStorage and includes the now-stale OAuth row.
  • Both render in Sync Settings → user sees the same workspace twice.

Whichever entry happens to win storageType may carry a stale token / branch / context, which is why sync also "stops working" for affected users.

Fix

  1. utils/credentials.ts – short-circuit updateCredentials for TOKENS_STUDIO_OAUTH. OAuth providers must never be persisted.
  2. figmaStorage/ApiProvidersProperty.ts – strip any TOKENS_STUDIO_OAUTH entries when reading apiProviders. This auto-cleans existing affected users on next plugin load — no manual intervention needed.
  3. app/components/SyncSettings.tsx – defensively filter apiProviders against live studioProviders internalIds (and drop OAuth) so even a stale in-memory state can never render duplicates.

Tests

  • Added updateCredentials test asserting OAuth providers are not persisted.
  • Existing credentials + figmaStorage suites (30 tests) still pass.

Notes

Did not modify the offending BranchSelector call site directly because updateCredentials is the right place to enforce this invariant — BranchSelector legitimately needs to persist branch changes for non-OAuth providers, and there are other callers (jsonbin, github, gitlab, ado, bitbucket, supernova, generic versioned, tokensStudio PAT) that should keep persisting.

Tokens Studio OAuth providers are derived live from the user's
organizations on every load. A regression in BranchSelector was
persisting them to clientStorage when switching branches, which caused
the same workspace to appear twice in Sync Settings (one live row + one
stale persisted row) and pinned sync operations to the stale entry.

- Short-circuit updateCredentials for TOKENS_STUDIO_OAUTH so OAuth
  providers are never persisted.
- Strip any legacy persisted OAuth entries when reading apiProviders to
  clean up existing users without requiring manual deletion.
- Defensively dedupe apiProviders against live studioProviders by
  internalId in SyncSettings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

⤵️ 📦 ✨ The artifact was successfully created! Want to test it? Download it here 👀 🎁

@github-actions

Copy link
Copy Markdown
Contributor

Commit SHA:f12213f19357da92f8757f81696f68c9c83d3b35

Test coverage results 🧪

Code coverage diff between base branch:main and head branch: six7/six7-dedupe-studio-oauth-sync-providers 
Status File % Stmts % Branch % Funcs % Lines
🟢 total 61.52 (0.02) 52.99 (0.01) 59.24 (0.02) 61.94 (0.03)
🔴 packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx 80.95 (-2.38) 70.96 (-5.04) 75 (-1.47) 86.2 (1.59)
🟢 packages/tokens-studio-for-figma/src/figmaStorage/ApiProvidersProperty.ts 85.71 (14.29) 50 (0) 75 (8.34) 83.33 (16.67)
🟢 packages/tokens-studio-for-figma/src/utils/credentials.ts 93.93 (0.39) 87.5 (1.14) 100 (0) 93.33 (0.48)

@github-actions

Copy link
Copy Markdown
Contributor

Commit SHA:f12213f19357da92f8757f81696f68c9c83d3b35
Current PR reduces the test coverage percentage by 1 for some tests

@anathaniel-TS

Copy link
Copy Markdown

🛡️ Hyma Compliance Check

⚠️ 1 finding(s) across 3 file(s)

Severity Category File Headline
🟢 LOW Access Control src/utils/credentials.ts, src/figmaStorage/ApiProvidersProperty.ts, src/app/components/SyncSettings.tsx OAuth providers excluded from Figma storage and sync display
Full finding details

[Access Control] OAuth providers stripped from Figma storage persistence and UI

  • Files: credentials.ts (early return on save), ApiProvidersProperty.ts (filter on read), SyncSettings.tsx (filter from visible list)
  • Implication: StorageProviderType.TOKENS_STUDIO_OAUTH entries are now explicitly excluded from being persisted to Figma storage and from appearing in the sync settings UI. This is a positive security change — OAuth credentials should not be stored in Figma's shared document storage where they could be accessible to other collaborators.
  • Recommended action: Confirm OAuth session tokens are stored only in memory or a secure local store (not Figma document properties or plugin data). No action needed if already the case.

ℹ️ This check is informational — findings do not block merging.
🛡️ Hyma Compliance Check · automated

@anathaniel-TS

Copy link
Copy Markdown

🛡️ Hyma Compliance Check

⚠️ 1 finding(s) across 1 file(s)

Severity Category File Headline
🟢 LOW Access Control packages/tokens-studio-for-figma/src/utils/credentials.ts OAuth sync credentials are no longer persisted to clientStorage and legacy entries are stripped on read
Full finding details

[Access Control] OAuth sync credentials are no longer persisted to clientStorage and legacy entries are stripped on read

  • File: packages/tokens-studio-for-figma/src/utils/credentials.ts (lines 6-16; related: figmaStorage/ApiProvidersProperty.ts ~10-24, app/components/SyncSettings.tsx ~83-101)
  • Implication: This changes how Tokens Studio OAuth provider credentials are handled at rest — they are now derived live and intentionally not written to local clientStorage, and any previously persisted OAuth entries are removed on next load. This is a privacy-positive change but alters the credential storage/data-flow picture for the sync feature.
  • Recommended action: Before/after merging, confirm the ROPA/data-flow record for the OAuth sync provider reflects that OAuth credentials are held in-memory (live-derived) rather than persisted locally, and note the one-time cleanup of legacy persisted entries.

ℹ️ This check is informational — findings do not block merging.
🛡️ Hyma Compliance Check · automated

@anathaniel-TS

Copy link
Copy Markdown

🛡️ Hyma Compliance Check

No compliance-relevant changes detected

Files scanned · 0 findings

No new external calls, personal data processing changes, or authentication/authorization modifications detected. No compliance action required.

ℹ️ This check is informational.
🛡️ Hyma Compliance Check · automated

@anathaniel-TS

Copy link
Copy Markdown

🛡️ Hyma Compliance Check

⚠️ 1 finding(s) across 3 file(s)

Severity Category File Headline
🟢 LOW Access Control packages/tokens-studio-for-figma/src/utils/credentials.ts OAuth credentials no longer persisted to client storage; legacy entries stripped
Full finding details

[Access Control] OAuth credentials no longer persisted to client storage; legacy entries stripped

  • File: packages/tokens-studio-for-figma/src/utils/credentials.ts (lines ~6-18); also packages/tokens-studio-for-figma/src/figmaStorage/ApiProvidersProperty.ts (lines ~10-24) and packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx
  • Implication: Credential-handling behavior change for auth data. updateCredentials now returns early for TOKENS_STUDIO_OAUTH providers so they are never written to clientStorage, and ApiProvidersProperty strips any legacy persisted OAuth entries on read (they are re-derived live from the user's organizations). This is a data-minimization improvement (less stored credential state), not a gap — noted for the audit trail since it touches auth/session credential storage.
  • Recommended action: Informational — confirm no downstream flow depended on the previously persisted OAuth entries; no action required from a compliance standpoint (change reduces stored credential footprint).

ℹ️ This check is informational — findings do not block merging.
🛡️ Hyma Compliance Check · automated

@anathaniel-TS

Copy link
Copy Markdown

🛡️ Hyma Compliance Check

⚠️ 1 finding(s) across 3 file(s)

Severity Category File Headline
🟢 LOW Access Control src/utils/credentials.ts Tokens Studio OAuth provider entries are no longer persisted to Figma clientStorage and legacy entries are stripped on read
Full finding details

[Access Control] Tokens Studio OAuth provider entries are no longer persisted to Figma clientStorage and legacy entries are stripped on read

  • File: src/utils/credentials.ts (updateCredentials early-return, line ~10–16); also src/figmaStorage/ApiProvidersProperty.ts (strip on read, line ~14–24) and src/app/components/SyncSettings.tsx (dedupe rendering).
  • Implication: This is a positive credential-hygiene change: OAuth sync providers are now derived live from the user's organizations rather than written to client storage, reducing stored credential-linked state and preventing sync from pinning to a stale token/branch context. It touches the git-provider credential storage path, so it is in scope for access-control review.
  • Recommended action: Before merging, confirm no other code path relies on reading a persisted TOKENS_STUDIO_OAUTH entry from apiProviders. After merging, note in the access-control/ROPA record that OAuth provider identifiers are held in memory (live-derived) rather than persisted client storage.

ℹ️ This check is informational — findings do not block merging.
🛡️ Hyma Compliance Check · automated

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.

2 participants