feat(core): introduce experimental refreshUserInfo APIs - #219
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
📝 WalkthroughWalkthroughThis PR adds refresh-userinfo support across the auth core package, including new API contracts, server and client entry points, route wiring, rate-limit handling, error codes, supporting utilities, and test coverage. ChangesrefreshUserInfo API implementation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
packages/core/src/shared/errors.ts (1)
843-850: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueClarify awkward message phrasing.
The
INVALID_REFRESH_USER_INFO_RESPONSEcatalog message contains "during a synchronization sync block" which is redundant and unclear. Consider simplifying to something like "during a profile synchronization request."💬 Suggested rewording
message: - "The outbound HTTP request to the provider user info profile endpoint returned a non-2xx status code during a synchronization sync block. The response 'ok' field resolved to false.", + "The outbound HTTP request to the provider user info profile endpoint returned a non-2xx status code during a profile synchronization request. The response 'ok' field resolved to false.",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/shared/errors.ts` around lines 843 - 850, The INVALID_REFRESH_USER_INFO_RESPONSE catalog message has awkward redundant wording (“synchronization sync block”) that should be simplified. Update the message string in the errors catalog entry to use clearer phrasing, such as referring to a profile synchronization request, and keep the change localized to the INVALID_REFRESH_USER_INFO_RESPONSE definition so the AuthError metadata stays unchanged.packages/core/src/router/rate-limiter.ts (1)
79-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider adding an explicit return type to
verifyRateLimit.
defaultValuesreturns a union of different object shapes depending onaction, andverifyRateLimit's return type is inferred. This creates a type-safety gap at call sites, which are already worked around withascasts (e.g.,rateLimit as RefreshUserInfoAPIReturn<DefaultUser>). Adding an explicit return type or a generic parameter would surface mismatches at compile time.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/router/rate-limiter.ts` around lines 79 - 94, The return type of verifyRateLimit is currently inferred, which hides shape mismatches from defaultValues and forces unsafe casts at call sites. Add an explicit return type for verifyRateLimit, preferably a generic that ties the action key to the corresponding RateLimiterConfig/defaultValues shape, and keep the success/error object consistent with that type. Use verifyRateLimit and defaultValues as the main symbols when updating the signature so compile-time checks catch invalid combinations instead of relying on as casts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/actions/callback/userinfo.ts`:
- Line 86: Remove the stray debug console output in the user info error path and
use the existing structured logger instead. In the error handling inside the
userinfo action, delete the console.log call and, if error details are needed,
attach them to the existing logger?.log("OAUTH_USERINFO_REQUEST_FAILED") call
via structuredData so the failure is recorded without polluting stdout or
leaking provider details.
In `@packages/core/src/api/refreshUserInfo.ts`:
- Line 72: Rename the refresh-user-info auth error code to use the correct
spelling everywhere it is defined and thrown: update the AuraAuthError code in
refreshUserInfo and the matching catalog entry in shared/errors so both use
INVALID_ACCESS_TOKEN_RETRIEVING_REFRESH_USER_INFO instead of
INVALID_ACCESS_TOKEN_RETRIVING_REFRESH_USER_INFO. Keep the symbol names
AuraAuthError, refreshUserInfo, and the error catalog entry in sync so the code
and lookup remain consistent.
- Around line 75-86: The `getUserInfo` call in `refreshUserInfo` is passing the
wrong value for `expires_in`: `AccessTokenContext.expiresIn` expects a relative
lifetime in seconds, but the code is using the absolute `tokens.expiresAt`
timestamp. Update the `userInfo.request` payload construction in
`refreshUserInfo` so `expires_in` is derived from the token’s remaining lifetime
rather than `expiresAt`, and keep the mapping aligned with the `tokens` fields
used alongside `access_token`, `refresh_token`, and `id_token`.
In `@packages/core/src/router/rate-limiter.ts`:
- Around line 62-74: Add a dedicated `defaultValues()` branch for
`getProviderTokens` in `rate-limiter.ts`; the current default payload omits the
`tokens` field, so rate-limited failures won’t match the expected shape. Update
the `switch` in `defaultValues(action)` to return `tokens: null` for
`getProviderTokens`, while keeping the existing behavior for `refreshUserInfo`,
`signIn`, `signInCredentials`, `signUp`, and `updateSession`.
In `@packages/core/src/shared/errors.ts`:
- Line 119: The error code constant has a typo in its name: update the
INVALID_ACCESS_TOKEN_RETRIVING_REFRESH_USER_INFO symbol in the errors catalog to
INVALID_ACCESS_TOKEN_RETRIEVING_REFRESH_USER_INFO, and then update every
matching reference in the auth error path, including the AuraAuthError throw
site(s), so the exported API name stays consistent everywhere.
In `@packages/core/test/api/refreshUserInfo.test.ts`:
- Around line 11-13: The test cleanup in afterEach only resets environment
stubs, so global stubs like fetch can leak between tests. Update the existing
afterEach in refreshUserInfo.test.ts to also call vi.unstubAllGlobals()
alongside vi.unstubAllEnvs(), so all vi.stubGlobal usage is properly restored
after each test.
- Around line 246-259: The mocked getUserInfo success responses are missing
headers, which causes response.headers.get("Content-Type") to throw before the
intended OAuth error handling runs. Update the ok: true fetch mocks in
refreshUserInfo.test.ts to include a Headers object with Content-Type set to
application/json so the getUserInfo path can parse the response and reach the
expected error assertion. Use the getUserInfo mock response setup in the
relevant test cases to locate and fix both affected mocks.
---
Nitpick comments:
In `@packages/core/src/router/rate-limiter.ts`:
- Around line 79-94: The return type of verifyRateLimit is currently inferred,
which hides shape mismatches from defaultValues and forces unsafe casts at call
sites. Add an explicit return type for verifyRateLimit, preferably a generic
that ties the action key to the corresponding RateLimiterConfig/defaultValues
shape, and keep the success/error object consistent with that type. Use
verifyRateLimit and defaultValues as the main symbols when updating the
signature so compile-time checks catch invalid combinations instead of relying
on as casts.
In `@packages/core/src/shared/errors.ts`:
- Around line 843-850: The INVALID_REFRESH_USER_INFO_RESPONSE catalog message
has awkward redundant wording (“synchronization sync block”) that should be
simplified. Update the message string in the errors catalog entry to use clearer
phrasing, such as referring to a profile synchronization request, and keep the
change localized to the INVALID_REFRESH_USER_INFO_RESPONSE definition so the
AuthError metadata stays unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d7127d6d-1d3d-4e8f-81f9-7a4009cc17ed
📒 Files selected for processing (9)
packages/core/src/@types/api.tspackages/core/src/@types/config.tspackages/core/src/actions/callback/userinfo.tspackages/core/src/api/createApi.tspackages/core/src/api/refreshUserInfo.tspackages/core/src/router/rate-limiter.tspackages/core/src/shared/errors.tspackages/core/src/shared/utils.tspackages/core/test/api/refreshUserInfo.test.ts
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/actions/user/refresh.ts`:
- Around line 10-13: The z.enum() call in the user refresh action is using the
old string-based second argument, which is no longer valid in Zod 4. Update the
oauth schema in refresh.ts to pass the message through the Zod error object
format, using the existing “The OAuth provider is not supported or invalid.”
text, and keep the change localized to the z.enum(...) call that validates
OAuthProviderRecord keys.
In `@packages/core/src/api/refreshUserInfo.ts`:
- Around line 75-77: The expiresIn calculation in refreshUserInfo is mixing
units because tokens.expiresAt is in Unix seconds while Date.now() is in
milliseconds, causing incorrect zero values for future expiries. Update the
subtraction logic in refreshUserInfo to compare expiresAt against Unix seconds
by using Math.floor(Date.now() / 1000), and keep the existing
Math.max/Math.floor behavior so custom userInfo handlers receive a correct
expires_in value.
- Around line 103-108: Handle the null-return shape from createStandardSession
in refreshUserInfo: when userClaims.sub is missing, it returns an object with
session set to null and headers, but the current assignment stores the whole
object in session. Update refreshUserInfo to destructure the
createStandardSession result and use only its session value in the response
body, while still preserving the returned headers as needed, so the response
stays { session: null, success: true } in that edge case.
In `@packages/core/src/client/client.ts`:
- Around line 339-346: The JSDoc example in the refreshUserInfo documentation
shows the wrong user identifier field; update the example session object to use
sub instead of id so it matches the actual AuthClient session shape and related
expectations. Keep the rest of the example unchanged and ensure the `@example`
near refreshUserInfo reflects the same identifiers used by the client API.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a36ed73e-0654-41a6-a196-85aded39f3c3
📒 Files selected for processing (12)
packages/core/CHANGELOG.mdpackages/core/src/actions/callback/userinfo.tspackages/core/src/actions/user/refresh.tspackages/core/src/api/createApi.tspackages/core/src/api/refreshUserInfo.tspackages/core/src/client/client.tspackages/core/src/createAuth.tspackages/core/src/router/rate-limiter.tspackages/core/src/shared/errors.tspackages/core/src/shared/utils.tspackages/core/test/actions/user/refresh.test.tspackages/core/test/api/refreshUserInfo.test.ts
✅ Files skipped from review due to trivial changes (2)
- packages/core/CHANGELOG.md
- packages/core/src/actions/callback/userinfo.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/core/src/api/createApi.ts
- packages/core/src/shared/errors.ts
- packages/core/src/router/rate-limiter.ts
- packages/core/src/shared/utils.ts
- packages/core/test/api/refreshUserInfo.test.ts
Description
This pull request introduces the experimental
refreshUserInfo()API for both server-side and client-side applications.The new API allows applications to refresh a user's profile information and update the current session without requiring the user to sign in again. It fetches the latest user information from the OAuth or OpenID Connect (OIDC) provider's configured
userInfoendpoint, reconstructs the user profile using the provider'sprofilemapping function, and updates the session with the refreshed data.On the server, the functionality is exposed through
auth.api.refreshUserInfo(). On the client,createAuthClient().refreshUserInfo()communicates with thePOST /providers/:provider/user/refreshendpoint to perform the same operation.Usage
Server-side
Client-side
Note
refreshUserInfo()requires the configured provider to expose auserInfoendpoint. Providers that do not implement user information retrieval cannot support this feature.@coderabbitai ignore