xai-org/grok-build · error · OidcError
OidcError::ForceLoginNoPrincipalsAllowed
Error message
OidcError::ForceLoginNoPrincipalsAllowed
What it means
OidcError::ForceLoginNoPrincipalsAllowed is raised by enforce_login_principal when the force_login_team_uuid policy is set to an empty AnyOf list. Since no team principal is permitted, the code fails closed and rejects the login instead of allowing everyone. It indicates a deployment misconfiguration, not a user mistake.
Source
Thrown at crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs:182
/// Reject a token whose principal isn't allowed, BEFORE persisting (no partial
/// state). A restriction also rejects a token with no principal (else picking
/// "personal" on the consent page defeats it); an empty `AnyOf` fails closed.
///
/// The `actual` principal comes from the access-token claim
/// (`peek_access_token_principal`, an unverified `insecure_decode`). This
/// client-side check is fail-fast UX / defense-in-depth — NOT the security
/// boundary: the server re-validates the signed token on every API call and
/// is authoritative, so a locally tampered token still cannot reach the API.
pub(crate) fn enforce_login_principal(
policy: Option<&ForceLoginTeam>,
actual: Option<&str>,
) -> anyhow::Result<()> {
let allowed: &[String] = match policy {
None => return Ok(()),
Some(ForceLoginTeam::Single(id)) => std::slice::from_ref(id),
Some(ForceLoginTeam::AnyOf(ids)) if ids.is_empty() => {
tracing::warn!("OIDC: force_login_team_uuid is an empty list; failing closed");
return Err(anyhow::Error::new(OidcError::ForceLoginNoPrincipalsAllowed));
}
Some(ForceLoginTeam::AnyOf(ids)) => ids,
};
if let Some(actual) = actual
&& allowed.iter().any(|a| a == actual)
{
return Ok(());
}
let expected = if allowed.len() == 1 {
format!("team {}", allowed[0])
} else {
format!("one of teams: {}", allowed.join(", "))
};
tracing::warn!(
expected = %expected,
actual = ?actual,
"OIDC: login principal does not satisfy required policy; rejecting"
);View on GitHub (pinned to bc7f02eddd)
Solutions
- Remove force_login_team_uuid entirely from config to allow any principal
- Populate the list with at least one team UUID
- If the intent was 'block logins', disable OIDC login instead of using an empty allow-list
Example fix
// before force_login_team_uuid: [] // after force_login_team_uuid: ["<team-uuid>"] // or delete the key to unpin
Defensive patterns
Strategy: validation
Validate before calling
// validate config before starting the login flow
fn validate_force_login(policy: Option<&ForceLoginTeam>) -> Result<(), String> {
match policy {
None | Some(ForceLoginTeam::Single(_)) => Ok(()),
Some(ForceLoginTeam::AnyOf(ids)) if ids.is_empty() => {
Err("force_login_team_uuid is empty; remove it or list >= 1 team".into())
}
Some(ForceLoginTeam::AnyOf(_)) => Ok(()),
}
} Type guard
fn has_allowed_principals(policy: Option<&ForceLoginTeam>) -> bool {
match policy {
None => true,
Some(ForceLoginTeam::Single(_)) => true,
Some(ForceLoginTeam::AnyOf(ids)) => !ids.is_empty(),
}
} Prevention
- Never commit force_login_team_uuid as an empty list; treat empty as a config error
- Add a startup config check that rejects empty allow-lists before login
- Validate env/secret expansions that populate the list are non-empty
When it happens
Trigger: Calling the OIDC login flow (run_login_flow_with_config -> enforce_login_principal) when config.oidc.force_login_team_uuid is ForceLoginTeam::AnyOf with a zero-length ids vector.
Common situations: An administrator writes `force_login_team_uuid: []` in the deployment config (or an env/secret expansion yields an empty list) intending to disable pinning, but the empty list means 'no team allowed'.
Related errors
- no OIDC auth entry found in {}. Run `grok login` first.
- auth entry has no oidc_issuer — cannot refresh expired token
- auth entry has no oidc_client_id — cannot refresh expired to
- OidcError::IssuerMismatch
- Session does not exist locally (session registry is disabled
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/732b57cd550c7ea3.
Report an issue: GitHub.