Skip to content
Clone
9362a10
71 lines 2.1 KB Rust
use export::http::{Query, Redirect, Response, State};
use serde::Deserialize;
use url::form_urlencoded;
use crate::auth::errors::ApiError;
use crate::auth::services::AuthService;
#[derive(Debug, Deserialize)]
pub struct LoginQuery {
#[serde(default)]
pub redirect: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct SignupQuery {
#[serde(default)]
pub redirect: Option<String>,
}
pub async fn login(
State(service): State<AuthService>,
Query(query): Query<LoginQuery>,
) -> Result<Response, ApiError> {
redirect_to_identity(&service, "/login", query.redirect.as_deref())
}
pub async fn signup(
State(service): State<AuthService>,
Query(query): Query<SignupQuery>,
) -> Result<Response, ApiError> {
redirect_to_identity(&service, "/register", query.redirect.as_deref())
}
fn redirect_to_identity(
service: &AuthService,
path: &str,
redirect: Option<&str>,
) -> Result<Response, ApiError> {
let client = service
.state
.kenauth
.as_ref()
.ok_or_else(|| ApiError::internal("identity service is unavailable"))?;
let issuer = client.service().signing().issuer().trim_end_matches('/');
let base = service
.config()
.web
.web_base_url
.trim()
.trim_end_matches('/');
let callback_path = if base.is_empty() {
"/api/v1/auth/callback".to_owned()
} else {
format!("{base}/api/v1/auth/callback")
};
let mut serializer = form_urlencoded::Serializer::new(String::new());
serializer.append_pair("redirect", &sanitize_redirect(redirect));
let callback = format!("{callback_path}?{}", serializer.finish());
let mut serializer = form_urlencoded::Serializer::new(String::new());
serializer.append_pair("callback_url", &callback);
let location = format!("{issuer}{path}?{}", serializer.finish());
Ok(Redirect::to(&location).into_response())
}
fn sanitize_redirect(raw: Option<&str>) -> String {
match raw.map(str::trim) {
Some(value) if value.starts_with('/') && !value.starts_with("//") => value.to_string(),
_ => "/app".to_string(),
}
}