From 24c7072c0c877d8e666c9465e1d69d9f1cee9404 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 28 Mar 2024 18:58:51 +0100 Subject: [PATCH] Refactor code to prepare support of implicit flow --- src/controllers/openid_controller.rs | 108 +++++++++++++++------------ src/data/client.rs | 1 + 2 files changed, 63 insertions(+), 46 deletions(-) diff --git a/src/controllers/openid_controller.rs b/src/controllers/openid_controller.rs index 4c0d4af..edaa063 100644 --- a/src/controllers/openid_controller.rs +++ b/src/controllers/openid_controller.rs @@ -16,7 +16,7 @@ use crate::constants::*; use crate::controllers::base_controller::{build_fatal_error_page, redirect_user}; use crate::data::action_logger::{Action, ActionLogger}; use crate::data::app_config::AppConfig; -use crate::data::client::{ClientID, ClientManager}; +use crate::data::client::{AuthenticationFlow, ClientID, ClientManager}; use crate::data::code_challenge::CodeChallenge; use crate::data::current_user::CurrentUser; use crate::data::id_token::IdToken; @@ -162,14 +162,6 @@ pub async fn authorize( return error_redirect(&query, "invalid_request", "openid scope missing!"); } - if !query.response_type.eq("code") { - return error_redirect( - &query, - "invalid_request", - "Only code response type is supported!", - ); - } - if query.state.as_ref().map(String::is_empty).unwrap_or(false) { return error_redirect(&query, "invalid_request", "State is specified but empty!"); } @@ -201,45 +193,69 @@ pub async fn authorize( ); } - // Save all authentication information in memory - let session = Session { - session_id: SessionID(rand_str(OPEN_ID_SESSION_LEN)), - client: client.id.clone(), - user: user.uid.clone(), - auth_time: SessionIdentity(Some(&id)).auth_time(), - redirect_uri, - authorization_code: rand_str(OPEN_ID_AUTHORIZATION_CODE_LEN), - authorization_code_expire_at: time() + OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, - access_token: None, - access_token_expire_at: time() + OPEN_ID_ACCESS_TOKEN_TIMEOUT, - refresh_token: "".to_string(), - refresh_token_expire_at: 0, - nonce: query.0.nonce, - code_challenge, - }; - sessions - .send(openid_sessions_actor::PushNewSession(session.clone())) - .await - .unwrap(); + // Check that requested authorization flow is supported + if query.response_type != "code" && query.response_type != "id_token" { + return error_redirect(&query, "invalid_request", "Unsupported authorization flow!"); + } - log::trace!("New OpenID session: {:#?}", session); - logger.log(Action::NewOpenIDSession { client: &client }); + match (client.auth_flow(), query.response_type.as_str()) { + (AuthenticationFlow::AuthorizationCode, "code") => { + // Save all authentication information in memory + let session = Session { + session_id: SessionID(rand_str(OPEN_ID_SESSION_LEN)), + client: client.id.clone(), + user: user.uid.clone(), + auth_time: SessionIdentity(Some(&id)).auth_time(), + redirect_uri, + authorization_code: rand_str(OPEN_ID_AUTHORIZATION_CODE_LEN), + authorization_code_expire_at: time() + OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, + access_token: None, + access_token_expire_at: time() + OPEN_ID_ACCESS_TOKEN_TIMEOUT, + refresh_token: "".to_string(), + refresh_token_expire_at: 0, + nonce: query.0.nonce, + code_challenge, + }; + sessions + .send(openid_sessions_actor::PushNewSession(session.clone())) + .await + .unwrap(); - HttpResponse::Found() - .append_header(( - "Location", - format!( - "{}?{}session_state={}&code={}", - session.redirect_uri, - match &query.0.state { - Some(state) => format!("state={}&", urlencoding::encode(state)), - None => "".to_string(), - }, - urlencoding::encode(&session.session_id.0), - urlencoding::encode(&session.authorization_code) - ), - )) - .finish() + log::trace!("New OpenID session: {:#?}", session); + logger.log(Action::NewOpenIDSession { client: &client }); + + HttpResponse::Found() + .append_header(( + "Location", + format!( + "{}?{}session_state={}&code={}", + session.redirect_uri, + match &query.0.state { + Some(state) => format!("state={}&", urlencoding::encode(state)), + None => "".to_string(), + }, + urlencoding::encode(&session.session_id.0), + urlencoding::encode(&session.authorization_code) + ), + )) + .finish() + } + + //(AuthenticationFlow::Implicit, "id_token") => {} + (flow, code) => { + log::warn!( + "For client {:?}, configured with flow {:?}, made request with code {}", + client.id, + flow, + code + ); + error_redirect( + &query, + "invalid_request", + "Requested authentication flow is unsupported / not configured for this client!", + ) + } + } } #[derive(serde::Serialize)] diff --git a/src/data/client.rs b/src/data/client.rs index daf72bd..fdc2538 100644 --- a/src/data/client.rs +++ b/src/data/client.rs @@ -4,6 +4,7 @@ use crate::utils::string_utils::apply_env_vars; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct ClientID(pub String); +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum AuthenticationFlow { AuthorizationCode, Implicit,