diff --git a/src/controllers/login_controller.rs b/src/controllers/login_controller.rs index ba9fc61..5bcf1f3 100644 --- a/src/controllers/login_controller.rs +++ b/src/controllers/login_controller.rs @@ -19,12 +19,12 @@ use crate::data::session_identity::{SessionIdentity, SessionStatus}; use crate::data::user::User; use crate::data::webauthn_manager::WebAuthManagerReq; -struct BaseLoginPage<'a> { - danger: Option, - success: Option, - page_title: &'static str, - app_name: &'static str, - redirect_uri: &'a LoginRedirect, +pub struct BaseLoginPage<'a> { + pub danger: Option, + pub success: Option, + pub page_title: &'static str, + pub app_name: &'static str, + pub redirect_uri: &'a LoginRedirect, } #[derive(Template)] diff --git a/src/controllers/providers_controller.rs b/src/controllers/providers_controller.rs index ff53d3a..16a87c7 100644 --- a/src/controllers/providers_controller.rs +++ b/src/controllers/providers_controller.rs @@ -2,16 +2,43 @@ use std::sync::Arc; use actix::Addr; use actix_web::{web, HttpResponse, Responder}; +use askama::Template; use crate::actors::providers_states_actor; use crate::actors::providers_states_actor::{ProviderLoginState, ProvidersStatesActor}; +use crate::constants::APP_NAME; use crate::controllers::base_controller::{build_fatal_error_page, redirect_user}; +use crate::controllers::login_controller::BaseLoginPage; use crate::data::action_logger::{Action, ActionLogger}; use crate::data::login_redirect::LoginRedirect; use crate::data::provider::{ProviderID, ProvidersManager}; use crate::data::provider_configuration::ProviderConfigurationHelper; use crate::data::remote_ip::RemoteIP; +#[derive(askama::Template)] +#[template(path = "login/prov_login_error.html")] +struct ProviderLoginError<'a> { + _p: BaseLoginPage<'a>, + message: &'a str, +} + +impl<'a> ProviderLoginError<'a> { + pub fn get(message: &'a str, redirect_uri: &'a LoginRedirect) -> String { + Self { + _p: BaseLoginPage { + danger: None, + success: None, + page_title: "Upstream login", + app_name: APP_NAME, + redirect_uri, + }, + message, + } + .render() + .unwrap() + } +} + #[derive(serde::Deserialize)] pub struct StartLoginQuery { #[serde(default)] @@ -20,7 +47,6 @@ pub struct StartLoginQuery { } /// Start user authentication using a provider -#[allow(clippy::too_many_arguments)] pub async fn start_login( remote_ip: RemoteIP, providers: web::Data>, @@ -71,3 +97,66 @@ pub async fn start_login( // Redirect user redirect_user(&url) } + +#[derive(serde::Deserialize)] +pub struct FinishLoginSuccess { + code: String, + state: String, +} + +#[derive(serde::Deserialize)] +pub struct FinishLoginError { + error: String, + error_description: Option, +} + +#[derive(serde::Deserialize)] +pub struct FinishLoginQuery { + #[serde(flatten)] + success: Option, + #[serde(flatten)] + error: Option, +} + +/// Finish user authentication using a provider +pub async fn finish_login( + remote_ip: RemoteIP, + providers: web::Data>, + states: web::Data>, + query: web::Query, + logger: ActionLogger, +) -> impl Responder { + let query = match query.0.success { + Some(q) => q, + None => { + let error_message = query + .0 + .error + .map(|e| e.error_description.unwrap_or(e.error)) + .unwrap_or("Authentication failed (unspecified error)!".to_string()); + + return HttpResponse::Unauthorized().body(ProviderLoginError::get( + &error_message, + &LoginRedirect::default(), + )); + } + }; + + // Get & consume state + let state = states + .send(providers_states_actor::ConsumeState { + ip: remote_ip.0, + state_id: query.state.clone(), + }) + .await + .unwrap(); + + // TODO : rate limiting + // TODO : finish login, get user information + // TODO : check token signature + // TODO : check if user is authorized to access application + // TODO : check if 2FA is enabled + // TODO : redirect user to login route + // TODO : add proper logging + HttpResponse::Ok().body("continue") +} diff --git a/src/main.rs b/src/main.rs index cf3b8b2..1093f87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,6 +166,10 @@ async fn main() -> std::io::Result<()> { "/login_with_prov", web::get().to(providers_controller::start_login), ) + .route( + OIDC_PROVIDER_CB_URI, + web::get().to(providers_controller::finish_login), + ) // Settings routes .route( "/settings", diff --git a/templates/login/prov_login_error.html b/templates/login/prov_login_error.html new file mode 100644 index 0000000..56f692a --- /dev/null +++ b/templates/login/prov_login_error.html @@ -0,0 +1,13 @@ +{% extends "base_login_page.html" %} +{% block content %} + +
+ Authentication failed! + +

{{ message }}

+
+ +Go back to login + + +{% endblock content %} \ No newline at end of file