xai-org/grok-build · error · OidcError

MissingIdToken

MissingIdToken

Error message

OidcError::MissingIdToken

What it means

OidcError::MissingIdToken is thrown by extract_user_info when no `principal_type == team` shortcut applies and the optional id_token parameter is None (protocol.rs:747). Without an ID token there is nothing to validate or extract user claims from, so the login flow cannot proceed.

Source

Thrown at crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs:747

            user_id: team_user_id,
            email: None,
            first_name: None,
            last_name: None,
            profile_image_asset_id: None,
            principal_type: Some(crate::auth::model::TEAM_PRINCIPAL_TYPE.to_string()),
            principal_id: principal_id.map(ToOwned::to_owned),
            team_id: principal_id.map(ToOwned::to_owned).or(fallback_team_id),
            team_name: None,
            team_role: None,
            organization_id: None,
            organization_name: None,
            organization_role: None,
            user_blocked_reason: None,
            team_blocked_reasons: vec![],
            coding_data_retention_opt_out: crate::auth::default_coding_data_retention_opt_out(),
        });
    }
    let token = id_token.ok_or_else(|| anyhow::Error::new(OidcError::MissingIdToken))?;
    validate_and_extract_user_info(
        token,
        discovery,
        expected_issuer,
        expected_client_id,
        expected_nonce,
    )
    .await
    .map(|mut user_info| {
        user_info.principal_type = principal_type.map(ToOwned::to_owned);
        user_info.principal_id = principal_id.map(ToOwned::to_owned);
        if user_info.team_id.is_none() {
            user_info.team_id = fallback_team_id;
        }
        user_info
    })
    .map_err(|e| anyhow::Error::new(OidcError::IdTokenValidationFailed(e.to_string())))
}

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Ensure the `openid` scope is included in the authorization request so the provider issues an id_token.
  2. Log the token endpoint response and confirm an id_token field is present; fix scope/flow parameters if not.
  3. Use the team-principal path (principal_type=team) only when intentionally skipping ID token validation; otherwise supply the token.

Example fix

// before: request without openid scope
let auth_url = format!("{auth}?client_id={id}&redirect_uri={uri}&response_type=code");
// after: request openid scope so an id_token is returned
let auth_url = format!("{auth}?client_id={id}&redirect_uri={uri}&response_type=code&scope=openid%20profile%20email");
Defensive patterns

Strategy: validation

Validate before calling

// verify the token response carries an id_token before calling extract_user_info
let id_token = token_response.get("id_token").and_then(|v| v.as_str());
if id_token.is_none() {
    eprintln!("provider returned no id_token; ensure 'openid' scope requested");
}

Type guard

fn has_id_token(resp: &serde_json::Value) -> bool { resp.get("id_token").map_or(false, |v| v.as_str().map_or(false, |s| !s.is_empty())) }

Try / catch

match result {
    Err(e) if e.to_string().contains("MissingIdToken") => eprintln!("no id_token in response; check scopes/flow"),
    other => other,
}

Prevention

When it happens

Trigger: The token endpoint response (or device/callback flow result) contained no id_token, and extract_user_info was called with id_token=None for a non-team principal.

Common situations: The IdP is configured to not return id_token for the requested flow/scopes; the token response JSON was parsed with a wrong field name; `openid` scope missing from the authorization request so no id_token is issued; partial response from a race fallback path (full_login_flow_via_race).

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/4ab7103f558cb83e. Report an issue: GitHub.