feat: initial oauth2 implementation

This commit is contained in:
Zomatree
2025-06-23 21:51:03 +01:00
parent ed22b3a5ce
commit 9e05e5be7e
21 changed files with 711 additions and 10 deletions

View File

@@ -1,5 +1,8 @@
use super::User;
#[cfg(feature = "validator")]
use validator::Validate;
auto_derived!(
/// Bot
#[derive(Default)]
@@ -48,6 +51,13 @@ auto_derived!(
)]
pub privacy_policy_url: String,
/// Oauth2 bot settings
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none", default)
)]
pub oauth2: Option<BotOauth2>,
/// Enum of bot flags
#[cfg_attr(
feature = "serde",
@@ -60,6 +70,8 @@ auto_derived!(
pub enum FieldsBot {
Token,
InteractionsURL,
Oauth2,
Oauth2Secret,
}
/// Flags that may be attributed to a bot
@@ -101,7 +113,7 @@ auto_derived!(
/// Bot Details
#[derive(Default)]
#[cfg_attr(feature = "validator", derive(validator::Validate))]
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct DataCreateBot {
/// Bot username
#[cfg_attr(
@@ -113,7 +125,7 @@ auto_derived!(
/// New Bot Details
#[derive(Default)]
#[cfg_attr(feature = "validator", derive(validator::Validate))]
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct DataEditBot {
/// Bot username
#[cfg_attr(
@@ -131,11 +143,24 @@ auto_derived!(
/// Interactions URL
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 2048)))]
pub interactions_url: Option<String>,
#[cfg_attr(feature = "validator", validate)]
pub oauth2: Option<DataEditBotOauth2>,
/// Fields to remove from bot object
#[cfg_attr(feature = "serde", serde(default))]
pub remove: Vec<FieldsBot>,
}
#[derive(Default)]
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct DataEditBotOauth2 {
#[cfg_attr(feature = "serde", serde(default))]
pub public: Option<bool>,
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 10)))]
pub redirects: Option<Vec<String>>,
}
/// Where we are inviting a bot to
#[serde(untagged)]
pub enum InviteBotDestination {
@@ -170,3 +195,19 @@ auto_derived!(
pub user: User,
}
);
auto_derived_partial!(
/// Bot Oauth2 information
pub struct BotOauth2 {
/// Whether the oauth client is public and should not receive a secret key
#[serde(default)]
pub public: bool,
/// Secret key used for authorisation, not provided if the client is public
#[serde(default)]
pub secret: Option<String>,
/// Allowed redirects for the authorisation
#[serde(default)]
pub redirects: Vec<String>
},
"PartialBotOauth2"
);

View File

@@ -7,6 +7,7 @@ mod embeds;
mod emojis;
mod files;
mod messages;
mod oauth2;
mod policy_changes;
mod safety_reports;
mod server_bans;
@@ -24,6 +25,7 @@ pub use embeds::*;
pub use emojis::*;
pub use files::*;
pub use messages::*;
pub use oauth2::*;
pub use policy_changes::*;
pub use safety_reports::*;
pub use server_bans::*;

View File

@@ -0,0 +1,101 @@
use crate::v0::{Bot, User};
auto_derived!(
pub struct OAuth2AuthorizeAuthResponse {
/// Redirect URI which will contain the access token
pub redirect_uri: String,
}
pub struct OAuth2AuthorizeInfoResponse {
pub bot: Bot,
pub user: User,
}
#[derive(Copy)]
#[cfg_attr(feature = "serde", serde(rename = "lowercase"))]
#[cfg_attr(feature = "rocket", derive(rocket::FromFormField))]
pub enum OAuth2ResponseType {
#[cfg_attr(feature = "rocket", field(value = "code"))]
Code,
#[cfg_attr(feature = "rocket", field(value = "token"))]
Token,
}
#[derive(Copy)]
#[cfg_attr(feature = "rocket", derive(rocket::FromFormField))]
pub enum OAuth2GrantType {
#[cfg_attr(feature = "rocket", field(value = "code"))]
#[cfg_attr(feature = "serde", serde(rename = "code"))]
AuthorizationCode,
#[cfg_attr(feature = "rocket", field(value = "implicit"))]
#[cfg_attr(feature = "serde", serde(rename = "implicit"))]
Implicit,
}
#[derive(Copy)]
#[cfg_attr(feature = "rocket", derive(rocket::FromFormField))]
pub enum OAuth2CodeChallangeMethod {
#[cfg_attr(feature = "rocket", field(value = "plain"))]
#[cfg_attr(feature = "serde", serde(rename = "plain"))]
Plain,
S256
}
#[cfg_attr(feature = "rocket", derive(rocket::FromForm))]
pub struct OAuth2AuthorizationForm {
pub client_id: String,
pub scope: String,
pub redirect_uri: String,
pub response_type: OAuth2ResponseType,
pub state: Option<String>,
pub code_challenge: Option<String>,
pub code_challenge_method: Option<OAuth2CodeChallangeMethod>,
}
#[cfg_attr(feature = "rocket", derive(rocket::FromForm))]
pub struct OAuth2TokenExchangeForm {
pub grant_type: OAuth2GrantType,
pub client_id: String,
pub client_secret: Option<String>,
pub code: String,
pub code_verifier: Option<String>,
}
pub struct OAuth2TokenExchangeResponse {
pub access_token: String,
pub token_type: String,
pub scope: String,
}
pub enum OAuth2Scope {
Identify,
Full,
}
);
impl OAuth2Scope {
#[allow(clippy::should_implement_trait)]
pub fn from_str(str: &str) -> Option<OAuth2Scope> {
match str {
"identify" => Some(OAuth2Scope::Identify),
"full" => Some(OAuth2Scope::Full),
_ => None,
}
}
pub fn scopes_from_str(string: &str) -> Option<Vec<OAuth2Scope>> {
let mut scopes = Vec::new();
for scope in string.split(' ') {
if let Some(scope) = OAuth2Scope::from_str(scope) {
scopes.push(scope);
} else {
return None;
}
}
Some(scopes)
}
}