Skip to content

fix(integrations): keep Jira reads working past token expiry and persist created issues#465

Open
therealbrad wants to merge 3 commits into
mainfrom
fix-jira-oauth-paths
Open

fix(integrations): keep Jira reads working past token expiry and persist created issues#465
therealbrad wants to merge 3 commits into
mainfrom
fix-jira-oauth-paths

Conversation

@therealbrad

Copy link
Copy Markdown
Contributor

Description

Three fixes to the Jira OAuth integration, surfaced while UAT-ing the per-user OAuth flow on a local dev build against a real Atlassian app + Jira Cloud:

  1. Reads no longer break ~1 hour after the admin connects. Issue reads (hover / details) borrow any connected user's OAuth token via getAdapter without passing a userId. Token refresh was gated on that (absent) caller userId, and the adapter cache returned the expired-token adapter before any refresh check — so read-without-login worked for an hour, then started failing with Failed to get accessible resources until some user-scoped request refreshed the token. Now the borrowed token refreshes on the token owner's behalf (auth.userId), and cached OAuth adapters are evicted once their access token expires so the next read rebuilds and refreshes.

  2. Issues created through the create flow now render correctly. create-issue wrote the issue title into the name column and left the columns the UI actually reads — externalKey, externalUrl, externalStatus, status, priority, and issue-type fields — empty, populating only the data JSON blob. Created issues therefore appeared with no key, no Jira link, no status, and no hover badge across lists, the Issues page, and the case editor. They now get the same first-class columns a synced/webhooked issue gets (mirrors SyncService.issueFields).

  3. i18n: the "single-use Magic Link" sign-in button label is translated across all locales (a Crowdin sync had reverted it to English).

Related Issue

N/A — found during local UAT of the per-user Jira OAuth flow.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement

How Has This Been Tested?

Describe the tests you ran to verify your changes:

  • Unit tests
  • Integration tests
  • E2E tests
  • Manual testing

Details:

  • Unit / regression tests — added coverage for the borrowed-token refresh and cache eviction-on-expiry (IntegrationManager.test.ts), and for the created-issue column persistence (create-issue/route.test.ts).
  • Manual UAT (local dev build, real Atlassian OAuth app + Jira Cloud): admin connects the integration; a second user who never connected Jira can read issue details (works); create is blocked until that user connects their own Jira and is then attributed to that user; the post-1h read failure was reproduced (expired stored token → borrowed read refreshes + persists a new token).
  • Full pnpm precommit is green — 9788 unit tests passing, lint + format clean.

Test Configuration:

  • OS: macOS
  • Browser (if applicable): n/a (Playwright-driven local dev server)
  • Node version: 24

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published
  • I have signed the CLA

Screenshots (if applicable)

N/A

Additional Notes

Two operational notes for production OAuth (not code in this PR), also surfaced during UAT:

  • A development-mode Atlassian OAuth app can only be authorized by its owner — every other user gets "you don't have access to this app." For multi-user OAuth the app must be set to Distributed / Sharing.
  • Per-user attribution depends on each user connecting their own Atlassian account; with a single shared Atlassian login every created issue is reported as that one account.

… expiry

Issue reads (hover/details) borrow any connected user's OAuth token via
getAdapter without passing a userId. Token refresh was gated on that caller
userId, so a borrowed read could never refresh an expired token — reads
started failing ~1 hour after the admin connected, until some user-scoped
request refreshed the token. Two fixes:

- Refresh on behalf of the token's owner (auth.userId on the auth row) when
  no caller userId is supplied, and persist the rotated token.
- Track each cached OAuth adapter's access-token expiry and evict it once
  lapsed, so the next read rebuilds and refreshes instead of being served a
  stale, expired-token adapter from cache.

Adds regression tests for both the borrowed-token refresh and the cache
eviction on expiry.
create-issue stored the issue title in the `name` column and left the
columns the UI actually reads — externalKey, externalUrl, externalStatus,
status, priority, and issue-type fields — empty, populating only the `data`
JSON blob. So issues created through the create flow rendered without their
key, Jira link, status, or hover badge across lists, the Issues page, and
the case editor.

Now populate the same first-class columns a synced/webhooked issue gets
(mirrors SyncService.issueFields), on both the create and update branches of
the upsert, so a created issue is indistinguishable from a synced one.

Adds a regression test asserting the persisted columns (key in name,
externalKey/Url/Status, status, issue type).
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.

1 participant