babelforce-manager-sdk 0.46.0

Rust SDK for the babelforce manager APIs — auth, user & agent management, call reporting, metrics, and task automations.
Documentation
use crate::error::{map_auth_err, ManagerError};
use crate::gen::auth::apis::configuration::Configuration;
use crate::gen::auth::apis::o_auth_api as api;
use crate::gen::auth::models;
use crate::retry::{with_retry, RetryPolicy};
use crate::token::SharedCfg;

/// OAuth 2.0 endpoints — `/oauth/authorize`, `/oauth/token`, `/oauth/revoke`.
///
/// These authenticate via client credentials carried in the request itself (per RFC 6749/7009),
/// independent of the client's configured auth — `token` is how you *obtain* a bearer token.
pub struct AuthResource {
    pub(crate) cfg: SharedCfg<Configuration>,
    pub(crate) retry: RetryPolicy,
}

/// Optional parameters for the OAuth token endpoint. Set the fields relevant to your grant type
/// (e.g. `code` + `redirect_uri` + `code_verifier` for `authorization_code`, `refresh_token` for
/// `refresh_token`, `username` + `password` for `password`, `client_id` + `client_secret` for
/// `client_credentials`); leave the rest at their default `None`.
///
/// The [`Debug`](std::fmt::Debug) impl redacts the secret-bearing fields (`code`, `code_verifier`,
/// `refresh_token`, `password`, `client_secret`), showing only whether each is set.
#[derive(Clone, Default)]
pub struct TokenRequest {
    /// Authorization code from the redirect (`authorization_code` grant).
    pub code: Option<String>,
    /// Redirect URI registered for your application (must match the authorize request).
    pub redirect_uri: Option<String>,
    /// PKCE code verifier matching the `code_challenge` sent to `/oauth/authorize`.
    pub code_verifier: Option<String>,
    /// Refresh token to exchange (`refresh_token` grant).
    pub refresh_token: Option<String>,
    /// Resource-owner username (`password` grant).
    pub username: Option<String>,
    /// Resource-owner password (`password` grant).
    pub password: Option<String>,
    /// OAuth2 application client id.
    pub client_id: Option<String>,
    /// OAuth2 application client secret (`client_credentials` grant).
    pub client_secret: Option<String>,
    /// Space-separated scopes to request.
    pub scope: Option<String>,
    /// e.g. `"offline"` to request a refresh token alongside the access token.
    pub access_type: Option<String>,
}

impl std::fmt::Debug for TokenRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // Redact secret-bearing fields; show only presence (`Some("***")` / `None`).
        let redact = |o: &Option<String>| o.as_ref().map(|_| "***");
        f.debug_struct("TokenRequest")
            .field("code", &redact(&self.code))
            .field("redirect_uri", &self.redirect_uri)
            .field("code_verifier", &redact(&self.code_verifier))
            .field("refresh_token", &redact(&self.refresh_token))
            .field("username", &self.username)
            .field("password", &redact(&self.password))
            .field("client_id", &self.client_id)
            .field("client_secret", &redact(&self.client_secret))
            .field("scope", &self.scope)
            .field("access_type", &self.access_type)
            .finish()
    }
}

impl AuthResource {
    /// Exchange a grant for tokens at the OAuth token endpoint. `grant_type` is e.g.
    /// `authorization_code`, `refresh_token`, `password`, or `client_credentials`.
    pub async fn token(
        &self,
        grant_type: &str,
        req: TokenRequest,
    ) -> Result<models::OAuthTokenResponse, ManagerError> {
        let cfg = self.cfg.get().await?;
        let cfg = cfg.as_ref();
        with_retry(&self.retry, false, || {
            api::token(
                cfg,
                grant_type,
                req.code.as_deref(),
                req.redirect_uri.as_deref(),
                req.code_verifier.as_deref(),
                req.refresh_token.as_deref(),
                req.username.as_deref(),
                req.password.as_deref(),
                req.client_id.as_deref(),
                req.client_secret.as_deref(),
                req.scope.as_deref(),
                req.access_type.as_deref(),
            )
        })
        .await
        .map_err(map_auth_err)
    }

    /// Revoke a token (RFC 7009).
    pub async fn revoke(
        &self,
        token: &str,
        token_type_hint: Option<&str>,
        client_id: Option<&str>,
        client_secret: Option<&str>,
    ) -> Result<(), ManagerError> {
        let cfg = self.cfg.get().await?;
        let cfg = cfg.as_ref();
        with_retry(&self.retry, false, || {
            api::revoke(cfg, token, token_type_hint, client_id, client_secret)
        })
        .await
        .map_err(map_auth_err)?;
        Ok(())
    }

    /// The OAuth authorization endpoint (consent / redirect). Returns the raw response body (the
    /// authorization redirect / consent page). `state` and the PKCE `code_challenge` are optional.
    /// A direct pass-through of the standard OAuth authorize query parameters.
    #[allow(clippy::too_many_arguments)]
    pub async fn authorize(
        &self,
        response_type: &str,
        client_id: &str,
        redirect_uri: &str,
        scope: &str,
        state: Option<&str>,
        code_challenge: Option<&str>,
        code_challenge_method: Option<&str>,
    ) -> Result<String, ManagerError> {
        let cfg = self.cfg.get().await?;
        let cfg = cfg.as_ref();
        with_retry(&self.retry, true, || {
            api::authorize(
                cfg,
                response_type,
                client_id,
                redirect_uri,
                scope,
                state,
                code_challenge,
                code_challenge_method,
            )
        })
        .await
        .map_err(map_auth_err)
    }
}