twitch_oauth_token 4.0.1

Type-safe Twitch OAuth 2.0 authentication library with CSRF protection and full scope support
Documentation
//! Testing with Mock API
//!
//! Defaults:
//! - port: 8080
//! - User authorization URL: http://localhost:8080/auth/authorize
//! - App authorization URL: http://localhost:8080/auth/token
//!
//! ```rust,no_run
//! # #[cfg(feature = "test")]
//! # {
//!  use twitch_oauth_token::{
//!      scope::ChatScopes,
//!      test_oauth::mock_api::MockApiUnits,
//!      TwitchOauth,
//!  };
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!      let mock_api = MockApiUnits::new();
//!      let clients = mock_api.get_clients().await.unwrap();
//!      let client = clients.data.first().unwrap();
//!     
//!     let oauth = TwitchOauth::from_credentials(client.ID.clone(), client.Secret.clone())
//!         .with_test();
//!
//!     // Get app token from mock API
//!     let app_token = oauth.app_access_token()
//!         .await?;
//!
//!     let users = mock_api.get_users().await?;
//!     let user = users.data.first().unwrap();
//!     // Get user token from mock API
//!     let mut user_token_request = oauth.exchange_code(&user.id);
//!     user_token_request.scopes_mut().send_chat_message();
//!     
//!     let user_token = user_token_request
//!         .send()
//!         .await?;
//!
//!     Ok(())
//! }
//! # }
//! ```

pub mod mock_api;
pub mod response;

mod access_token;

pub use access_token::TestAccessToken;

use std::collections::HashSet;
use std::str::FromStr;

use asknothingx2_util::api::preset;
use url::Url;

use crate::{
    oauth::OauthFlow, request::ClientCredentialsRequest, types::GrantType, AuthUrl, Error,
    TokenUrl, TwitchOauth,
};

#[derive(Debug)]
pub struct TwitchOauthTest<Flow>
where
    Flow: OauthFlow,
{
    oauth: TwitchOauth<Flow>,
    test_env: TestEnv,
}

impl<Flow> TwitchOauthTest<Flow>
where
    Flow: OauthFlow,
{
    pub fn new(oauth: TwitchOauth<Flow>) -> Self {
        let this = oauth.with_client(
            preset::testing("twitch-oauth-token-test/1.0")
                .build()
                .unwrap(),
        );

        Self {
            oauth: this,
            test_env: TestEnv::default(),
        }
    }

    pub fn with_env(mut self, test_env: TestEnv) -> Self {
        self.test_env = test_env;
        self
    }

    pub fn oauth(&self) -> &TwitchOauth<Flow> {
        &self.oauth
    }

    #[allow(deprecated)]
    pub fn exchange_code<'a>(&'a self, user_id: &'a str) -> TestAccessToken<'a> {
        TestAccessToken::new(
            self.oauth.client_id(),
            self.oauth.client_secret(),
            GrantType::UserToken,
            Some(user_id),
            HashSet::new(),
            AuthUrl::from_str(self.test_env.user_auth_url().as_ref()).unwrap(),
        )
    }

    #[allow(deprecated)]
    pub async fn app_access_token(&self) -> Result<crate::AppToken, crate::Error> {
        let resp = self
            .oauth
            .send(ClientCredentialsRequest::new(
                self.oauth.client_id(),
                self.oauth.client_secret(),
                GrantType::ClientCredentials,
                &TokenUrl::from_str(self.test_env.app_auth_url().as_ref()).unwrap(),
            ))
            .await?;

        crate::oauth::decode_response(resp).await
    }
}

#[derive(Debug, Clone)]
pub struct TestEnv {
    port: Option<u16>,
    user_auth_url: Option<Url>,
    app_auth_url: Option<Url>,
}

impl TestEnv {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn with_port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    pub fn with_user_auth_url(mut self, url: Url) -> Self {
        self.user_auth_url = Some(url);
        self
    }

    pub fn with_app_auth_url(mut self, url: Url) -> Self {
        self.app_auth_url = Some(url);
        self
    }

    pub fn configure(self) -> Result<TestEnv, Error> {
        let port = self.port.unwrap_or(8080);
        let user_auth_url = if self.user_auth_url.is_some() {
            self.user_auth_url
        } else {
            Some(Url::parse(&format!("http://localhost:{port}/auth/authorize")).unwrap())
        };

        let app_auth_url = if self.app_auth_url.is_some() {
            self.app_auth_url
        } else {
            Some(Url::parse(&format!("http://localhost:{port}/auth/token")).unwrap())
        };

        Ok(TestEnv {
            port: Some(port),
            user_auth_url,
            app_auth_url,
        })
    }

    pub fn user_auth_url(&self) -> &Url {
        self.user_auth_url
            .as_ref()
            .expect("URLs should be configured")
    }

    pub fn app_auth_url(&self) -> &Url {
        self.app_auth_url
            .as_ref()
            .expect("URLs should be configured")
    }

    pub fn port(&self) -> u16 {
        self.port.expect("Port should be configured")
    }
}

impl Default for TestEnv {
    fn default() -> Self {
        let user_auth_url = Some(Url::parse("http://localhost:8080/auth/authorize").unwrap());
        let app_auth_url = Some(Url::parse("http://localhost:8080/auth/token").unwrap());

        TestEnv {
            port: Some(8080),
            user_auth_url,
            app_auth_url,
        }
    }
}