Skip to main content

SSO: intermittent 401 at /oauth2/idpresponse - hourly forced re-auth + per-tab guard

**Symptom:** Users intermittently land on a bare `401 Authorization Required` page at `https://coachpulse.coach.com/oauth2/idpresponse`. No Coach branding, no app shell. **Why it looks like nothing:** That page is generated by the load balancer. The request never reached our container and no application code ran, which is why nothing appears in app logs or the browser console. ## Root condition Every user is forced through a full re-authentication once an hour. The ALB session is set to SessionTimeout 3600 and the token scope in use (openid only) issues no refresh token, so the only way to renew the session cookie is a complete page navigation back through Azure and Cognito. An associate working a full shift makes that round trip 8 or 9 times, and each one is an opportunity to hit this 401. This is documented in our own codebase, in the header of src/lib/albReauth.js (lines 3-7) - the module exists specifically to work around it. ## Failure modes, ranked 1. **Two tabs re-authenticate at once (ours, P0).** The re-auth guard lives in sessionStorage, which is per-tab by definition. Two open tabs both navigate at expiry; the second overwrites the first's nonce cookie and the first tab's callback fails state validation. 2. **A spent authorization code is replayed (P1).** Codes are single-use and short-lived. Refreshing the callback URL, or restoring the tab from history, replays a consumed code. 3. **The nonce cookie does not come back (P1).** Sign-in starting on one host and finishing on another, cookies cleared mid-flow, or a corporate browser policy stripping them. 4. **Client secret drift on the listener (P2).** If the Cognito app client secret was rotated without updating the listener rule, every exchange fails constantly rather than occasionally. ## FutureProof ships - **A1 (P0):** Make the re-auth guard cross-tab. Replace the sessionStorage flag with a timestamped localStorage lock plus a BroadcastChannel, with a TTL so a crashed tab cannot deadlock the others. Contained to albReauth.js and useProfile.js. - **A2 (P1):** Give session expiry a deliberate screen with one action that navigates to the site root, never to a callback URL. - **A3 (P1):** Instrument it. Emit a Sentry breadcrumb when the guard is set and another when it clears on success. Every set without a matching clear is this failure. ## Tapestry infra must do - **I1 (P0, the actual fix):** Enable the additional scopes on the Consumer App Client (open since May) and raise the session timeout, so the ALB renews silently instead of bouncing every user through Azure hourly. This removes the cause rather than mitigating it. - **I2 (P0):** Pull ALB access log lines for elb_status_code 401 on /oauth2/idpresponse. The actions_executed field names the failing step and separates modes 1 and 3 from mode 4. Pair with the ELBAuthFailure metric on the 443 listener. - **I3 (P1):** Confirm the client secret on the listener's authenticate action still matches the app client. ## Interim guidance Start from the site root, not by refreshing the error page (its URL contains an already-consumed code and will fail on every refresh). Close duplicate tabs until A1 ships. Do not circulate the error URL - it carries a live code and state value. --- Full analysis: Coach Pulse - SSO Session and Callback Failures v1 (FutureProof, 25 Aug 2026).
Status: Completed And Live In Production

Log in to comment and vote

No comments yet

Be the first to share your thoughts.