{"record":{"id":"44e2e892bf98655d","repo":"xai-org/grok-build","slug":"oidcerror-callbackauthfailed","errorCode":null,"errorMessage":"OidcError::CallbackAuthFailed","messagePattern":"OidcError::CallbackAuthFailed","errorType":"exception","errorClass":"OidcError","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/oidc/login.rs","lineNumber":297,"sourceCode":"    };\n\n    drop(tx);\n\n    let result = tokio::select! {\n        r = tokio::time::timeout(AUTH_CALLBACK_TIMEOUT, rx.recv()) => {\n            r.map_err(|_| anyhow::Error::new(OidcError::CallbackTimeout))?\n                .ok_or_else(|| anyhow::Error::new(OidcError::CallbackChannelClosed))?\n        }\n        _ = client_bridge => {\n            rx.recv().await\n                .ok_or_else(|| anyhow::Error::new(OidcError::CallbackChannelClosed))?\n        }\n    };\n\n    let _ = shutdown_tx.send(());\n    let _ = server.await;\n\n    result.map_err(|e| anyhow::Error::new(OidcError::CallbackAuthFailed(e)))\n}\n\n/// Race loopback callback against stdin paste.\nasync fn race_callback_and_stdin(\n    listener: TcpListener,\n    enable_stdin: bool,\n) -> anyhow::Result<Callback> {\n    tracing::debug!(\n        enable_stdin = enable_stdin,\n        \"OIDC: waiting for auth code (loopback + stdin)\"\n    );\n    let (tx, mut rx) = tokio::sync::mpsc::channel::<CallbackResult>(1);\n    let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();\n\n    let app = build_callback_router(tx.clone());\n    let server = tokio::spawn(async move {\n        let _ = axum::serve(listener, app)\n            .with_graceful_shutdown(async {","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/oidc/login.rs#L279-L315","documentation":"After a code was received in `race_callback_and_client_ui`, the flow performs the token exchange and any post-callback processing. If that inner `result` is Err, it is wrapped as OidcError::CallbackAuthFailed, preserving the underlying cause. This is not a transport failure — the callback arrived but turning it into tokens failed.","triggerScenarios":"result.map_err(|e| OidcError::CallbackAuthFailed(e)) at the end of race_callback_and_client_ui — the awaited token exchange / authorization-code processing returned an error.","commonSituations":"Authorization code already redeemed or expired; PKCE code_verifier mismatch; wrong client_id/client_secret; IDP returned an OAuth error (invalid_grant); clock skew invalidating tokens; IDP temporarily down.","solutions":["Retry the login from scratch — authorization codes are single-use; a reused code yields invalid_grant.","Verify client_id, client_secret, and redirect_uri exactly match the IDP application registration.","Check PKCE: code_verifier must be the one bound to the authorize URL; don't re-run only the token step.","Inspect the wrapped inner error (`e`) for the IDP's OAuth error description (e.g. invalid_grant, unauthorized_client).","Ensure system clock is accurate (NTP) — large skew breaks token validation."],"exampleFix":"// before: reusing a stale code fails\nlet tokens = exchange(code_from_previous_run)?;\n// after: redo the full flow to obtain a fresh code\nlet (auth, _created) = run_login_flow(config, auth_manager, channels).await?;","handlingStrategy":"retry","validationCode":"// preflight: verify OIDC config before starting the flow\nlet oidc = config.oidc.as_ref().ok_or(\"oidc not configured\")?;\nassert!(!oidc.client_id.is_empty(), \"client_id must be set\");\nreqwest::get(format!(\"{}/.well-known/openid-configuration\", oidc.issuer)).await?;","typeGuard":"fn is_callback_auth_failed(err: &anyhow::Error) -> Option<&anyhow::Error> {\n    err.downcast_ref::<OidcError>()\n        .and_then(|e| match e {\n            OidcError::CallbackAuthFailed(inner) => Some(inner),\n            _ => None,\n        })\n}","tryCatchPattern":"match run_login_flow(config, auth_manager, channels).await {\n    Ok(res) => res,\n    Err(e) if is_callback_auth_failed(&e).is_some() => {\n        // codes are single-use: restart the whole flow for a fresh code\n        run_login_flow(config, auth_manager, channels).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Never reuse authorization codes — always run the full flow","Double-check client_id/client_secret/redirect_uri against the IDP registration","Keep PKCE verifier tied to the same authorize request","Sync system clock (NTP) to avoid token validation skew"],"tags":["oidc","token-exchange","invalid-grant"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}