From dbe590951384569c0a4551335a88bdbe7a6241e9 Mon Sep 17 00:00:00 2001 From: Guilherme Werner Date: Sun, 3 Dec 2023 14:10:37 -0300 Subject: [PATCH] Add oauth2 types --- src/lib.rs | 42 +++++++++++++- src/oauth2.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/token.rs | 20 +++++++ 3 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 src/oauth2.rs create mode 100644 src/token.rs diff --git a/src/lib.rs b/src/lib.rs index 810af7a..6a27ec6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,15 @@ #![allow(dead_code)] use alnilam_consts::TARGET_TRIPLE; -use anyhow::Result; +use anyhow::{Error, Result}; use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::Client; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub mod oauth2; +pub mod token; + #[derive(Clone)] pub struct TribufuClient { client_id: u64, @@ -17,7 +20,7 @@ pub struct TribufuClient { } impl TribufuClient { - const BASE_URL: &'static str = "https://api.tribufu.com"; + const BASE_URL: &'static str = "http://localhost:5000"; pub fn new(id: u64, secret: impl Into) -> Result { let user_agent = format!( @@ -44,6 +47,41 @@ impl TribufuClient { pub fn id(&self) -> u64 { self.client_id } + + pub async fn get_token(&self) -> Result { + let body = oauth2::OAuth2TokenRequest { + grant_type: oauth2::OAuth2GrantType::ClientCredentials, + code: None, + refresh_token: None, + username: None, + password: None, + client_id: self.client_id.to_string(), + client_secret: self.client_secret.clone(), + redirect_uri: None, + }; + + let response = match self + .http + .post(format!("{}/v1/oauth2/token", Self::BASE_URL)) + .form(&body) + .send() + .await + { + Ok(r) => r, + Err(e) => return Err(e.into()), + }; + + if response.status() != 200 { + return Err(Error::msg(format!( + "Failed to get token: {}", + response.status() + ))); + } + + let token = response.json::().await?; + + Ok(token) + } } #[cfg(test)] diff --git a/src/oauth2.rs b/src/oauth2.rs new file mode 100644 index 0000000..a869a5a --- /dev/null +++ b/src/oauth2.rs @@ -0,0 +1,148 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2ResponseType { + Code, + Token, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2ClientType { + Confidential, + Public, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2TokenHintType { + AccessToken, + RefreshToken, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2GrantType { + AuthorizationCode, + ClientCredentials, + DeviceCode, + Password, + RefreshToken, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2AuthorizeError { + AccessDenied, + InvalidRequest, + InvalidScope, + ServerError, + TemporarilyUnavailable, + UnauthorizedClient, + UnsupportedResponseType, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OAuth2TokenType { + Bearer, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2AuthorizeRequest { + pub response_type: OAuth2ResponseType, + pub client_id: String, + pub client_secret: Option, + pub redirect_uri: String, + pub scope: Option, + pub state: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2CodeResponse { + pub code: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2ErrorResponse { + pub error: OAuth2AuthorizeError, + + #[serde(skip_serializing_if = "Option::is_none")] + pub error_description: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub error_uri: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2TokenRequest { + pub grant_type: OAuth2GrantType, + pub client_id: Option, + pub client_secret: Option, + pub redirect_uri: Option, + pub code: Option, + pub refresh_token: Option, + pub username: Option, + pub password: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2TokenResponse { + pub token_type: OAuth2TokenType, + + pub access_token: String, + + pub refresh_token: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option, + + pub expires_in: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2RevokeRequest { + pub token: String, + pub token_type_hint: OAuth2TokenHintType, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OAuth2IntrospectionResponse { + pub active: bool, + + #[serde(skip_serializing_if = "Option::is_none")] + pub client_id: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub username: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub exp: Option, +} + +impl OAuth2IntrospectionResponse { + fn inative() -> Self { + Self { + active: false, + client_id: None, + username: None, + scope: None, + exp: None, + } + } +} diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..c3adf4e --- /dev/null +++ b/src/token.rs @@ -0,0 +1,20 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TokenType { + User, + Bot, + Client, + Server, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AuthorizationType { + ApiKey, + Basic, + Bearer, +}