ez-token 0.1.0

CLI tool for generating OAuth2 access tokens via PKCE and Client Credentials for Microsoft Entra ID and Auth0
Documentation
/// OAuth2 endpoint variants for supported identity providers.
///
/// Provides a type-safe way to construct authorization and token endpoint
/// URLs, avoiding scattered `format!` literals across the codebase.
#[derive(Debug, PartialEq)]
pub enum IdentityProvider {
    /// Microsoft Entra ID (Azure AD).
    ///
    /// Requires a tenant ID — a GUID, domain (e.g. `contoso.onmicrosoft.com`),
    /// or `"common"` for multi-tenant applications.
    Microsoft {
        /// The Entra ID tenant identifier.
        tenant_id: String,
    },

    /// Auth0.
    ///
    /// Requires your Auth0 domain (e.g. `my-org.eu.auth0.com`) and
    /// audience (e.g. `api://ez-token`).
    Auth0 {
        /// The Auth0 domain (e.g. `my-org.eu.auth0.com`).
        domain: String,
        /// The API audience identifier (e.g. `api://ez-token`).
        audience: String,
    },
}

impl IdentityProvider {
    /// Constructs the authorization endpoint URL for this provider.
    pub fn auth_url(&self) -> String {
        match self {
            Self::Microsoft { tenant_id } => format!(
                "https://login.microsoftonline.com/{}/oauth2/v2.0/authorize",
                tenant_id
            ),
            Self::Auth0 { domain, .. } => format!("https://{}/authorize", domain),
        }
    }

    /// Constructs the token endpoint URL for this provider.
    pub fn token_url(&self) -> String {
        match self {
            Self::Microsoft { tenant_id } => format!(
                "https://login.microsoftonline.com/{}/oauth2/v2.0/token",
                tenant_id
            ),
            Self::Auth0 { domain, .. } => format!("https://{}/oauth/token", domain),
        }
    }

    /// Returns the audience if required by this provider.
    ///
    /// Auth0 requires an explicit audience parameter to identify the target API.
    /// Microsoft does not use this parameter.
    pub fn audience(&self) -> Option<&str> {
        match self {
            Self::Microsoft { .. } => None,
            Self::Auth0 { audience, .. } => Some(audience),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_microsoft_endpoints() {
        let provider = IdentityProvider::Microsoft {
            tenant_id: "common".to_string(),
        };

        assert_eq!(
            provider.auth_url(),
            "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
        );

        assert_eq!(
            provider.token_url(),
            "https://login.microsoftonline.com/common/oauth2/v2.0/token"
        );

        assert_eq!(provider.audience(), None);
    }

    #[test]
    fn test_microsoft_endpoints_with_guid() {
        let provider = IdentityProvider::Microsoft {
            tenant_id: "1234567891011121314".to_string(),
        };

        assert_eq!(
            provider.auth_url(),
            "https://login.microsoftonline.com/1234567891011121314/oauth2/v2.0/authorize"
        );

        assert_eq!(
            provider.token_url(),
            "https://login.microsoftonline.com/1234567891011121314/oauth2/v2.0/token"
        );
    }

    #[test]
    fn test_auth0_endpoints() {
        let provider = IdentityProvider::Auth0 {
            domain: "my-org.eu.auth0.com".to_string(),
            audience: "api://ez-token".to_string(),
        };

        assert_eq!(provider.auth_url(), "https://my-org.eu.auth0.com/authorize");

        assert_eq!(
            provider.token_url(),
            "https://my-org.eu.auth0.com/oauth/token"
        );

        assert_eq!(provider.audience(), Some("api://ez-token"));
    }
}