Skip to content

Conversation

@shineli1984
Copy link
Collaborator

@shineli1984 shineli1984 commented Dec 17, 2025

Summary

Adding auth-nextjs for convenient integration with nextjs + SSR.


Note

Add @imtbl/auth-nextjs package (Auth.js v5 for Next.js App Router)

  • Client: ImmutableAuthProvider, useImmutableAuth, useAccessToken, useHydratedData, and CallbackPage for popup/redirect flows with client-side token refresh
  • Server: createImmutableAuth, createAuthConfig, getAuthenticatedData, createProtectedFetchers, withAuth, and createAuthMiddleware for SSR, route protection, and server actions
  • Token handling: server marks expired tokens; client refreshes and syncs via session updates; isTokenExpired utility and userinfo validation in credentials provider

Core @imtbl/auth updates

  • Emit TOKEN_REFRESHED (on silent refresh) and USER_REMOVED (on permanent auth errors) events; refine refresh logic and add tests

Sample app wiring

  • Add per-environment NextAuth API routes, provider integration, callback page, and UI demo; enable API routes via env flag and adjust Next config

Repo/config tweaks

  • Increase pre-commit Node heap, serialize network installs in .npmrc, add syncpack peer-deps ignore, add eslint/jest/tsup/tsconfig for new package

Written by Cursor Bugbot for commit 0d731e2. This will update automatically on new commits. Configure here.

@shineli1984 shineli1984 requested a review from a team as a code owner December 17, 2025 01:09
@socket-security
Copy link

socket-security bot commented Dec 17, 2025

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
HTTP dependency: npm @imtbl/contracts depends on https://github.com/immutable/seaport.git#1.6.0+im4

Dependency: seaport-16@https://github.com/immutable/seaport.git#1.6.0+im4

Location: Package overview

From: examples/contracts/package.jsonnpm/@imtbl/contracts@2.2.18

ℹ Read more on: This package | This alert | What are http dependencies?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Publish the HTTP URL dependency to a public or private package repository and consume it from there.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@imtbl/contracts@2.2.18. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
HTTP dependency: npm @imtbl/contracts depends on https://github.com/immutable/seaport-core.git#1.6.0+im2

Dependency: seaport-core-16@https://github.com/immutable/seaport-core.git#1.6.0+im2

Location: Package overview

From: examples/contracts/package.jsonnpm/@imtbl/contracts@2.2.18

ℹ Read more on: This package | This alert | What are http dependencies?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Publish the HTTP URL dependency to a public or private package repository and consume it from there.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@imtbl/contracts@2.2.18. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@nx-cloud
Copy link

nx-cloud bot commented Dec 17, 2025

View your CI Pipeline Execution ↗ for commit 0d731e2

Command Status Duration Result
nx run-many -p @imtbl/sdk,@imtbl/checkout-widge... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-15 22:25:40 UTC

@nx-cloud
Copy link

nx-cloud bot commented Dec 17, 2025

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit e9cfdc4

Command Status Duration Result
nx affected -t build,lint,test ❌ Failed 3m 12s View ↗
nx run-many -p @imtbl/sdk,@imtbl/checkout-widge... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-17 04:04:32 UTC


return handler(session, ...args);
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withAuth doesn't check for session errors

Medium Severity

The withAuth helper only checks if (!session) but doesn't check for session.error. When a refresh token is invalid (e.g., revoked or expired after 365 days), auth() returns a session with error: "RefreshTokenError". The handler would receive this invalid session and could fail when using the stale accessToken for API calls. The README explicitly documents that server code should check for session.error, but this helper doesn't implement that check.

Fix in Cursor Fix in Web

fetchData();
}
// eslint-disable-next-line
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure causes failed token refresh in useHydratedData

Medium Severity

The useHydratedData effect uses an empty dependency array, causing it to run immediately on mount before the Auth instance is initialized (Auth is created in a parent component's effect, which runs after child effects). When ssr: false due to an expired token, fetchData calls getAccessToken, which finds auth === null and then throws "Session expired" instead of waiting for Auth to initialize and refresh the token. This contradicts the documented behavior that promises automatic client-side token refresh when SSR is skipped.

Additional Locations (1)

Fix in Cursor Fix in Web

// 1. SSR was skipped (token expired) - need to refresh token and fetch
// 2. Server fetch failed - retry on client
// 3. No server data - need to fetch
const needsClientFetch = !ssr || Boolean(fetchError) || serverData === null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary client refetch when server returns null data

Medium Severity

The condition serverData === null in needsClientFetch causes unnecessary client-side fetches when the server successfully returns null as a legitimate response. When ssr=true and no fetchError, but data is null (e.g., "no results found"), the client incorrectly re-fetches instead of using the server's null result. The condition !ssr already handles when SSR was skipped, and Boolean(fetchError) handles server errors, making serverData === null redundant and incorrect.

Fix in Cursor Fix in Web

);

// Track if we've already started fetching to prevent duplicate calls
const hasFetchedRef = useRef(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hook state not synchronized when props change

Medium Severity

The useHydratedData hook initializes state with useState(serverData) but never syncs state when props change. If a component instance receives new props (e.g., navigating between routes using the same component), data retains the old value because useState only uses the initial value. Combined with hasFetchedRef preventing re-fetches, the component displays stale data from previous props instead of the new serverData.

Fix in Cursor Fix in Web

} finally {
setIsLoading(false);
}
}, [fetcher, getAccessToken]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing fetch cancellation causes stale data race condition

Medium Severity

The useHydratedData hook has a race condition where in-flight fetches are not cancelled when props change. When props change from ssr: false to ssr: true (e.g., during soft navigation after token refresh), the props-sync effect correctly sets the fresh serverData. However, the previously started client-side fetch continues running and when it completes, setData(result) overwrites the correct server data with stale results. There's no AbortController, fetch ID tracking, or staleness check to prevent this.

Additional Locations (1)

Fix in Cursor Fix in Web


hasFetchedRef.current = true;
fetchData();
}, [needsClientFetch, ssr, auth, fetchData]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client fetch fails when session not yet loaded

Medium Severity

When ssr=true but fetchError exists (server-side fetch failed), useHydratedData immediately attempts a client-side retry without waiting for the session to load from useSession(). The guard if (!ssr && !auth) return only waits for auth when ssr=false, but when ssr=true with a fetchError, it proceeds immediately. If useSession() is still in 'loading' state, getAccessToken() will throw "No access token available" because session is undefined. After this error, hasFetchedRef.current remains true, blocking retries even after the session loads.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants