Skip to content

Commit 512396f

Browse files
committed
#406 - apple id redirection fixes
1 parent ef6810c commit 512396f

File tree

2 files changed

+95
-17
lines changed

2 files changed

+95
-17
lines changed

warpgate-protocol-http/src/api/sso_provider_list.rs

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use poem::session::Session;
2-
use poem::web::Data;
2+
use poem::web::{Data, Form};
33
use poem::Request;
44
use poem_openapi::param::Query;
5-
use poem_openapi::payload::{Json, Response};
5+
use poem_openapi::payload::{Html, Json, Response};
66
use poem_openapi::{ApiResponse, Enum, Object, OpenApi};
7+
use serde::Deserialize;
78
use tracing::*;
89
use warpgate_common::auth::{AuthCredential, AuthResult};
910
use warpgate_core::Services;
@@ -42,6 +43,23 @@ enum ReturnToSsoResponse {
4243
Ok,
4344
}
4445

46+
#[allow(clippy::large_enum_variant)]
47+
#[derive(ApiResponse)]
48+
enum ReturnToSsoPostResponse {
49+
#[oai(status = 200)]
50+
Redirect(Html<String>),
51+
}
52+
53+
#[derive(Deserialize)]
54+
pub struct ReturnToSsoFormData {
55+
pub code: Option<String>,
56+
}
57+
58+
fn make_redirect_url(err: &str) -> String {
59+
error!("SSO error: {err}");
60+
format!("/@warpgate?login_error={err}")
61+
}
62+
4563
#[OpenApi]
4664
impl Api {
4765
#[oai(
@@ -73,25 +91,68 @@ impl Api {
7391
}
7492

7593
#[oai(path = "/sso/return", method = "get", operation_id = "return_to_sso")]
76-
async fn api_return_to_sso(
94+
async fn api_return_to_sso_get(
7795
&self,
7896
req: &Request,
7997
session: &Session,
8098
services: Data<&Services>,
8199
code: Query<Option<String>>,
82100
) -> poem::Result<Response<ReturnToSsoResponse>> {
83-
fn make_err_response(err: &str) -> poem::Result<Response<ReturnToSsoResponse>> {
84-
error!("SSO error: {err}");
85-
Ok(Response::new(ReturnToSsoResponse::Ok)
86-
.header("Location", format!("/@warpgate?login_error={err}")))
87-
}
101+
let url = self
102+
.api_return_to_sso_get_common(req, session, services, &*code)
103+
.await?
104+
.unwrap_or_else(|x| make_redirect_url(&x));
105+
106+
Ok(Response::new(ReturnToSsoResponse::Ok).header("Location", url))
107+
}
108+
109+
#[oai(
110+
path = "/sso/return",
111+
method = "post",
112+
operation_id = "return_to_sso_with_form_data"
113+
)]
114+
async fn api_return_to_sso_post(
115+
&self,
116+
req: &Request,
117+
session: &Session,
118+
services: Data<&Services>,
119+
data: Form<ReturnToSsoFormData>,
120+
) -> poem::Result<ReturnToSsoPostResponse> {
121+
let url = self
122+
.api_return_to_sso_get_common(req, session, services, &data.code)
123+
.await?
124+
.unwrap_or_else(|x| make_redirect_url(&x));
125+
let serialized_url =
126+
serde_json::to_string(&url).map_err(poem::error::InternalServerError)?;
127+
Ok(ReturnToSsoPostResponse::Redirect(
128+
poem_openapi::payload::Html(format!(
129+
"<!doctype html>\n
130+
<html>
131+
<script>
132+
location.href = {serialized_url};
133+
</script>
134+
<body>
135+
Redirecting to <a href='{url}'>{url}</a>...
136+
</body>
137+
</html>
138+
"
139+
)),
140+
))
141+
}
88142

143+
async fn api_return_to_sso_get_common(
144+
&self,
145+
req: &Request,
146+
session: &Session,
147+
services: Data<&Services>,
148+
code: &Option<String>,
149+
) -> poem::Result<Result<String, String>> {
89150
let Some(context) = session.get::<SsoContext>(SSO_CONTEXT_SESSION_KEY) else {
90-
return make_err_response("Not in an active SSO process");
151+
return Ok(Err("Not in an active SSO process".to_string()));
91152
};
92153

93154
let Some(ref code) = *code else {
94-
return make_err_response("No authorization code in the return URL request");
155+
return Ok(Err("No authorization code in the return URL request".to_string()));
95156
};
96157

97158
let response = context
@@ -101,11 +162,11 @@ impl Api {
101162
.map_err(poem::error::InternalServerError)?;
102163

103164
if !response.email_verified.unwrap_or(true) {
104-
return make_err_response("The SSO account's e-mail is not verified");
165+
return Ok(Err("The SSO account's e-mail is not verified".to_string()));
105166
}
106167

107168
let Some(email) = response.email else {
108-
return make_err_response("No e-mail information in the SSO response");
169+
return Ok(Err("No e-mail information in the SSO response".to_string()));
109170
};
110171

111172
info!("SSO login as {email}");
@@ -122,7 +183,7 @@ impl Api {
122183
.username_for_sso_credential(&cred)
123184
.await?;
124185
let Some(username) = username else {
125-
return make_err_response(&format!("No user matching {email}"));
186+
return Ok(Err(format!("No user matching {email}")));
126187
};
127188

128189
let mut auth_state_store = services.auth_state_store.lock().await;
@@ -141,9 +202,10 @@ impl Api {
141202
authorize_session(req, username).await?;
142203
}
143204

144-
Ok(Response::new(ReturnToSsoResponse::Ok).header(
145-
"Location",
146-
context.next_url.as_deref().unwrap_or("/@warpgate#/login"),
147-
))
205+
Ok(Ok(context
206+
.next_url
207+
.as_deref()
208+
.unwrap_or("/@warpgate#/login")
209+
.to_owned()))
148210
}
149211
}

warpgate-sso/src/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use once_cell::sync::Lazy;
24
use openidconnect::{ClientId, ClientSecret, IssuerUrl};
35
use serde::{Deserialize, Serialize};
@@ -108,4 +110,18 @@ impl SsoInternalProviderConfig {
108110
SsoInternalProviderConfig::Custom { scopes, .. } => scopes.clone(),
109111
}
110112
}
113+
114+
#[inline]
115+
pub fn extra_parameters(&self) -> HashMap<String, String> {
116+
match self {
117+
SsoInternalProviderConfig::Google { .. }
118+
| SsoInternalProviderConfig::Custom { .. }
119+
| SsoInternalProviderConfig::Azure { .. } => HashMap::new(),
120+
SsoInternalProviderConfig::Apple { .. } => {
121+
let mut map = HashMap::new();
122+
map.insert("response_mode".to_string(), "form_post".to_string());
123+
map
124+
}
125+
}
126+
}
111127
}

0 commit comments

Comments
 (0)