aboutsummaryrefslogtreecommitdiffstats
path: root/crates/api-auth
diff options
context:
space:
mode:
Diffstat (limited to 'crates/api-auth')
-rw-r--r--crates/api-auth/Cargo.toml23
-rw-r--r--crates/api-auth/src/discord/mod.rs30
-rw-r--r--crates/api-auth/src/error.rs25
-rw-r--r--crates/api-auth/src/lib.rs69
4 files changed, 147 insertions, 0 deletions
diff --git a/crates/api-auth/Cargo.toml b/crates/api-auth/Cargo.toml
new file mode 100644
index 0000000..7df9411
--- /dev/null
+++ b/crates/api-auth/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "api-auth"
+version = "0.0.0"
+edition = "2024"
+license.workspace = true
+readme.workspace = true
+documentation.workspace = true
+homepage.workspace = true
+
+[dependencies]
+api-core = { workspace = true, features = ["auth", "users"] }
+async-trait.workspace = true
+oauth2 = "5.0.0"
+secrecy.workspace = true
+serde.workspace = true
+sqlx.workspace = true
+thiserror.workspace = true
+utoipa = { workspace = true, optional = true }
+url.workspace = true
+
+[features]
+discord = []
+utoipa = ["dep:utoipa", "serde/derive"]
diff --git a/crates/api-auth/src/discord/mod.rs b/crates/api-auth/src/discord/mod.rs
new file mode 100644
index 0000000..a39722d
--- /dev/null
+++ b/crates/api-auth/src/discord/mod.rs
@@ -0,0 +1,30 @@
+use api_core::models::user::User;
+use async_trait::async_trait;
+use sqlx::PgPool;
+
+use crate::{BasicClient, OauthDriver, error::AuthError};
+
+#[derive(Clone, Debug)]
+pub struct AuthServiceDiscord {
+ database: PgPool,
+ client: BasicClient,
+}
+
+impl AuthServiceDiscord {
+ pub fn new(database: PgPool, client: BasicClient) -> Self {
+ Self { database, client }
+ }
+}
+
+#[async_trait]
+impl OauthDriver for AuthServiceDiscord {
+ async fn get_auth_token(&self) -> Result<String, AuthError> {
+ todo!()
+ }
+ async fn get_user(&self) -> Result<User, AuthError> {
+ todo!()
+ }
+ async fn create_session(&self, _user: &User) {
+ todo!()
+ }
+}
diff --git a/crates/api-auth/src/error.rs b/crates/api-auth/src/error.rs
new file mode 100644
index 0000000..ec60e51
--- /dev/null
+++ b/crates/api-auth/src/error.rs
@@ -0,0 +1,25 @@
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum AuthClientError {
+ #[error("missing field: {0}")]
+ MissingField(&'static str),
+ #[error("invalid auth url: {0}")]
+ InvalidAuthUrl(#[from] oauth2::url::ParseError),
+ #[error("invalid token url: {0}")]
+ InvalidTokenUrl(#[source] oauth2::url::ParseError),
+ #[error("invalid redirect url: {0}")]
+ InvalidRedirectUrl(#[source] oauth2::url::ParseError),
+}
+
+#[derive(Debug, Error)]
+pub enum AuthError {
+ #[error("missing field: {0}")]
+ MissingField(&'static str),
+ #[error("invalid auth url: {0}")]
+ InvalidAuthUrl(#[from] oauth2::url::ParseError),
+ #[error("invalid token url: {0}")]
+ InvalidTokenUrl(#[source] oauth2::url::ParseError),
+ #[error("invalid redirect url: {0}")]
+ InvalidRedirectUrl(#[source] oauth2::url::ParseError),
+}
diff --git a/crates/api-auth/src/lib.rs b/crates/api-auth/src/lib.rs
new file mode 100644
index 0000000..284b772
--- /dev/null
+++ b/crates/api-auth/src/lib.rs
@@ -0,0 +1,69 @@
+#[cfg(feature = "discord")]
+pub mod discord;
+
+mod error;
+use api_core::auth::AuthClientConfig;
+use api_core::auth::provider::OauthProvider;
+use api_core::models::user::User;
+pub use error::AuthClientError;
+
+use oauth2::{EndpointNotSet, EndpointSet};
+
+type C = oauth2::basic::BasicClient<
+ EndpointSet,
+ EndpointNotSet,
+ EndpointNotSet,
+ EndpointNotSet,
+ EndpointSet,
+>;
+
+#[derive(Clone, Debug)]
+pub struct BasicClient(C);
+
+#[async_trait::async_trait]
+pub trait OauthDriver: Send + Sync + std::fmt::Debug {
+ async fn get_auth_token(&self) -> Result<String, AuthError>;
+ async fn get_user(&self) -> Result<User, AuthError>;
+ async fn create_session(&self, user: &User);
+}
+
+use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
+use sqlx::PgPool;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::{convert::TryFrom, ops::Deref};
+
+use crate::error::AuthError;
+
+pub struct OauthService {
+ clients: HashMap<OauthProvider, Arc<dyn OauthDriver>>,
+}
+
+impl Deref for BasicClient {
+ type Target = C;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl TryFrom<AuthClientConfig> for BasicClient {
+ type Error = AuthClientError;
+
+ fn try_from(value: AuthClientConfig) -> Result<Self, Self::Error> {
+ let auth_url = AuthUrl::new(value.auth_url).map_err(AuthClientError::InvalidAuthUrl)?;
+
+ let token_url = TokenUrl::new(value.token_uri).map_err(AuthClientError::InvalidTokenUrl)?;
+
+ let redirect_url =
+ RedirectUrl::new(value.redirect_uri).map_err(AuthClientError::InvalidRedirectUrl)?;
+
+ Ok(Self(
+ oauth2::basic::BasicClient::new(ClientId::new(value.client_id))
+ .set_client_secret(ClientSecret::new(value.client_secret))
+ .set_auth_uri(auth_url)
+ .set_token_uri(token_url)
+ .set_redirect_uri(redirect_url),
+ ))
+ }
+}