Repository: astral-sh/uv
File: crates/uv-publish/src/trusted_publishing.rs (Rust)

Code before:
    }

    // GitLab CI flow: discover OIDC token via {AUD}_ID_TOKEN (PYPI_ID_TOKEN, TESTPYPI_ID_TOKEN)
    if env::var(EnvVars::GITLAB_CI).is_ok() {
        let audience = get_audience(registry, client).await?;
        let env_key = format!("{}_ID_TOKEN", audience.to_ascii_uppercase());
        if let Ok(jwt) = env::var(&env_key) {
            let publish_token = get_publish_token(registry, &jwt, client).await?;
            debug!("Received token, using trusted publishing via GitLab (audience: {audience})");
            return Ok(publish_token);
        }
    }

    // If we get here, we couldn't obtain a token via any known mechanism.
    // For GitHub, surface the clearer missing-variable error.
    let err = env::var(EnvVars::ACTIONS_ID_TOKEN_REQUEST_TOKEN)
        .map(|_| String::new())
        .err()
        .map(|e| TrustedPublishingError::from_var_err(EnvVars::ACTIONS_ID_TOKEN_REQUEST_TOKEN, e));
    if let Some(err) = err {
        return Err(err);
    }
    // For GitLab CI, indicate the expected variables.
    if env::var(EnvVars::GITLAB_CI).is_ok() {
        return Err(TrustedPublishingError::MissingEnvVar(
            "PYPI_ID_TOKEN/TESTPYPI_ID_TOKEN",
        ));
    }
    // Otherwise, indicate that the OIDC token is missing for the environment.
    Err(TrustedPublishingError::MissingEnvVar(
        EnvVars::ACTIONS_ID_TOKEN_REQUEST_TOKEN,
    ))
}

async fn get_audience(
    registry: &DisplaySafeUrl,
    client: &ClientWithMiddleware,
) -> Result<String, TrustedPublishingError> {
    // `pypa/gh-action-pypi-publish` uses `netloc` (RFC 1808), which is deprecated for authority
    // (RFC 3986).
    let audience_url = DisplaySafeUrl::parse(&format!(
        "{}://{}/_/oidc/audience",
        registry.scheme(),
        registry.authority()
    ))?;
    debug!("Querying the trusted publishing audience from {audience_url}");
    let response = client
        .get(Url::from(audience_url.clone()))
        .send()
        .await
        .map_err(|err| TrustedPublishingError::ReqwestMiddleware(audience_url.clone(), err))?;

Review comment (suggestion):
For generality, this should be formatted as `{AUD}_ID_TOKEN`, after obtaining and normalizing the `audience` per above.

Code after:
    }
}

async fn get_audience(
    registry: &DisplaySafeUrl,
    client: &ClientWithMiddleware,
) -> Result<String, TrustedPublishingError> {
    // `pypa/gh-action-pypi-publish` uses `netloc` (RFC 1808), which is deprecated for authority
    // (RFC 3986).
    // Prefer HTTPS for OIDC discovery; allow HTTP only in test builds
    let scheme: &str = if cfg!(feature = "test") {
        registry.scheme()
    } else {
        "https"
    };
    let audience_url = DisplaySafeUrl::parse(&format!(
        "{}://{}/_/oidc/audience",
        scheme,
        registry.authority()
    ))?;
    debug!("Querying the trusted publishing audience from {audience_url}");
    let response = client
        .get(Url::from(audience_url.clone()))
        .send()
        .await
        .map_err(|err| TrustedPublishingError::ReqwestMiddleware(audience_url.clone(), err))?;
    let audience = response
        .error_for_status()
        .map_err(|err| TrustedPublishingError::Reqwest(audience_url.clone(), err))?
        .json::<Audience>()
        .await
        .map_err(|err| TrustedPublishingError::Reqwest(audience_url.clone(), err))?;
    trace!("The audience is `{}`", &audience.audience);
    Ok(audience.audience)
}

/// Perform ambient OIDC token discovery.
async fn get_oidc_token(
    audience: &str,
    client: &ClientWithMiddleware,
) -> Result<Option<ambient_id::IdToken>, TrustedPublishingError> {
    let detector = ambient_id::Detector::new_with_client(client.clone());

    match detector.detect(audience).await {
        Ok(token) => Ok(token),
        // Specialize the error case insufficient permissions error case,
        // since we can offer the user a hint about fixing their permissions.
        Err(
            err @ ambient_id::Error::GitHubActions(
                ambient_id::GitHubError::InsufficientPermissions(_),
            ),