Skip to content

Commit 2e23cec

Browse files
committed
Fix OIDC flow: Redirect to initial URL on error
- Store initial URL in OidcLoginState. - Use initial URL when building redirect response on callback error.
1 parent 1c3b2c7 commit 2e23cec

File tree

1 file changed

+27
-24
lines changed

1 file changed

+27
-24
lines changed

src/webserver/oidc.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ async fn handle_unauthenticated_request(
368368

369369
log::debug!("Redirecting to OIDC provider");
370370

371-
let response = build_auth_provider_redirect_response(oidc_state, &request).await;
371+
let initial_url = request.uri().to_string();
372+
let response = build_auth_provider_redirect_response(oidc_state, initial_url).await;
372373
Ok(MiddlewareResponse::Respond(request.into_response(response)))
373374
}
374375

@@ -380,9 +381,11 @@ async fn handle_oidc_callback(
380381
match process_oidc_callback(oidc_state, query_string, &request).await {
381382
Ok(response) => Ok(request.into_response(response)),
382383
Err(e) => {
383-
log::error!("Failed to process OIDC callback with params {query_string}: {e:#}");
384+
let redirect_url =
385+
get_state_from_cookie(&request).map_or_else(|_| "/".into(), |s| s.initial_url);
386+
log::error!("Failed to process OIDC callback. Refreshing oidc provider metadata, then redirecting to {redirect_url}: {e:#}");
384387
oidc_state.refresh_on_error(&request).await;
385-
let resp = build_auth_provider_redirect_response(oidc_state, &request).await;
388+
let resp = build_auth_provider_redirect_response(oidc_state, redirect_url).await;
386389
Ok(request.into_response(resp))
387390
}
388391
}
@@ -511,10 +514,11 @@ async fn set_auth_cookie(
511514

512515
async fn build_auth_provider_redirect_response(
513516
oidc_state: &OidcState,
514-
request: &ServiceRequest,
517+
initial_url: String,
515518
) -> HttpResponse {
516519
let AuthUrl { url, params } = build_auth_url(oidc_state).await;
517-
let state_cookie = create_state_cookie(request, params);
520+
let state = OidcLoginState::new(initial_url, params);
521+
let state_cookie = create_state_cookie(&state);
518522
HttpResponse::TemporaryRedirect()
519523
.append_header(("Location", url.to_string()))
520524
.cookie(state_cookie)
@@ -730,20 +734,6 @@ async fn build_auth_url(oidc_state: &OidcState) -> AuthUrl {
730734
}
731735
}
732736

733-
#[derive(Debug, Serialize, Deserialize)]
734-
struct OidcLoginState {
735-
/// The URL to redirect to after the login process is complete.
736-
#[serde(rename = "u")]
737-
initial_url: String,
738-
/// The CSRF token to use for the login process.
739-
#[serde(rename = "c")]
740-
csrf_token: CsrfToken,
741-
/// The source nonce to use for the login process. It must be checked against the hash
742-
/// stored in the ID token.
743-
#[serde(rename = "n")]
744-
nonce: Nonce,
745-
}
746-
747737
fn hash_nonce(nonce: &Nonce) -> String {
748738
use argon2::password_hash::{rand_core::OsRng, PasswordHasher, SaltString};
749739
let salt = SaltString::generate(&mut OsRng);
@@ -791,19 +781,32 @@ fn nonce_matches(id_token_nonce: &Nonce, state_nonce: &Nonce) -> Result<(), Stri
791781
Ok(())
792782
}
793783

784+
#[derive(Debug, Serialize, Deserialize)]
785+
struct OidcLoginState {
786+
/// The URL to redirect to after the login process is complete.
787+
#[serde(rename = "u")]
788+
initial_url: String,
789+
/// The CSRF token to use for the login process.
790+
#[serde(rename = "c")]
791+
csrf_token: CsrfToken,
792+
/// The source nonce to use for the login process. It must be checked against the hash
793+
/// stored in the ID token.
794+
#[serde(rename = "n")]
795+
nonce: Nonce,
796+
}
797+
794798
impl OidcLoginState {
795-
fn new(request: &ServiceRequest, auth_url: AuthUrlParams) -> Self {
799+
fn new(initial_url: String, auth_url: AuthUrlParams) -> Self {
796800
Self {
797-
initial_url: request.uri().to_string(),
801+
initial_url,
798802
csrf_token: auth_url.csrf_token,
799803
nonce: auth_url.nonce,
800804
}
801805
}
802806
}
803807

804-
fn create_state_cookie(request: &ServiceRequest, auth_url: AuthUrlParams) -> Cookie {
805-
let state = OidcLoginState::new(request, auth_url);
806-
let state_json = serde_json::to_string(&state).unwrap();
808+
fn create_state_cookie(login_state: &OidcLoginState) -> Cookie {
809+
let state_json = serde_json::to_string(login_state).unwrap();
807810
Cookie::build(SQLPAGE_STATE_COOKIE_NAME, state_json)
808811
.secure(true)
809812
.http_only(true)

0 commit comments

Comments
 (0)