Skip to content

Commit

Permalink
Include nonce in OAuth code request
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Oct 7, 2024
1 parent f74e638 commit 4f17dd6
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 50 deletions.
45 changes: 29 additions & 16 deletions src/core/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::time::Duration;

use ahash::AHashSet;
use leptos::{expect_context, RwSignal};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::{Deserialize, Serialize};

use crate::components::messages::alert::Alert;
Expand All @@ -23,6 +24,9 @@ pub enum OAuthCodeRequest {
Code {
client_id: String,
redirect_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
nonce: Option<String>,
},
Device {
code: String,
Expand Down Expand Up @@ -103,12 +107,27 @@ pub async fn oauth_authenticate(
username: &str,
password: &str,
) -> AuthenticationResult<AuthenticationResponse> {
let response =
match oauth_user_authentication(base_url, username, password, "webadmin", REDIRECT_URI.into()).await {
AuthenticationResult::Success(response) => response,
AuthenticationResult::TotpRequired => return AuthenticationResult::TotpRequired,
AuthenticationResult::Error(err) => return AuthenticationResult::Error(err),
};
let response = match oauth_user_authentication(
base_url,
username,
password,
&OAuthCodeRequest::Code {
client_id: "webadmin".to_string(),
redirect_uri: REDIRECT_URI.to_string().into(),
nonce: thread_rng()
.sample_iter(Alphanumeric)
.take(10)
.map(char::from)
.collect::<String>()
.into(),
},
)
.await
{
AuthenticationResult::Success(response) => response,
AuthenticationResult::TotpRequired => return AuthenticationResult::TotpRequired,
AuthenticationResult::Error(err) => return AuthenticationResult::Error(err),
};
let permissions = response.permissions;
let is_enterprise = response.is_enterprise;
match HttpRequest::post(format!("{base_url}/auth/token"))
Expand Down Expand Up @@ -145,15 +164,11 @@ pub async fn oauth_user_authentication(
base_url: &str,
username: &str,
password: &str,
client_id: &str,
redirect_uri: Option<&str>,
request: &OAuthCodeRequest,
) -> AuthenticationResult<OAuthCodeResponse> {
match HttpRequest::post(format!("{base_url}/api/oauth"))
.with_basic_authorization(username, password)
.with_body(OAuthCodeRequest::Code {
client_id: client_id.to_string(),
redirect_uri: redirect_uri.map(ToOwned::to_owned),
})
.with_body(request)
.unwrap()
.send::<OAuthCodeResponse>()
.await
Expand All @@ -177,13 +192,11 @@ pub async fn oauth_device_authentication(
base_url: &str,
username: &str,
password: &str,
code: &str,
request: &OAuthCodeRequest,
) -> AuthenticationResult<bool> {
match HttpRequest::post(format!("{base_url}/api/oauth"))
.with_basic_authorization(username, password)
.with_body(OAuthCodeRequest::Device {
code: code.to_string(),
})
.with_body(request)
.unwrap()
.send::<bool>()
.await
Expand Down
61 changes: 30 additions & 31 deletions src/pages/authorize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn Authorize() -> impl IntoView {
create_memo(move |_| params.get().get("type").map_or(true, |t| t != "code"));
let redirect_uri = create_memo(move |_| query.get().get("redirect_uri").cloned());
let client_id = create_memo(move |_| query.get().get("client_id").cloned());
let nonce = create_memo(move |_| query.get().get("nonce").cloned());
let show_totp = create_rw_signal(false);

let login_action = create_action(
Expand All @@ -48,17 +49,17 @@ pub fn Authorize() -> impl IntoView {
let state = query.get().get("state").cloned();

async move {

match &request {
OAuthCodeRequest::Code {
client_id,
redirect_uri,
..
} => {
match oauth_user_authentication(
BASE_URL,
&username,
&password,
client_id,
redirect_uri.as_deref(),
&request,
)
.await
{
Expand Down Expand Up @@ -90,9 +91,9 @@ pub fn Authorize() -> impl IntoView {
}
}
}
OAuthCodeRequest::Device { code } => {
OAuthCodeRequest::Device { .. } => {
let message =
match oauth_device_authentication(BASE_URL, &username, &password, code)
match oauth_device_authentication(BASE_URL, &username, &password, &request)
.await
{
AuthenticationResult::Success(true) => {
Expand Down Expand Up @@ -193,33 +194,30 @@ pub fn Authorize() -> impl IntoView {
type="submit"
class="w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-gray-600"
on:click=move |_| {
let (redirect_uri, client_id) = if !is_device_auth.get() {
let redirect_uri = redirect_uri.get();
match &redirect_uri {
Some(redirect_uri) if redirect_uri.starts_with("http:") => {
alert
.set(
Alert::error(
"Invalid redirect_uri parameter, must be a valid HTTPS URL",
),
);
return;
}
Some(_) => {}
None => {
alert
.set(
Alert::error("Missing redirect_uri in query parameters"),
);
return;
}
let is_auth_flow = !is_device_auth.get();
let redirect_uri = match redirect_uri.get() {
Some(redirect_uri) if redirect_uri.starts_with("http:") => {
alert
.set(
Alert::error(
"Invalid redirect_uri parameter, must be a valid HTTPS URL",
),
);
return;
}
None if is_auth_flow => {
alert
.set(
Alert::error("Missing redirect_uri in query parameters"),
);
return;
}
(redirect_uri, client_id.get().unwrap_or_default().into())
} else {
(None, None)
redirect_uri => redirect_uri,
};
let client_id = client_id.get();
let nonce = nonce.get();
data.update(|data| {
if client_id.is_some() {
if is_auth_flow {
data.set("code", "none");
}
if data.validate_form() {
Expand All @@ -233,10 +231,11 @@ pub fn Authorize() -> impl IntoView {
(password, Some(totp)) => format!("{}${}", password, totp),
(password, None) => password,
};
let request = if let Some(client_id) = client_id {
let request = if is_auth_flow {
OAuthCodeRequest::Code {
client_id,
client_id: client_id.unwrap_or_default(),
redirect_uri,
nonce,
}
} else {
OAuthCodeRequest::Device {
Expand Down
5 changes: 2 additions & 3 deletions src/pages/directory/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,8 @@ pub fn PrincipalEdit() -> impl IntoView {
.take(30)
.map(char::from)
.collect::<String>()]);
principal.enabled_permissions = PrincipalValue::StringList(vec![
"authenticate".to_string(),
]);
principal.enabled_permissions =
PrincipalValue::StringList(vec!["authenticate".to_string()]);
}
_ => {}
}
Expand Down

0 comments on commit 4f17dd6

Please sign in to comment.