Diagnosing sign-in failures
All domains, identifiers, and log values in these examples are fictional.
On Monday morning, the printer's support queue has three new messages. The first customer says that Continue with your photo account shows an error page at the photo service. The second says sign-in worked last week, but now it returns them to the printer with "We couldn't sign you in." The third says the screen keeps flickering between the two sites until the browser gives up.
All three are sign-in failures, and each stopped somewhere different. Diagnosing them starts with finding where.
Finding the stage
A sign-in passes through its stages in a fixed order, and each leaves different evidence behind:
| Stage | Where the person ends up | What the printer can know |
|---|---|---|
| The request | On the provider's error page | The redirect URI, scopes, and other parameters it sent |
| At the provider | Still at the provider | Only that the pending attempt was never completed |
| The callback | Back at the printer, with an error | Whether a pending attempt matched, the iss received, and any error code |
| The token exchange | Back at the printer, with an error | The HTTP status and error code from the token endpoint |
| ID token validation | Back at the printer, with an error | The failing check, the issuer, the kid, and the token's times |
| Account and session | Signed in to the wrong account, or not for long | The issuer and subject looked up, and whether the session cookie came back |
Where the person ended up narrows things quickly. If they never came back from the photo service, the printer did not refuse them. If they came back and then failed, the printer's records should say which check did.
That depends on what the printer recorded. When validation fails listed the fields: the stage, the failing check, the issuer, the key identifier, and a correlation ID, never the code or the tokens. The second customer's failure was recorded like this:
{
"time": "2026-10-05T08:14:07Z",
"message": "Sign-in failed during ID token validation",
"correlation_id": "corr-5521",
"issuer": "https://auth.photos.example",
"stage": "id_token_validation",
"check": "iat",
"kid": "photos-rs-2026-09",
"token_iat": 1791188347,
"server_time": 1791188047
}
Before the ID token
An error page at the provider. The first customer never returned. The photo service showed its own error because the request's redirect_uri did not exactly match a registered address, and a provider will not send a browser, even with an error, to an address it does not recognize. The printer sees only attempts that expire without a callback, so a rising count of them is worth watching. The causes are usually small: a new host name, a proxy in front of the application that leads it to build http:// addresses, a trailing slash, or the connection callback, /oauth/callback, sent with a sign-in. The redirect URI is not secret, so the printer can log what it sent and compare it with the registration character by character.
invalid_client from the token endpoint. The callback worked, but the photo service could not authenticate the printer. Typical causes are a secret rotated in the registration but not on every server, a deployment holding the test client's secret, or punctuation in a secret that was not form-encoded, as Client authentication methods warned. Log which server made the request, the client ID, and a version label for the credential it loaded, never the secret or the Authorization header. If only some requests fail, look for the server that missed a rotation. Its neighbor, invalid_grant, points at the code instead: used twice, exchanged too late, or sent with a different verifier or redirect URI.
No pending sign-in. Sometimes a valid-looking response arrives and the printer finds no pending attempt for this browser, because the cookie that identifies the attempt did not come with it. Record whether the callback carried that cookie, as yes or no rather than its value, along with the request method, the host it arrived on, and how long ago the attempt started. Those facts separate the usual causes:
- The attempt cookie is
SameSite=Strict, or it isLaxwhile the provider usesform_post. A cross-site POST carries neither, as Response types and response modes described. - The attempt started on
www.printer.example, but the registered callback is onprinter.example. A host-only cookie, such as one named with the__Host-prefix, is never sent to the other host. - The cookie expired while the person was at the provider, perhaps setting up a new authenticator, which took longer than the attempt's lifetime.
Rejected ID tokens
Issued in the future. The second customer's log entry failed on iat. The token says it was issued at 08:19:07, and the printer's clock says 08:14:07. The photo service did not sign it in the future. That server's clock has drifted five minutes behind. Failures clustered on one server, with token times consistently offset from server_time, confirm it. The fix is to synchronize every server with a reliable time source and alert when one drifts. Widening the clock tolerance would hide the drift and weaken every time check, as When validation fails warned.
Unknown key. On the morning the photo service starts signing with photos-rs-2027-01, a printer whose key cache never refreshes rejects every sign-in, because the token names a kid its cached set lacks. A correctly built printer refetches the set once, within the limit Signing keys and rotation described, and carries on. Key identifiers are public, so the printer can log the token's kid, the identifiers it holds, and when and how it last fetched them. A failed refetch points at the network. A successful refetch that still lacks the key points at the token: check its iss, because a token from a test provider sent to production fails exactly this way.
Nonce mismatch. The ID token is otherwise valid, but its nonce does not match the attempt. Sometimes that is a replay, like the token carrying demo-nonce-2 in Expiration and nonce checks. More often it is the printer's own bookkeeping. If the nonce lives in one fixed cookie, starting a second sign-in in another tab overwrites it, and the first tab's sign-in then fails. Storing the nonce with its pending attempt, found by state, lets attempts run side by side. The evidence is whether an attempt was found, how many were pending for this browser, and whether the token's nonce matched a different one of them.
Sign-in loops
The third customer's screen flickered between the two sites. In a loop, each round trip ends in an answer and the printer immediately asks again. Two loops are common, and only one of them shows on screen.
A session that never sticks. This is the third customer's loop. The callback succeeds and sets the printer's session cookie, but the next request arrives without it. The printer sends the browser to sign in, and because the photo service still has a session for the customer, it sends the browser straight back within a moment, again and again. Browsers silently reject a __Host- cookie that lacks Secure, carries a Domain attribute, or is set over plain HTTP, as on a test server without TLS. The evidence is the attributes the callback set on __Host-printer-session, never its value, and whether the next request carried it.
A prompt=none loop. Suppose the printer checks silently on each page load whether you are signed in at the photo service, and treats login_required as a reason to try again. As a top-level redirect, for someone with no session at the photo service, every page bounces there and back before it loads. In a hidden frame, the loop never shows on screen at all. A browser that blocks third-party cookies never sends the photo service its own session cookie, so the answer is login_required every time, even for someone who is signed in there, and the only trace is a steady stream of silent requests in both sites' logs. As Authentication errors put it, login_required is an answer. The printer records that the silent check failed for this browser and does not ask again until something changes, such as you choosing to sign in.
Each loop has its own fix, and all of them need a limit too. The printer counts sign-in attempts for each browser over a short window, and after a few it stops redirecting, records the attempts under their correlation IDs, and shows a page that explains what happened. The third customer's next visit ends on that page, and the printer's logs hold the sequence of attempts that shows why.
Once the session sticks, the printer knows who you are. Whether you may cancel an order, see a shared photo book, or manage other people's accounts is a different question, answered by authorization, and Designing permissions starts there.