Skip to content

Commit

Permalink
Add more paths for OIDC discovery attempts (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunklas authored Oct 25, 2024
1 parent c3958c4 commit 3a8dbe3
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 71 deletions.
48 changes: 1 addition & 47 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/axum-example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ async fn main() {
oidc_provider_host, oidc_provider_port
))
.build()
.await
.expect("Failed to build OAuth2ResourceServer");

let app = Router::new()
Expand Down
1 change: 1 addition & 0 deletions examples/salvo-example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ async fn main() {
oidc_provider_host, oidc_provider_port
))
.build()
.await
.expect("Failed to build OAuth2ResourceServer");

let router = Router::new()
Expand Down
1 change: 1 addition & 0 deletions examples/tonic-example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
oidc_provider_host, oidc_provider_port
))
.build()
.await
.expect("Failed to build OAuth2ResourceServer");

let addr = "[::1]:50051".parse()?;
Expand Down
2 changes: 1 addition & 1 deletion tower-oauth2-resource-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ http = "1.1.0"
jsonwebtoken = "9.3.0"
log = { workspace = true }
pin-project = "1.1.5"
reqwest = { version = "0.12.8", features = ["json"] }
serde = "1.0.210"
serde_with = "3.9.0"
tokio = { workspace = true, features = ["rt", "sync", "time"] }
tower = { workspace = true }
ureq = { version = "2.9.7", features = ["json"] }

[dev-dependencies]
base64 = "0.22.1"
Expand Down
7 changes: 4 additions & 3 deletions tower-oauth2-resource-server/src/jwks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ impl DecodingKeysProvider for JwksDecodingKeysProvider {
}

async fn fetch_jwks(jwks_uri: &str) -> Result<JwkSet, JwkError> {
let response = ureq::get(jwks_uri)
.call()
let response = reqwest::get(jwks_uri)
.await
.map_err(|_| JwkError::FetchFailed)?;
let parsed = response
.into_json::<JwkSet>()
.json::<JwkSet>()
.await
.map_err(|_| JwkError::ParseFailed)?;
Ok(parsed)
}
Expand Down
22 changes: 16 additions & 6 deletions tower-oauth2-resource-server/src/oidc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,21 @@ pub(crate) struct OidcConfigProvider {
}

impl OidcConfigProvider {
pub fn from_issuer_uri(issuer_uri: &str) -> Result<Self, Box<dyn Error>> {
Ok(OidcConfigProvider {
config: ureq::get(&format!("{}/.well-known/openid-configuration", issuer_uri))
.call()?
.into_json::<OidcConfig>()?,
})
pub async fn from_issuer_uri(issuer_uri: &str) -> Result<Self, Box<dyn Error>> {
let paths = vec![
"/.well-known/openid-configuration",
"/.well-known/openid-configuration/issuer",
"/.well-known/oauth-authorization-server/issuer",
];
for path in paths {
if let Ok(response) = reqwest::get(format!("{}{}", issuer_uri, path)).await {
if let Ok(oidc_config) = response.json().await {
return Ok(OidcConfigProvider {
config: oidc_config,
});
}
}
}
Err("Failed to fetch OIDC configuration".into())
}
}
9 changes: 6 additions & 3 deletions tower-oauth2-resource-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ where
OAuth2ResourceServerBuilder::new()
}

pub(crate) fn new(
pub(crate) async fn new(
issuer_uri: String,
audiences: Vec<String>,
jwk_set_refresh_interval: Duration,
claims_validation_spec: Option<ClaimsValidationSpec>,
) -> Result<OAuth2ResourceServer<Claims>, Box<dyn Error>> {
let config = OidcConfigProvider::from_issuer_uri(&issuer_uri)?.config;
let config = OidcConfigProvider::from_issuer_uri(&issuer_uri)
.await?
.config;
info!(
"Successfully fetched oidc config for issuer: {:?}",
&issuer_uri
Expand Down Expand Up @@ -137,7 +139,7 @@ where
self
}

pub fn build(self) -> Result<OAuth2ResourceServer<Claims>, Box<dyn Error>> {
pub async fn build(self) -> Result<OAuth2ResourceServer<Claims>, Box<dyn Error>> {
let issuer_uri = self
.issuer_uri
.ok_or(InvalidParametersError::new("issuer_uri is required"))?;
Expand All @@ -147,6 +149,7 @@ where
self.jwk_set_refresh_interval,
self.claims_validation_spec,
)
.await
}
}

Expand Down
29 changes: 18 additions & 11 deletions tower-oauth2-resource-server/tests/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async fn unauthorized_on_missing_authorization() {
let mock_server = MockServer::start().await;
mock_oidc_config(&mock_server, "").await;
let mut service = ServiceBuilder::new()
.layer(default_auth_layer(&mock_server, Vec::new()))
.layer(default_auth_layer(&mock_server, Vec::new()).await)
.service_fn(echo);

let request = request_with_headers(Vec::new());
Expand All @@ -40,7 +40,7 @@ async fn unauthorized_on_invalid_authorization() {
let mock_server = MockServer::start().await;
mock_oidc_config(&mock_server, "").await;
let mut service = ServiceBuilder::new()
.layer(default_auth_layer(&mock_server, Vec::new()))
.layer(default_auth_layer(&mock_server, Vec::new()).await)
.service_fn(echo);

let request = request_with_headers(vec![(AUTHORIZATION, "NotAJWT")]);
Expand All @@ -63,10 +63,13 @@ async fn unauthorized_on_token_validation_failure() {
mock_oidc_config(&mock_server, "https://auth-server.com").await;
mock_jwks(&mock_server, [("good_key".to_owned(), public_key)].to_vec()).await;
let mut service = ServiceBuilder::new()
.layer(default_auth_layer(
&mock_server,
["https://some-resource-server.com".to_owned()].to_vec(),
))
.layer(
default_auth_layer(
&mock_server,
["https://some-resource-server.com".to_owned()].to_vec(),
)
.await,
)
.service_fn(echo);

let token = jwt_from(
Expand All @@ -93,10 +96,13 @@ async fn ok() {
mock_oidc_config(&mock_server, "https://auth-server.com").await;
mock_jwks(&mock_server, [("good_key".to_owned(), public_key)].to_vec()).await;
let mut service = ServiceBuilder::new()
.layer(default_auth_layer(
&mock_server,
["https://some-resource-server.com".to_owned()].to_vec(),
))
.layer(
default_auth_layer(
&mock_server,
["https://some-resource-server.com".to_owned()].to_vec(),
)
.await,
)
.service_fn(echo);
// Needed for initial jwks fetch
sleep(Duration::from_millis(100)).await;
Expand All @@ -118,14 +124,15 @@ async fn ok() {
assert_eq!(response.status(), StatusCode::OK);
}

fn default_auth_layer(
async fn default_auth_layer(
mock_server: &MockServer,
audiences: Vec<String>,
) -> OAuth2ResourceServerLayer<DefaultClaims> {
<OAuth2ResourceServer>::builder()
.issuer_uri(&mock_server.uri())
.audiences(audiences)
.build()
.await
.expect("Failed to build OAuth2ResourceServer")
.into_layer()
}
Expand Down

0 comments on commit 3a8dbe3

Please sign in to comment.