Forum Discussion
ssoSilent() not working across Next.js apps — timed_out or account picker on localhost
Three Next.js apps, one app registration, msal-browser 5.6.1. ssoSilent either times out (redirect_bridge_timeout) or falls through to the account picker.
There is a wrong assumption underneath the whole approach, and once you see it the behavior makes sense. localStorage is scoped to a full origin, which is scheme plus host plus port. So localhost:3000 and localhost:3001 are different origins and do not share localStorage at all. Your SsoInitializer reading a sibling app's MSAL cache cannot work across ports. Worth knowing it also will not work across subdomains in production, app1.contoso.com and app2.contoso.com do not share localStorage either, so if cross app SSO works in prod today it is the Entra session cookie doing it, not shared cache.
So do not try to share the cache. Cross app SSO should come from the shared identity provider session, with each app independently calling ssoSilent on load. Answering your specific questions:
- ssoSilent can work on the session cookie alone, but only cleanly when there is exactly one session. With any ambiguity, or no cached account, MSAL does not know which account to pick and returns interaction_required, which is your account picker fall through. Pass a loginHint (or better, the sid) when you have one and it gets far more reliable.
- The silent callback page should be a truly minimal blank HTML page that does NOT load MSAL and does NOT call handleRedirectPromise. That call is what caused your redirect_bridge_timeout, the hidden iframe was trying to run MSAL inside itself. The parent app's MSAL instance is what monitors the iframe, so the target page just needs to exist and load fast. Your second attempt with blank HTML was the right direction.
- For local dev across different ports, accept that you are relying on a hidden iframe to login.microsoftonline.com, and that is exactly what Chrome's third party cookie restrictions block, which is the other reason for the timeouts. The robust local pattern is to run the apps behind one origin (a reverse proxy with path routing, all on localhost:3000) so they genuinely share context. If you must keep separate ports, test with third party cookies allowed for the login domain and know it is fragile by nature.
- The redirectUri for the silent request must be a registered SPA redirect URI, and a blank page is not just sufficient, it is recommended. It must not run MSAL.
Net: stop reading sibling localStorage, give each app its own MSAL instance, use acquireTokenSilent for cached tokens and ssoSilent with a loginHint only for the initial bootstrap, point it at an empty registered redirect page, and do your local testing behind a single origin.