aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2026-04-04 12:24:59 +0200
committerrtkay123 <dev@kanjala.com>2026-04-04 12:24:59 +0200
commite86366c6d68b9d3d2af4ac4afb5cf7d5a8400dde (patch)
tree6632795c61426f26ae3b5cdd1d2da7d2212b7d0c /crates
parent19c25138f88acf19c9a959a58de4f58e54026ebc (diff)
downloadsellershut-e86366c6d68b9d3d2af4ac4afb5cf7d5a8400dde.tar.bz2
sellershut-e86366c6d68b9d3d2af4ac4afb5cf7d5a8400dde.zip
feat(auth): create session
Diffstat (limited to 'crates')
-rw-r--r--crates/api-auth/Cargo.toml1
-rw-r--r--crates/api-auth/src/discord/mod.rs18
-rw-r--r--crates/api-auth/src/lib.rs11
-rw-r--r--crates/sellershut/src/main.rs2
-rw-r--r--crates/sellershut/src/server/api/error.rs27
-rw-r--r--crates/sellershut/src/server/api/mod.rs10
-rw-r--r--crates/sellershut/src/server/api/routes/auth/discord.rs64
-rw-r--r--crates/sellershut/src/server/api/routes/auth/mod.rs22
-rw-r--r--crates/sellershut/src/server/api/routes/mod.rs1
-rw-r--r--crates/sellershut/src/server/mod.rs5
10 files changed, 148 insertions, 13 deletions
diff --git a/crates/api-auth/Cargo.toml b/crates/api-auth/Cargo.toml
index 7df9411..053bbb9 100644
--- a/crates/api-auth/Cargo.toml
+++ b/crates/api-auth/Cargo.toml
@@ -17,6 +17,7 @@ sqlx.workspace = true
thiserror.workspace = true
utoipa = { workspace = true, optional = true }
url.workspace = true
+async-session = "3.0.0"
[features]
discord = []
diff --git a/crates/api-auth/src/discord/mod.rs b/crates/api-auth/src/discord/mod.rs
index a39722d..29b9bc2 100644
--- a/crates/api-auth/src/discord/mod.rs
+++ b/crates/api-auth/src/discord/mod.rs
@@ -1,8 +1,10 @@
use api_core::models::user::User;
+use async_session::Session;
use async_trait::async_trait;
+use oauth2::{CsrfToken, Scope};
use sqlx::PgPool;
-use crate::{BasicClient, OauthDriver, error::AuthError};
+use crate::{BasicClient, CSRF_TOKEN, OauthDriver, error::AuthError};
#[derive(Clone, Debug)]
pub struct AuthServiceDiscord {
@@ -24,7 +26,19 @@ impl OauthDriver for AuthServiceDiscord {
async fn get_user(&self) -> Result<User, AuthError> {
todo!()
}
- async fn create_session(&self, _user: &User) {
+ async fn create_oauth_session(&self)->Result<String,AuthError> {
+ let (auth_url, csrf_token) = self
+ .client
+ .authorize_url(CsrfToken::new_random)
+ .add_scope(Scope::new("identify".to_string()))
+ .url();
+
+ let mut session = Session::new();
+ session.insert(CSRF_TOKEN, &csrf_token).unwrap();
+
+ Ok(String::default())
+ }
+ async fn save_session(&self, user: &User)->Result<(), AuthError>{
todo!()
}
}
diff --git a/crates/api-auth/src/lib.rs b/crates/api-auth/src/lib.rs
index 284b772..95a04c4 100644
--- a/crates/api-auth/src/lib.rs
+++ b/crates/api-auth/src/lib.rs
@@ -3,7 +3,6 @@ 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;
@@ -24,20 +23,16 @@ pub struct BasicClient(C);
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);
+ async fn create_oauth_session(&self)->Result<String, AuthError>;
+ async fn save_session(&self, user: &User)->Result<(), AuthError>;
}
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>>,
-}
+static CSRF_TOKEN: &str = "csrf_token";
impl Deref for BasicClient {
type Target = C;
diff --git a/crates/sellershut/src/main.rs b/crates/sellershut/src/main.rs
index fca10e1..ebae4ed 100644
--- a/crates/sellershut/src/main.rs
+++ b/crates/sellershut/src/main.rs
@@ -16,7 +16,7 @@ use api_core::{
};
use clap::Parser;
use sqlx::PgPool;
-use tokio::{net::TcpListener};
+use tokio::net::TcpListener;
use tracing::info;
use crate::{
diff --git a/crates/sellershut/src/server/api/error.rs b/crates/sellershut/src/server/api/error.rs
new file mode 100644
index 0000000..6d07f9f
--- /dev/null
+++ b/crates/sellershut/src/server/api/error.rs
@@ -0,0 +1,27 @@
+use axum::{
+ http::StatusCode,
+ response::{IntoResponse, Response},
+};
+
+#[derive(Debug)]
+pub struct AppError(anyhow::Error);
+
+// Tell axum how to convert `AppError` into a response.
+impl IntoResponse for AppError {
+ fn into_response(self) -> Response {
+ tracing::error!("Application error: {:#}", self.0);
+
+ (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response()
+ }
+}
+
+// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
+// `Result<_, AppError>`. That way you don't need to do that manually.
+impl<E> From<E> for AppError
+where
+ E: Into<anyhow::Error>,
+{
+ fn from(err: E) -> Self {
+ Self(err.into())
+ }
+}
diff --git a/crates/sellershut/src/server/api/mod.rs b/crates/sellershut/src/server/api/mod.rs
index c227f59..ebe29f8 100644
--- a/crates/sellershut/src/server/api/mod.rs
+++ b/crates/sellershut/src/server/api/mod.rs
@@ -1,9 +1,15 @@
+pub mod error;
+
use api_core::health::ApiDocBase;
use axum::Router;
use utoipa::OpenApi;
use utoipa_axum::router::OpenApiRouter;
-use crate::{config::Config, server::api::routes::ServerConfigDoc, state::AppState};
+use crate::{
+ config::Config,
+ server::api::routes::{ServerConfigDoc, auth::AuthDoc},
+ state::AppState,
+};
pub mod routes;
@@ -20,10 +26,12 @@ pub async fn router(state: AppState, config: Config) -> Router<()> {
doc.merge(ApiDocBase::openapi());
doc.merge(ServerConfigDoc::openapi());
+ doc.merge(AuthDoc::openapi());
let stubs = OpenApiRouter::with_openapi(doc)
.routes(utoipa_axum::routes!(routes::health))
.nest("/api", routes::router(state.clone()))
+ .nest("/api", routes::auth::router(state.clone()))
.with_state(state);
let (router, _api) = stubs.split_for_parts();
diff --git a/crates/sellershut/src/server/api/routes/auth/discord.rs b/crates/sellershut/src/server/api/routes/auth/discord.rs
new file mode 100644
index 0000000..163619b
--- /dev/null
+++ b/crates/sellershut/src/server/api/routes/auth/discord.rs
@@ -0,0 +1,64 @@
+use crate::server::api::error::AppError;
+use anyhow::Context;
+use api_core::auth::provider::OauthProvider;
+use axum::{
+ extract::State,
+ http::HeaderMap,
+ response::{IntoResponse, Redirect},
+};
+
+use crate::state::AppState;
+
+/// Update log level
+#[utoipa::path(
+ patch,
+ responses(
+ (
+ status = 200,
+ description = "A redirect to discord",
+ headers(
+ ("x-request-id", description = "Request identifier")
+ )
+ ),
+ ),
+ operation_id = "auth_discord", // https://github.com/juhaku/utoipa/issues/1170
+ path = "/auth",
+ tag = super::AUTH,
+)]
+pub async fn discord_auth(State(state): State<AppState>) -> Result<impl IntoResponse, AppError> {
+ let client = state
+ .auth_clients
+ .get(&OauthProvider::Discord)
+ .context("missing discord driver")?;
+
+ let headers = HeaderMap::new();
+ Ok((headers, Redirect::to(redirect_url)))
+
+ // let (auth_url, csrf_token) = client
+ // .authorize_url(CsrfToken::new_random)
+ // .add_scope(Scope::new("identify".to_string()))
+ // .url();
+ //
+ // // Create session to store csrf_token
+ // let mut session = Session::new();
+ // session
+ // .insert(CSRF_TOKEN, &csrf_token)
+ // .context("failed in inserting CSRF token into session")?;
+ //
+ // // Store the session in MemoryStore and retrieve the session cookie
+ // let cookie = store
+ // .store_session(session)
+ // .await
+ // .context("failed to store CSRF token session")?
+ // .context("unexpected error retrieving CSRF cookie value")?;
+ //
+ // // Attach the session cookie to the response header
+ // let cookie = format!("{COOKIE_NAME}={cookie}; SameSite=Lax; HttpOnly; Secure; Path=/");
+ // let mut headers = HeaderMap::new();
+ // headers.insert(
+ // SET_COOKIE,
+ // cookie.parse().context("failed to parse cookie")?,
+ // );
+ //
+ // Ok((headers, Redirect::to(auth_url.as_ref())))
+}
diff --git a/crates/sellershut/src/server/api/routes/auth/mod.rs b/crates/sellershut/src/server/api/routes/auth/mod.rs
new file mode 100644
index 0000000..3e36eaa
--- /dev/null
+++ b/crates/sellershut/src/server/api/routes/auth/mod.rs
@@ -0,0 +1,22 @@
+#[cfg(feature = "auth-discord")]
+mod discord;
+
+use utoipa::OpenApi;
+use utoipa_axum::router::OpenApiRouter;
+
+use crate::state::AppState;
+
+const AUTH: &str = "Authentication";
+
+#[derive(OpenApi)]
+#[openapi(tags((name = AUTH, description = "Transaction monitoring endpoints")))]
+pub struct AuthDoc;
+
+pub fn router(store: AppState) -> OpenApiRouter<AppState> {
+ let router = OpenApiRouter::new();
+
+ #[cfg(feature = "auth-discord")]
+ let router = router.routes(utoipa_axum::routes!(discord::discord_auth));
+
+ router.with_state(store)
+}
diff --git a/crates/sellershut/src/server/api/routes/mod.rs b/crates/sellershut/src/server/api/routes/mod.rs
index 1de8e80..822bfe7 100644
--- a/crates/sellershut/src/server/api/routes/mod.rs
+++ b/crates/sellershut/src/server/api/routes/mod.rs
@@ -1,3 +1,4 @@
+pub mod auth;
mod logs;
use axum::{extract::State, response::IntoResponse};
diff --git a/crates/sellershut/src/server/mod.rs b/crates/sellershut/src/server/mod.rs
index a66eed5..f8ea2c5 100644
--- a/crates/sellershut/src/server/mod.rs
+++ b/crates/sellershut/src/server/mod.rs
@@ -3,7 +3,10 @@ pub mod logs;
#[cfg(test)]
mod boostrap {
- use std::{collections::HashMap, sync::{Arc, OnceLock}};
+ use std::{
+ collections::HashMap,
+ sync::{Arc, OnceLock},
+ };
use api_core::health::BaseService;
use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt, reload};