Skip to content

fix: refresh JWT tokens after user updates#1082

Closed
deanpektas27 wants to merge 4 commits intostack-auth:devfrom
deanpektas27:fix-setselectedteam-token-refresh
Closed

fix: refresh JWT tokens after user updates#1082
deanpektas27 wants to merge 4 commits intostack-auth:devfrom
deanpektas27:fix-setselectedteam-token-refresh

Conversation

@deanpektas27
Copy link

@deanpektas27 deanpektas27 commented Jan 3, 2026

When calling user.setSelectedTeam, the JWT does not refresh until the user re-authenticates.

Problem
After calling user.setSelectedTeam, the sdk updates cache but the JWT contains either the old team ID, or remains null (in my case its when the user signs up and is onboarding).

Adding await session.fetchNewTokens() ensures JWT's are refreshed after user updates.

Summary by CodeRabbit

  • Bug Fixes

    • Session tokens are now refreshed automatically after updating a user profile or changing the selected team, preventing stale tokens and improving authentication reliability.
  • Tests

    • Added end-to-end tests ensuring access tokens are re-issued, reflect the current selected team, and are unique after user or team changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@cmux-agent
Copy link

cmux-agent bot commented Jan 3, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@vercel
Copy link

vercel bot commented Jan 3, 2026

@deanpektas27 is attempting to deploy a commit to the Stack Auth Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Jan 3, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

Adds an awaited call to session.fetchNewTokens() after client-side user updates so tokens are refreshed before returning, and adds an end-to-end test that verifies access tokens are reissued and reflect updated selected_team_id after team selection or user updates.

Changes

Cohort / File(s) Summary
Session token refresh
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
After updating the client user, the implementation now awaits session.fetchNewTokens() to refresh session tokens before returning the update result.
End-to-end tests (new)
apps/e2e/tests/js/user-update-token-refresh.test.ts
New E2E test file that signs up a user, creates/switches teams via setSelectedTeam and user.update, decodes access tokens to assert selected_team_id updates, and verifies new tokens differ from previous ones.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped in the stack and gave tokens a spin,
Updated the user, then fetched new keys with a grin.
Teams swapped their hats, claims fresh and bright,
Old tokens retired — the new ones take flight. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix: refresh JWT tokens after user updates' directly describes the main change: adding token refresh after user updates, which is the core intent of this PR.
Description check ✅ Passed The PR description clearly explains the problem (JWT not refreshing after setSelectedTeam) and the solution (adding session.fetchNewTokens()), though it lacks a formal structure matching the minimal template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 3, 2026

Greptile Summary

Fixes JWT token staleness after user updates by adding a fetchNewTokens() call in _updateClientUser. This ensures the access token reflects up-to-date user claims like selected_team_id immediately after mutations.

  • Added await session.fetchNewTokens() after _refreshUser(session) in the _updateClientUser method
  • Resolves an issue where calling user.setSelectedTeam would update the local cache but leave the JWT with stale team information
  • The fetchNewTokens() call safely handles edge cases (returns null if no refresh token or session is invalid)

Confidence Score: 5/5

  • This is a straightforward, low-risk fix that correctly addresses JWT token staleness after user updates.
  • The change is minimal (single line addition), follows existing patterns in the codebase, and the fetchNewTokens() method already handles edge cases gracefully. The fix addresses a real bug without introducing new complexity.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts Added session.fetchNewTokens() call after user updates to refresh JWT tokens, ensuring claims like selected_team_id are up-to-date.

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant SDK as Client SDK
    participant Cache as Local Cache
    participant API as Backend API
    participant Session as InternalSession

    User->>SDK: setSelectedTeam(team)
    SDK->>SDK: update({ selectedTeamId })
    SDK->>SDK: _updateClientUser(update, session)
    SDK->>API: updateClientUser(update)
    API-->>SDK: response
    SDK->>Cache: _refreshUser(session)
    Cache-->>SDK: cache updated
    SDK->>Session: fetchNewTokens()
    Session->>API: refresh access token
    API-->>Session: new JWT with updated selected_team_id
    Session-->>SDK: new tokens
    SDK-->>User: complete
Loading

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 3, 2026

Greptile found no issues!

From now on, if a review finishes and we haven't found any issues, we will not post anything, but you can confirm that we reviewed your changes in the status check section.

This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR".

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: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6849075 and add379e.

📒 Files selected for processing (1)
  • packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Always add new E2E tests when changing the API or SDK interface
For blocking alerts and errors, never use toast; use alerts instead as they are less easily missed by the user
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error); use loading indicators and async callbacks instead, or use runAsynchronously/runAsynchronouslyWithAlert for error handling
Use ES6 maps instead of records wherever you can

Files:

  • packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,css}: Keep hover/click transitions snappy and fast; avoid fade-in delays on hover. Apply transitions after action completion instead, like smooth fade-out when hover ends
Use hover-exit transitions instead of hover-enter transitions; for example, use 'transition-colors hover:transition-none' instead of fade-in on hover

Files:

  • packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
{.env*,**/*.{ts,tsx,js}}

📄 CodeRabbit inference engine (AGENTS.md)

Prefix environment variables with STACK_ (or NEXT_PUBLIC_STACK_ if public) so changes are picked up by Turborepo and improves readability

Files:

  • packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Vercel Agent Review

@madster456
Copy link
Collaborator

madster456 commented Jan 3, 2026

Can you add testing? Following our existing testing structure.

Verifies that JWT tokens are properly refreshed with updated claims
(like selected_team_id) after calling setSelectedTeam() or user.update().

Tests:
- setSelectedTeam() triggers token refresh with correct team ID
- user.update() triggers token refresh with correct team ID
- Old and new tokens are different (new token issued, not mutated)
- Multiple team switches all produce fresh tokens
@cmux-agent
Copy link

cmux-agent bot commented Jan 3, 2026

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

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

🧹 Nitpick comments (1)
apps/e2e/tests/js/user-update-token-refresh.test.ts (1)

1-172: Consider adding a test for the null selected_team_id case during onboarding.

The PR description mentions that "the JWT is not refreshed; it can contain the old team ID or remain null (noted when a user signs up and is onboarding)." While your tests comprehensively cover team switching scenarios, you might want to add a specific test that:

  1. Signs up a new user
  2. Verifies the initial token has selected_team_id: null (or undefined)
  3. Creates and selects the first team
  4. Verifies the token is refreshed with the correct selected_team_id

This would explicitly document the onboarding flow mentioned in the PR description.

💡 Optional test for null selected_team_id during onboarding

You could add a test like this:

it("should refresh JWT token from null selected_team_id during initial onboarding", async ({ expect }) => {
  const { clientApp } = await createApp({
    config: {
      clientTeamCreationEnabled: true,
    },
  });

  // Sign up a user
  await signUp(clientApp, "onboarding@test.com");
  const user = await clientApp.getUser({ or: "throw" });

  // Get initial token and verify selected_team_id is null/undefined
  const initialAccessToken = await user.getAccessToken();
  expect(initialAccessToken).toBeDefined();
  const initialPayload = jose.decodeJwt(initialAccessToken!);
  expect(initialPayload.selected_team_id).toBeNullish(); // or toBeUndefined()

  // Create first team (onboarding scenario)
  const firstTeam = await user.createTeam({ displayName: "First Team" });
  await user.setSelectedTeam(firstTeam);

  // Verify token is refreshed with the correct team
  const newAccessToken = await user.getAccessToken();
  const newPayload = jose.decodeJwt(newAccessToken!);
  expect(newPayload.selected_team_id).toBe(firstTeam.id);
  expect(newAccessToken).not.toBe(initialAccessToken);
});
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between add379e and cb5727a.

📒 Files selected for processing (1)
  • apps/e2e/tests/js/user-update-token-refresh.test.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Always add new E2E tests when changing the API or SDK interface
For blocking alerts and errors, never use toast; use alerts instead as they are less easily missed by the user
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error); use loading indicators and async callbacks instead, or use runAsynchronously/runAsynchronouslyWithAlert for error handling
Use ES6 maps instead of records wherever you can

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer .toMatchInlineSnapshot over other selectors in tests when possible; check snapshot-serializer.ts for formatting details

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,css}: Keep hover/click transitions snappy and fast; avoid fade-in delays on hover. Apply transitions after action completion instead, like smooth fade-out when hover ends
Use hover-exit transitions instead of hover-enter transitions; for example, use 'transition-colors hover:transition-none' instead of fade-in on hover

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them; prefer using client components and hooks like usePathname instead of await params to keep pages static

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
{.env*,**/*.{ts,tsx,js}}

📄 CodeRabbit inference engine (AGENTS.md)

Prefix environment variables with STACK_ (or NEXT_PUBLIC_STACK_ if public) so changes are picked up by Turborepo and improves readability

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
apps/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always add new E2E tests when changing API or SDK interface; err on the side of creating too many tests due to the critical nature of the industry

Files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
🧠 Learnings (2)
📚 Learning: 2025-12-04T18:03:49.984Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T18:03:49.984Z
Learning: Applies to **/*.{ts,tsx} : Always add new E2E tests when changing the API or SDK interface

Applied to files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
📚 Learning: 2025-12-04T18:03:49.984Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T18:03:49.984Z
Learning: Applies to apps/e2e/**/*.{ts,tsx} : Always add new E2E tests when changing API or SDK interface; err on the side of creating too many tests due to the critical nature of the industry

Applied to files:

  • apps/e2e/tests/js/user-update-token-refresh.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Vercel Agent Review
🔇 Additional comments (5)
apps/e2e/tests/js/user-update-token-refresh.test.ts (5)

1-15: LGTM! Clean imports and helper setup.

The imports are appropriate for JWT token testing, and the signUp helper function follows DRY principles by centralizing the sign-up logic used across all tests.


17-57: Excellent test coverage for setSelectedTeam.

This test comprehensively verifies that:

  1. Tokens are reissued (not reused)
  2. The selected_team_id claim is updated correctly
  3. The new token differs from the initial token

The assertions are thorough and directly address the PR objective of ensuring JWTs are refreshed after user updates.


59-91: Good coverage of the user.update() API path.

This test ensures that token refresh works not just for setSelectedTeam but also for the more general user.update() method when changing selectedTeamId. This dual coverage is valuable for catching regressions in either code path.


93-129: Valuable test for verifying token reissuance.

This test explicitly verifies that old tokens aren't reused after setSelectedTeam, which directly addresses the problem statement in the PR description. The comparison of old vs. new token claims (lines 127-128) provides additional confidence that the token refresh is working correctly.


131-172: Comprehensive edge case coverage for multiple team switches.

This test ensures that token refresh works correctly across multiple sequential team changes, including switching back to a previously selected team. The assertions that all three tokens are unique (lines 169-171) are particularly valuable for catching potential caching issues where the SDK might incorrectly reuse an old token.

@promptless
Copy link
Contributor

promptless bot commented Jan 21, 2026

📝 Documentation updates detected!

New suggestion: Clarify that JWT tokens refresh on user data changes

@madster456
Copy link
Collaborator

@deanpektas27 Dean, thanks for the PR. This was actually included in #1069 and is merged at this point. We appreciate you contributing nonetheless!

Thanks,


@madster456 madster456 closed this Jan 28, 2026
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.

4 participants