Skip to content

Commit

Permalink
Suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Juliano1612 committed Dec 19, 2023
1 parent f8b2e0e commit fab8469
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ base64 = "0.21.4"
serde_urlencoded = "0.7.1"

[dev-dependencies]
assert-json-diff = "2.0.2"
did-jwk = "0.1.0"
tokio = { version = "1.25.0", features = ["macros"] }
151 changes: 111 additions & 40 deletions src/pushed_authorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use oauth2::{
header::{ACCEPT, CONTENT_TYPE},
HeaderValue, Method, StatusCode,
},
AuthUrl, ClientId, CsrfToken, HttpRequest, HttpResponse, PkceCodeChallenge, RedirectUrl,
AuthUrl, ClientId, CsrfToken, HttpRequest, HttpResponse, PkceCodeChallenge,
PkceCodeChallengeMethod, RedirectUrl,
};
use openidconnect::{core::CoreErrorResponseType, IssuerUrl, Nonce, StandardErrorResponse};
use serde::{Deserialize, Serialize};
Expand All @@ -21,10 +22,14 @@ use serde::{Deserialize, Serialize};
pub struct ParRequestUri(pub String);

impl ParRequestUri {
pub fn new_random() -> Self {
pub fn new(nonce: Option<Nonce>) -> Self {
Self(format!(
"urn:ietf:params:oauth:request_uri:{:}",
Nonce::new_random().secret().clone()
if let Some(n) = nonce {
n.secret().clone()
} else {
Nonce::new_random().secret().clone()
}
))
}

Expand All @@ -40,7 +45,7 @@ pub struct ParAuthParams {
client_id: ClientId,
state: CsrfToken,
code_challenge: String,
code_challenge_method: String,
code_challenge_method: PkceCodeChallengeMethod,
redirect_uri: RedirectUrl,
#[serde(skip_serializing_if = "Option::is_none")]
response_type: Option<String>,
Expand All @@ -64,7 +69,7 @@ impl ParAuthParams {
set_client_id -> client_id[ClientId],
set_state -> state[CsrfToken],
set_code_challenge -> code_challenge[String],
set_code_challenge_method -> code_challenge_method[String],
set_code_challenge_method -> code_challenge_method[PkceCodeChallengeMethod],
set_redirect_uri -> redirect_uri[RedirectUrl],
set_response_type -> response_type[Option<String>],
set_client_assertion -> client_assertion[Option<String>],
Expand Down Expand Up @@ -131,38 +136,10 @@ where
F: Future<Output = Result<HttpResponse, RE>>,
RE: std::error::Error + 'static,
{
let (url, token) = self.inner.url();

let query = serde_urlencoded::from_str::<ParAuthParams>(url.clone().as_str())
.unwrap()
.set_client_assertion_type(client_assertion_type.clone())
.set_client_assertion(client_assertion.clone())
.set_authorization_details(Some(
serde_json::to_string::<Vec<AuthorizationDetail<AD>>>(&self.authorization_details)
.unwrap(),
))
.set_wallet_issuer(self.wallet_issuer)
.set_user_hint(self.user_hint)
.set_issuer_state(self.issuer_state);
let mut auth_url = self.auth_url.url().clone();

let http_request = HttpRequest {
url: self.par_auth_url.url().clone(),
method: Method::POST,
headers: vec![
(
CONTENT_TYPE,
HeaderValue::from_static(MIME_TYPE_FORM_URLENCODED),
),
(ACCEPT, HeaderValue::from_static(MIME_TYPE_JSON)),
]
.into_iter()
.collect(),
body: serde_json::to_vec(&format!(
"&{:}&", // had to add & before and after to fix parsing during the request
serde_urlencoded::to_string(&query).unwrap()
))
.map_err(|e| RequestError::Other(e.to_string()))?,
};
let (http_request, req_body, token) =
self.prepare_request(client_assertion, client_assertion_type)?;

let http_response = http_client(http_request)
.await
Expand Down Expand Up @@ -195,19 +172,63 @@ where
)),
}?;

let mut auth_url = self.auth_url.url().clone();

auth_url
.query_pairs_mut()
.append_pair("request_uri", &parsed_response.request_uri.0);
.append_pair("request_uri", parsed_response.request_uri.get());

auth_url
.query_pairs_mut()
.append_pair("client_id", &query.client_id.to_string());
.append_pair("client_id", &req_body.client_id.to_string());

Ok((auth_url, token))
}

pub fn prepare_request<RE>(
self,
client_assertion: Option<String>,
client_assertion_type: Option<String>,
) -> Result<(HttpRequest, ParAuthParams, CsrfToken), RequestError<RE>>
where
RE: std::error::Error + 'static,
{
let (url, token) = self.inner.url();

let body = serde_urlencoded::from_str::<ParAuthParams>(url.clone().as_str())
.map_err(|_| RequestError::Other("failed parsing url".to_string()))?
.set_client_assertion_type(client_assertion_type.clone())
.set_client_assertion(client_assertion.clone())
.set_authorization_details(Some(
serde_json::to_string::<Vec<AuthorizationDetail<AD>>>(&self.authorization_details)
.unwrap_or("[]".to_string()),
))
.set_wallet_issuer(self.wallet_issuer)
.set_user_hint(self.user_hint)
.set_issuer_state(self.issuer_state);

Ok((
HttpRequest {
url: self.par_auth_url.url().clone(),
method: Method::POST,
headers: vec![
(
CONTENT_TYPE,
HeaderValue::from_static(MIME_TYPE_FORM_URLENCODED),
),
(ACCEPT, HeaderValue::from_static(MIME_TYPE_JSON)),
]
.into_iter()
.collect(),
body: serde_json::to_vec(&format!(
"&{:}&", // had to add & before and after to fix parsing during the request
serde_urlencoded::to_string(&body).unwrap_or("".to_string())
))
.map_err(|e| RequestError::Other(e.to_string()))?,
},
body,
token,
))
}

pub fn set_pkce_challenge(mut self, pkce_code_challenge: PkceCodeChallenge) -> Self {
self.inner = self.inner.set_pkce_challenge(pkce_code_challenge);
self
Expand All @@ -220,3 +241,53 @@ where
self
}
}

#[cfg(test)]
mod test {
use assert_json_diff::assert_json_eq;
use oauth2::{AuthUrl, ClientId, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, TokenUrl};
use serde_json::json;

use crate::{
core::profiles::CoreProfilesAuthorizationDetails, metadata::CredentialUrl,
openidconnect::reqwest::HttpClientError,
};

use super::*;

#[test]
fn example_pushed_authorization_request() {
let expected_body = json!({
"client_id": "s6BhdRkqt3",
"state": "state",
"code_challenge": "MYdqq2Vt_ZLMAWpXXsjGIrlxrCF2e4ZP4SxDf7cm_tg",
"code_challenge_method": "S256",
"redirect_uri": "https://client.example.org/cb",

"authorization_details": "[]",
});

let client = crate::core::client::Client::new(
ClientId::new("s6BhdRkqt3".to_string()),
IssuerUrl::new("https://server.example.com".into()).unwrap(),
CredentialUrl::new("https://server.example.com/credential".into()).unwrap(),
AuthUrl::new("https://server.example.com/authorize".into()).unwrap(),
Some(ParUrl::new("https://server.example.com/as/par".into()).unwrap()),
TokenUrl::new("https://server.example.com/token".into()).unwrap(),
RedirectUrl::new("https://client.example.org/cb".into()).unwrap(),
);

let pkce_verifier =
PkceCodeVerifier::new("challengechallengechallengechallengechallenge".into());
let pkce_challenge = PkceCodeChallenge::from_code_verifier_sha256(&pkce_verifier);
let state = CsrfToken::new("state".into());

let (_, body, _) = client
.pushed_authorization_request::<_, CoreProfilesAuthorizationDetails>(move || state)
.unwrap()
.set_pkce_challenge(pkce_challenge)
.prepare_request::<HttpClientError>(None, None)
.unwrap();
assert_json_eq!(expected_body, body);
}
}

0 comments on commit fab8469

Please sign in to comment.