#406 - apple id redirection fixes

This commit is contained in:
Eugene Pankov 2022-10-29 20:51:29 +02:00
parent ef6810c9b5
commit 512396ffb4
No known key found for this signature in database
GPG key ID: 5896FCBBDD1CF4F4
2 changed files with 95 additions and 17 deletions

View file

@ -1,9 +1,10 @@
use poem::session::Session;
use poem::web::Data;
use poem::web::{Data, Form};
use poem::Request;
use poem_openapi::param::Query;
use poem_openapi::payload::{Json, Response};
use poem_openapi::payload::{Html, Json, Response};
use poem_openapi::{ApiResponse, Enum, Object, OpenApi};
use serde::Deserialize;
use tracing::*;
use warpgate_common::auth::{AuthCredential, AuthResult};
use warpgate_core::Services;
@ -42,6 +43,23 @@ enum ReturnToSsoResponse {
Ok,
}
#[allow(clippy::large_enum_variant)]
#[derive(ApiResponse)]
enum ReturnToSsoPostResponse {
#[oai(status = 200)]
Redirect(Html<String>),
}
#[derive(Deserialize)]
pub struct ReturnToSsoFormData {
pub code: Option<String>,
}
fn make_redirect_url(err: &str) -> String {
error!("SSO error: {err}");
format!("/@warpgate?login_error={err}")
}
#[OpenApi]
impl Api {
#[oai(
@ -73,25 +91,68 @@ impl Api {
}
#[oai(path = "/sso/return", method = "get", operation_id = "return_to_sso")]
async fn api_return_to_sso(
async fn api_return_to_sso_get(
&self,
req: &Request,
session: &Session,
services: Data<&Services>,
code: Query<Option<String>>,
) -> poem::Result<Response<ReturnToSsoResponse>> {
fn make_err_response(err: &str) -> poem::Result<Response<ReturnToSsoResponse>> {
error!("SSO error: {err}");
Ok(Response::new(ReturnToSsoResponse::Ok)
.header("Location", format!("/@warpgate?login_error={err}")))
}
let url = self
.api_return_to_sso_get_common(req, session, services, &*code)
.await?
.unwrap_or_else(|x| make_redirect_url(&x));
Ok(Response::new(ReturnToSsoResponse::Ok).header("Location", url))
}
#[oai(
path = "/sso/return",
method = "post",
operation_id = "return_to_sso_with_form_data"
)]
async fn api_return_to_sso_post(
&self,
req: &Request,
session: &Session,
services: Data<&Services>,
data: Form<ReturnToSsoFormData>,
) -> poem::Result<ReturnToSsoPostResponse> {
let url = self
.api_return_to_sso_get_common(req, session, services, &data.code)
.await?
.unwrap_or_else(|x| make_redirect_url(&x));
let serialized_url =
serde_json::to_string(&url).map_err(poem::error::InternalServerError)?;
Ok(ReturnToSsoPostResponse::Redirect(
poem_openapi::payload::Html(format!(
"<!doctype html>\n
<html>
<script>
location.href = {serialized_url};
</script>
<body>
Redirecting to <a href='{url}'>{url}</a>...
</body>
</html>
"
)),
))
}
async fn api_return_to_sso_get_common(
&self,
req: &Request,
session: &Session,
services: Data<&Services>,
code: &Option<String>,
) -> poem::Result<Result<String, String>> {
let Some(context) = session.get::<SsoContext>(SSO_CONTEXT_SESSION_KEY) else {
return make_err_response("Not in an active SSO process");
return Ok(Err("Not in an active SSO process".to_string()));
};
let Some(ref code) = *code else {
return make_err_response("No authorization code in the return URL request");
return Ok(Err("No authorization code in the return URL request".to_string()));
};
let response = context
@ -101,11 +162,11 @@ impl Api {
.map_err(poem::error::InternalServerError)?;
if !response.email_verified.unwrap_or(true) {
return make_err_response("The SSO account's e-mail is not verified");
return Ok(Err("The SSO account's e-mail is not verified".to_string()));
}
let Some(email) = response.email else {
return make_err_response("No e-mail information in the SSO response");
return Ok(Err("No e-mail information in the SSO response".to_string()));
};
info!("SSO login as {email}");
@ -122,7 +183,7 @@ impl Api {
.username_for_sso_credential(&cred)
.await?;
let Some(username) = username else {
return make_err_response(&format!("No user matching {email}"));
return Ok(Err(format!("No user matching {email}")));
};
let mut auth_state_store = services.auth_state_store.lock().await;
@ -141,9 +202,10 @@ impl Api {
authorize_session(req, username).await?;
}
Ok(Response::new(ReturnToSsoResponse::Ok).header(
"Location",
context.next_url.as_deref().unwrap_or("/@warpgate#/login"),
))
Ok(Ok(context
.next_url
.as_deref()
.unwrap_or("/@warpgate#/login")
.to_owned()))
}
}

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use once_cell::sync::Lazy;
use openidconnect::{ClientId, ClientSecret, IssuerUrl};
use serde::{Deserialize, Serialize};
@ -108,4 +110,18 @@ impl SsoInternalProviderConfig {
SsoInternalProviderConfig::Custom { scopes, .. } => scopes.clone(),
}
}
#[inline]
pub fn extra_parameters(&self) -> HashMap<String, String> {
match self {
SsoInternalProviderConfig::Google { .. }
| SsoInternalProviderConfig::Custom { .. }
| SsoInternalProviderConfig::Azure { .. } => HashMap::new(),
SsoInternalProviderConfig::Apple { .. } => {
let mut map = HashMap::new();
map.insert("response_mode".to_string(), "form_post".to_string());
map
}
}
}
}