Allow redirect url path when user signs in #316

Open
reda wants to merge 3 commits from feat/auth-add-next-url-redirect into master
8 changed files with 44 additions and 27 deletions

View File

@@ -1,7 +0,0 @@
FLEET_AUTH_ISSUER_URL=
FLEET_AUTH_AUTHORIZE_URL=
FLEET_AUTH_TOKEN_URL=
FLEET_AUTH_CLIENT_ID=
FLEET_AUTH_REDIRECT_URI=
FLEET_AUTH_SCOPE=
FLEET_AUTH_TRUSTED_AUDIENCES=

View File

@@ -759,7 +759,7 @@ pub async fn mint_access_token(
access_token: String, access_token: String,
} }
let tr: TokenResponse = resp.json().await.context("parse token response")?; let tr: TokenResponse = resp.json().await.context("parse token response")?;
if std::env::var("FLEET_AUTH_CALLOUT_DEBUG_TOKENS").is_ok() if std::env::var("HARMONY_SSO_CALLOUT_DEBUG_TOKENS").is_ok()
&& let Some(payload_b64) = tr.access_token.split('.').nth(1) && let Some(payload_b64) = tr.access_token.split('.').nth(1)
{ {
use base64::Engine; use base64::Engine;

View File

@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
export BASE_URL=http://localhost:18080 export BASE_URL=http://localhost:18080
export FLEET_AUTH_ZITADEL_BASE=https://sso-stg.cb1.nationtech.io export HARMONY_SSO_ZITADEL_BASE=https://sso-stg.cb1.nationtech.io
export FLEET_AUTH_CLIENT_ID=372626218874372917 export HARMONY_SSO_CLIENT_ID=372626218874372917
export FLEET_AUTH_SCOPE="openid profile email" export HARMONY_SSO_SCOPE="openid profile email"
export FLEET_AUTH_LOGOUT_REDIRECT_URI="http://localhost:18080/" export HARMONY_SSO_LOGOUT_REDIRECT_URI="http://localhost:18080/"
export FLEET_OPERATOR_COOKIE_KEY_B64=6eKVpj88jwIcmaJajPfohdaIXhSPlfYCrHaOfymTcIWBAIadvhg7NHpMo5vPSMy90vac3cq2liWe1naSgkbaYg== export HARMONY_COOKIE_KEY_B64=6eKVpj88jwIcmaJajPfohdaIXhSPlfYCrHaOfymTcIWBAIadvhg7NHpMo5vPSMy90vac3cq2liWe1naSgkbaYg==
export FLEET_AUTH_TRUSTED_AUDIENCES=371639797493596981,371683318111994677,372626218874372917,371639797157987125 export HARMONY_SSO_TRUSTED_AUDIENCES=371639797493596981,371683318111994677,372626218874372917,371639797157987125
export BASE_URL=http://localhost:18080 export BASE_URL=http://localhost:18080
export RUST_LOG=debug export RUST_LOG=debug

View File

@@ -9,8 +9,8 @@ use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use crate::config::ZitadelAuthConfig; use crate::config::ZitadelAuthConfig;
use crate::jwks::JwksCache; use crate::jwks::JwksCache;
use crate::login::{ use crate::login::{
AuthCallbackQuery, RawAuthCallbackQuery, TokenResponse, build_login_attempt, build_logout_url, AuthCallbackQuery, LoginQuery, RawAuthCallbackQuery, TokenResponse, build_login_attempt,
exchange_code_for_token, jwt_exp, validate_callback_state, build_logout_url, exchange_code_for_token, jwt_exp, valid_next, validate_callback_state,
}; };
use crate::session::LoginAttemptCookie; use crate::session::LoginAttemptCookie;
@@ -23,8 +23,9 @@ pub const HARMONY_SESSION_COOKIE: &str = "harmony_fleet_session";
pub async fn login_handler( pub async fn login_handler(
jar: PrivateCookieJar, jar: PrivateCookieJar,
State(config): State<ZitadelAuthConfig>, State(config): State<ZitadelAuthConfig>,
Query(query): Query<LoginQuery>,
) -> Response { ) -> Response {
match build_login_response(jar, &config) { match build_login_response(jar, &config, query) {
Ok(r) => r.into_response(), Ok(r) => r.into_response(),
Err(e) => auth_error_response(e), Err(e) => auth_error_response(e),
} }
@@ -33,9 +34,16 @@ pub async fn login_handler(
fn build_login_response( fn build_login_response(
jar: PrivateCookieJar, jar: PrivateCookieJar,
config: &ZitadelAuthConfig, config: &ZitadelAuthConfig,
query: LoginQuery,
) -> Result<impl IntoResponse> { ) -> Result<impl IntoResponse> {
let attempt = build_login_attempt(config)?; let attempt = build_login_attempt(config)?;
let cookie_payload = LoginAttemptCookie::from(&attempt); let mut cookie_payload = LoginAttemptCookie::from(&attempt);
cookie_payload.next = Some(
query
.next
.filter(|next| valid_next(next))
.unwrap_or_else(|| "/".to_string()),
);
let cookie_value = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&cookie_payload)?); let cookie_value = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&cookie_payload)?);
let mut builder = Cookie::build((LOGIN_ATTEMPT_COOKIE, cookie_value)) let mut builder = Cookie::build((LOGIN_ATTEMPT_COOKIE, cookie_value))
@@ -113,7 +121,12 @@ async fn build_callback_response(
} }
let session_jar = session_jar.add(session_cookie(&tokens, config)); let session_jar = session_jar.add(session_cookie(&tokens, config));
Ok((jar, session_jar, Redirect::to("/")).into_response()) let next = attempt
.next
.as_deref()
.filter(|next| valid_next(next))
.unwrap_or("/");
Ok((jar, session_jar, Redirect::to(next)).into_response())
} }
AuthCallbackQuery::Failure { AuthCallbackQuery::Failure {
error, error,

View File

@@ -33,13 +33,13 @@ impl ZitadelAuthConfig {
} }
} }
pub const ZITADEL_BASE_ENV: &str = "FLEET_AUTH_ZITADEL_BASE"; pub const ZITADEL_BASE_ENV: &str = "HARMONY_SSO_ZITADEL_BASE";
pub const BASE_URL_ENV: &str = "BASE_URL"; pub const BASE_URL_ENV: &str = "BASE_URL";
pub const CLIENT_ID_ENV: &str = "FLEET_AUTH_CLIENT_ID"; pub const CLIENT_ID_ENV: &str = "HARMONY_SSO_CLIENT_ID";
pub const SCOPE_ENV: &str = "FLEET_AUTH_SCOPE"; pub const SCOPE_ENV: &str = "HARMONY_SSO_SCOPE";
pub const TRUSTED_AUDIENCES_ENV: &str = "FLEET_AUTH_TRUSTED_AUDIENCES"; pub const TRUSTED_AUDIENCES_ENV: &str = "HARMONY_SSO_TRUSTED_AUDIENCES";
pub const LOGOUT_REDIRECT_URI_ENV: &str = "FLEET_AUTH_LOGOUT_REDIRECT_URI"; pub const LOGOUT_REDIRECT_URI_ENV: &str = "HARMONY_SSO_LOGOUT_REDIRECT_URI";
pub const COOKIE_KEY_ENV: &str = "FLEET_OPERATOR_COOKIE_KEY_B64"; pub const COOKIE_KEY_ENV: &str = "HARMONY_COOKIE_KEY_B64";
pub fn config_from_env() -> ZitadelAuthConfig { pub fn config_from_env() -> ZitadelAuthConfig {
ZitadelAuthConfig { ZitadelAuthConfig {

View File

@@ -15,9 +15,9 @@ pub use config::{
pub use jwks::JwksCache; pub use jwks::JwksCache;
pub use login::{ pub use login::{
AuthCallbackQuery, LoginAttempt, RawAuthCallbackQuery, TokenResponse, ValidatedUser, AuthCallbackQuery, LoginAttempt, LoginQuery, RawAuthCallbackQuery, TokenResponse, ValidatedUser,
build_login_attempt, build_logout_url, exchange_code_for_token, jwt_exp, build_login_attempt, build_logout_url, exchange_code_for_token, jwt_exp,
validate_callback_state, validate_id_token, valid_next, validate_callback_state, validate_id_token,
}; };
pub use session::{LoginAttemptCookie, VerifiedSession}; pub use session::{LoginAttemptCookie, VerifiedSession};

View File

@@ -37,6 +37,11 @@ pub struct TokenResponse {
pub expires_in: Option<u64>, pub expires_in: Option<u64>,
} }
#[derive(Debug, Deserialize)]
pub struct LoginQuery {
pub next: Option<String>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct RawAuthCallbackQuery { pub struct RawAuthCallbackQuery {
pub code: Option<String>, pub code: Option<String>,
@@ -63,10 +68,15 @@ impl From<&LoginAttempt> for LoginAttemptCookie {
state: attempt.state.clone(), state: attempt.state.clone(),
pkce_code_verifier: attempt.pkce_code_verifier.clone(), pkce_code_verifier: attempt.pkce_code_verifier.clone(),
nonce: attempt.nonce.clone(), nonce: attempt.nonce.clone(),
next: None,
} }
} }
} }
pub fn valid_next(next: &str) -> bool {
next.starts_with('/') && !next.starts_with("//") && !next.chars().any(char::is_control)
}
impl TryFrom<RawAuthCallbackQuery> for AuthCallbackQuery { impl TryFrom<RawAuthCallbackQuery> for AuthCallbackQuery {
type Error = anyhow::Error; type Error = anyhow::Error;

View File

@@ -18,4 +18,5 @@ pub struct LoginAttemptCookie {
pub state: String, pub state: String,
pub pkce_code_verifier: String, pub pkce_code_verifier: String,
pub nonce: String, pub nonce: String,
pub next: Option<String>,
} }