diff --git a/.gitignore b/.gitignore index 4ba0216..e33f9b6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,8 @@ target/ config.toml -.idea/ \ No newline at end of file +.idea/ + +.env + +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a0bfb57..f10e55d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,6 +353,7 @@ dependencies = [ "axum", "axum-extra", "axum-keycloak-auth", + "base64", "chrono", "log", "oauth2", diff --git a/Cargo.toml b/Cargo.toml index 1c1a4e8..8d5ba96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ toml = "0.9.7" oauth2 = "5.0.0" uuid = "1.18.1" axum-extra = { version = "0.10.3", features = ["cookie"] } +base64 = "0.22.1" diff --git a/src/api.rs b/src/api.rs index 27c8c1b..fe8d6e8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,6 +1,6 @@ use crate::gsd::dto::GSDResponseDTO; use crate::middleware::AppState; -use crate::util::set_and_log_session; +use crate::util::{decode_payload_unchecked, set_and_log_session}; use axum::Extension; use axum::body::Body; use axum::extract::Request; @@ -8,6 +8,7 @@ use axum::http::{HeaderValue, StatusCode}; use axum::response::IntoResponse; use log::{error, info}; use std::sync::Arc; +use crate::model::{User}; pub async fn handle_post( Extension(state): Extension>, @@ -94,4 +95,9 @@ pub async fn handle_post( } } -pub async fn handle_login() -> impl IntoResponse {} +pub async fn userinfo(request: Request) -> impl IntoResponse { + let access_token_string = &request.headers().get("authorization").unwrap().to_str().unwrap().to_string()[7..]; + let user = decode_payload_unchecked::(access_token_string).unwrap(); + + serde_json::to_string(&user.employee).unwrap().into_response() +} diff --git a/src/auth.rs b/src/auth.rs index 5d4277e..4095541 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use crate::config::Config; use crate::middleware::AppState; use crate::repository::RedisRepository; use axum::http::{StatusCode, header}; -use axum::response::Response; +use axum::response::{Html, Response}; use axum::{ Router, extract::{Query, State}, @@ -22,6 +23,7 @@ use oauth2::{ use serde::{Deserialize, Serialize}; use std::sync::Arc; use axum::routing::post; +use log::info; pub type OAuthClient = Client< BasicErrorResponse, @@ -188,23 +190,13 @@ async fn callback( return (StatusCode::INTERNAL_SERVER_ERROR, "Login failed").into_response(); } - log::info!("Successfully created session {} for user", session_id); - - let cookie = format!( - "session_id={}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400", - session_id - ); + info!("Successfully created session {} for user", session_id); + info!("Token scopes: {:?}", token.extra_fields()); // 4. Redirect to frontend - let redirect_url = format!("{}?login=success", cloned_state.frontend_url); + let redirect_url = format!("{}/?session_id={}", cloned_state.frontend_url.clone(), session_id); - Response::builder() - .status(StatusCode::FOUND) - .header(header::SET_COOKIE, cookie) - .header(header::LOCATION, redirect_url.clone()) - .body::(format!("Redirecting to {}", redirect_url).into()) - .unwrap() - .into_response() + Redirect::to(redirect_url.as_str()).into_response() } Err(e) => { log::error!("Token exchange failed: {:?}", e); diff --git a/src/main.rs b/src/main.rs index f34199d..07a067b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -use crate::api::handle_post; +use crate::api::{handle_post, userinfo}; use crate::config::load_config; use crate::middleware::AppState; use crate::repository::RedisRepository; use crate::util::initialize_logging; -use axum::routing::post; +use axum::routing::{get, post}; use axum::{Extension, Router}; use axum_keycloak_auth::instance::{KeycloakAuthInstance, KeycloakConfig}; use axum_keycloak_auth::layer::KeycloakAuthLayer; @@ -18,12 +18,14 @@ mod gsd; mod middleware; mod repository; mod util; +mod model; #[tokio::main] async fn main() -> Result<(), Box> { let config = load_config()?; initialize_logging(&config); + info!("Redirect URI: {}", config.keycloak.redirect_url); info!("Logging initialized"); info!("Starting Holzleitner Delivery Backend"); @@ -51,6 +53,7 @@ async fn main() -> Result<(), Box> { let auth_router = auth::router(state.clone()); let proxy_router = Router::new() .route("/{*wildcard}", post(handle_post)) + .route("/userinfo", get(userinfo)) .route_layer(Extension(state.clone())) .route_layer(axum::middleware::from_fn_with_state( state.clone(), diff --git a/src/middleware.rs b/src/middleware.rs index 515109a..ec2e7be 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -172,5 +172,10 @@ pub async fn gsd_decorate_header( HeaderValue::from_str(state_cloned.config.gsd_app_key.as_str()).unwrap(), ); - next.run(request).await + let response = next.run(request).await; + + info!("Response: {:?}", response); + + response + } diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..7a8dbcc --- /dev/null +++ b/src/model.rs @@ -0,0 +1,13 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct User { + pub employee: Employee, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Employee { + pub number: String, + pub last_name: String, + pub first_name: String, + pub mail: String +} \ No newline at end of file diff --git a/src/util.rs b/src/util.rs index 24a2ed6..b19a4ac 100644 --- a/src/util.rs +++ b/src/util.rs @@ -6,6 +6,18 @@ use log::{LevelFilter, error, info}; use simplelog::{ColorChoice, CombinedLogger, TermLogger, TerminalMode, WriteLogger}; use std::fs::File; use std::sync::Arc; +use serde::de::DeserializeOwned; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; + +pub fn decode_payload_unchecked(token: &str) -> Result> { + let mut parts = token.split('.'); + let _header = parts.next().ok_or("missing header")?; + let payload_b64 = parts.next().ok_or("missing payload")?; + // signature is parts.next() but we ignore it here + let payload = URL_SAFE_NO_PAD.decode(payload_b64.as_bytes())?; + let claims = serde_json::from_slice::(&payload)?; + Ok(claims) +} pub fn initialize_logging(config: &Config) { CombinedLogger::init(vec![