use std::{net::Ipv6Addr, sync::Arc}; use axum::{ extract::{Query, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Form, Json, Router, }; use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use tracing::{event, instrument, Level}; use crate::auth::{ AuthenticationError, ServerPadlockGenerator, UserAuthenticator, UserServerKeyGenerator, }; use crate::secrets::{Password, ServerHash, UserToken}; #[derive(Debug)] struct AppState { user_authenticator: Arc, server_padlock_generator: Arc, user_server_key_generator: Arc, } #[instrument] pub async fn run( port: u16, user_authenticator: Arc, server_padlock_generator: Arc, user_server_key_generator: Arc, ) -> color_eyre::Result<()> { let app_state = Arc::new(AppState { user_authenticator, server_padlock_generator, user_server_key_generator, }); let app = Router::new() .route("/tls-check/success", get(|| async { "OK" })) .route("/api-login", post(api_login)) .route( "/generate-user-server-key-2", post(generate_user_server_key_2), ) .with_state(app_state); let listener = tokio::net::TcpListener::bind((Ipv6Addr::UNSPECIFIED, port)).await?; axum::serve(listener, app).await?; Ok(()) } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] struct ApiError { #[serde(skip_serializing)] status: StatusCode, error: String, message: String, } impl From for ApiError { fn from(err: AuthenticationError) -> Self { Self { status: StatusCode::UNAUTHORIZED, error: "authentication-failed".to_owned(), message: err.to_string(), } } } impl IntoResponse for ApiError { fn into_response(self) -> axum::response::Response { (self.status, Json(self)).into_response() } } type ApiResult = Result; #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] struct ApiVersion { api_version: String, } #[derive(Debug, Clone, Deserialize)] struct LoginRequest { username: String, password: Password, } #[derive(Debug, Clone, Serialize)] struct LoginResponse { username: String, token: String, } #[instrument] async fn api_login( State(state): State>, Query(ApiVersion { api_version }): Query, Form(LoginRequest { username, password }): Form, ) -> ApiResult> { event!(Level::INFO, "Generating user key"); let user_token = state .user_authenticator .create_user_token(&username, &password) .await?; Ok(Json(LoginResponse { username, token: user_token.0.expose_secret().to_owned(), })) } #[derive(Debug, Clone, Deserialize)] struct UserServerKeyRequest { username: String, token: UserToken, server_hash: ServerHash, } #[derive(Debug, Clone, Serialize)] struct UserServerKeyResponse { server_key: String, server_key_timestamp: String, } #[instrument] async fn generate_user_server_key_2( State(state): State>, Query(ApiVersion { api_version }): Query, Form(UserServerKeyRequest { username, token, server_hash, }): Form, ) -> ApiResult> { event!(Level::INFO, "Creating user_server_key"); let (server_key, server_key_timestamp) = state .user_server_key_generator .generate_user_server_key(&username, &token, &server_hash) .await?; Ok(Json(UserServerKeyResponse { server_key: server_key.0.expose_secret().to_owned(), server_key_timestamp, })) }