Set up the most basic of authentication clients

This commit is contained in:
Savanni D'Gerinel 2025-02-20 07:39:35 -05:00
parent e8a8a12de3
commit fd3ca9f561
4 changed files with 57 additions and 36 deletions

View File

@ -92,17 +92,16 @@ struct UserOverview {
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(tag = "type", content = "content", rename_all = "kebab-case")]
#[typeshare] #[typeshare]
enum AuthResponse<A> { enum AuthResponse {
Success(A), Success(SessionId),
PasswordReset(SessionId), PasswordReset(SessionId),
} }
#[axum::debug_handler] #[axum::debug_handler]
async fn check_password( async fn check_password(
request: Json<AuthRequest>, request: Json<AuthRequest>,
) -> (StatusCode, Json<Option<AuthResponse<SessionId>>>) { ) -> (StatusCode, Json<Option<AuthResponse>>) {
let Json(request) = request; let Json(request) = request;
if request.username == "vakarian" && request.password == "aoeu" { if request.username == "vakarian" && request.password == "aoeu" {
( (
@ -183,21 +182,20 @@ async fn main() {
let app = Router::new() let app = Router::new()
.route( .route(
"/api/test/health", "/api/test/health",
get(|| async { (StatusCode::OK, Json(None::<String>)) }), get(|| async { (StatusCode::OK, Json(None::<String>)) }).layer(
) CorsLayer::new()
.layer( .allow_methods([Method::GET])
CorsLayer::new() .allow_origin(Any),
.allow_methods([Method::GET]) ),
.allow_origin(Any),
) )
.route( .route(
"/api/test/auth", "/api/test/auth",
post(|req: Json<AuthRequest>| check_password(req)), post(|req: Json<AuthRequest>| check_password(req)).layer(
) CorsLayer::new()
.layer( .allow_methods([Method::POST])
CorsLayer::new() .allow_headers([CONTENT_TYPE])
.allow_methods([Method::POST]) .allow_origin(Any),
.allow_origin(Any), ),
) )
.route( .route(
"/api/test/list-users", "/api/test/list-users",
@ -226,13 +224,13 @@ async fn main() {
]), ]),
) )
}) })
}), })
) .layer(
.layer( CorsLayer::new()
CorsLayer::new() .allow_headers([AUTHORIZATION])
.allow_headers([AUTHORIZATION]) .allow_methods([Method::GET])
.allow_methods([Method::GET]) .allow_origin(Any),
.allow_origin(Any), ),
); );
let listener = tokio::net::TcpListener::bind("127.0.0.1:8001") let listener = tokio::net::TcpListener::bind("127.0.0.1:8001")
.await .await

3
visions/ui/Trunk.toml Normal file
View File

@ -0,0 +1,3 @@
[[proxy]]
backend = "http://localhost:8001/api"
insecure = true

View File

@ -1,3 +1,6 @@
use std::future::Future;
use gloo_console::log;
use gloo_net::http::{Request, Response}; use gloo_net::http::{Request, Response};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_bindgen_futures::wasm_bindgen::{self, JsValue}; use wasm_bindgen_futures::wasm_bindgen::{self, JsValue};
@ -9,15 +12,15 @@ struct AuthRequest {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
enum AuthResponse { pub enum AuthResponse {
Ok(SessionId), Success(SessionId),
PasswordReset(SessionId), PasswordReset(SessionId),
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct SessionId(String); pub struct SessionId(pub String);
enum ClientError { pub enum ClientError {
Unauthorized, Unauthorized,
Err(u16), Err(u16),
} }
@ -31,9 +34,9 @@ struct UserInfo {
name: String, name: String,
} }
trait Client { pub trait Client {
async fn auth(username: String, password: String) -> Result<AuthResponse, ClientError>; fn auth(&self, username: String, password: String) -> impl Future<Output = Result<AuthResponse, ClientError>>;
async fn list_users(session_id: SessionId) -> Result<Vec<UserInfo>, ClientError>; fn list_users(&self, session_id: SessionId) -> impl Future<Output = Result<Vec<UserInfo>, ClientError>>;
} }
pub struct Connection; pub struct Connection;
@ -43,9 +46,11 @@ impl Connection {
} }
impl Client for Connection { impl Client for Connection {
async fn auth(username: String, password: String) -> Result<AuthResponse, ClientError> { async fn auth(&self, username: String, password: String) -> Result<AuthResponse, ClientError> {
let response: Response = Request::post("http://localhost:8001") log!("authenticating: ", &username, &password);
.body(serde_wasm_bindgen::to_value(&AuthRequest{ username, password }).unwrap()) let response: Response = Request::post("/api/test/auth")
.header("Content-Type", "application/json")
.body(serde_wasm_bindgen::to_value(&serde_json::to_string(&AuthRequest{ username, password }).unwrap()).unwrap())
.unwrap() .unwrap()
.send() .send()
.await .await
@ -58,7 +63,7 @@ impl Client for Connection {
} }
} }
async fn list_users(session_id: SessionId) -> Result<Vec<UserInfo>, ClientError> { async fn list_users(&self, session_id: SessionId) -> Result<Vec<UserInfo>, ClientError> {
todo!() todo!()
} }
} }

View File

@ -27,6 +27,7 @@ impl Reducible for AuthInfo {
type Action = AuthAction; type Action = AuthAction;
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> { fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
// log!("reduce", action);
match action { match action {
AuthAction::Auth(session_id) => Self { AuthAction::Auth(session_id) => Self {
session_id: Some(session_id), session_id: Some(session_id),
@ -98,11 +99,25 @@ fn Login(LoginProps { on_login }: &LoginProps) -> Html {
#[function_component] #[function_component]
fn App() -> Html { fn App() -> Html {
let auth_info = use_reducer(AuthInfo::default); let auth_info = use_reducer(AuthInfo::default);
let client = Connection::new();
let on_login = { let on_login = {
let auth_info = auth_info.clone(); let auth_info = auth_info.clone();
Callback::from(move |(username, password)| auth_info.dispatch(AuthAction::Auth(username))) Callback::from(move |(username, password)| {
let auth_info = auth_info.clone();
wasm_bindgen_futures::spawn_local(async move {
let client = Connection::new();
match client.auth(username, password).await {
Ok(AuthResponse::Success(session_id)) => {
auth_info.dispatch(AuthAction::Auth(session_id.0))
}
Ok(AuthResponse::PasswordReset(session_id)) => {
auth_info.dispatch(AuthAction::Auth(session_id.0))
}
Err(ClientError::Unauthorized) => todo!(),
Err(ClientError::Err(status)) => todo!(),
};
})
})
}; };
if auth_info.session_id.is_some() { if auth_info.session_id.is_some() {