Skip to content

Commit

Permalink
Don't send Authorization headers to third-party services. (#562)
Browse files Browse the repository at this point in the history
  • Loading branch information
korran committed Feb 6, 2024
1 parent 9a31198 commit 3ce474a
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 27 deletions.
58 changes: 31 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ use crate::service::body::BodyStreamExt;

use chrono::{DateTime, Utc};
use http::{HeaderMap, HeaderValue, Method, Uri};
use service::middleware::auth_header::AuthHeaderLayer;
use std::convert::{Infallible, TryInto};
use std::fmt;
use std::io::Write;
Expand Down Expand Up @@ -685,37 +686,32 @@ impl OctocrabBuilder<NoSvc, DefaultOctocrabBuilderConfig, NoAuth, NotLayerReady>
));
}

let auth_state = match self.config.auth {
Auth::None => AuthState::None,
Auth::Basic { username, password } => AuthState::BasicAuth { username, password },
Auth::PersonalToken(token) => {
hmap.push((
http::header::AUTHORIZATION,
format!("Bearer {}", token.expose_secret()).parse().unwrap(),
));
AuthState::None
let (auth_header, auth_state): (Option<HeaderValue>, _) = match self.config.auth {
Auth::None => (None, AuthState::None),
Auth::Basic { username, password } => {
(None, AuthState::BasicAuth { username, password })
}
Auth::UserAccessToken(token) => {
hmap.push((
http::header::AUTHORIZATION,
format!("Bearer {}", token.expose_secret()).parse().unwrap(),
));
AuthState::None
}
Auth::App(app_auth) => AuthState::App(app_auth),
Auth::OAuth(device) => {
hmap.push((
http::header::AUTHORIZATION,
Auth::PersonalToken(token) => (
Some(format!("Bearer {}", token.expose_secret()).parse().unwrap()),
AuthState::None,
),
Auth::UserAccessToken(token) => (
Some(format!("Bearer {}", token.expose_secret()).parse().unwrap()),
AuthState::None,
),
Auth::App(app_auth) => (None, AuthState::App(app_auth)),
Auth::OAuth(device) => (
Some(
format!(
"{} {}",
device.token_type,
&device.access_token.expose_secret()
)
.parse()
.unwrap(),
));
AuthState::None
}
),
AuthState::None,
),
};

for (key, value) in self.config.extra_headers.iter() {
Expand All @@ -742,6 +738,8 @@ impl OctocrabBuilder<NoSvc, DefaultOctocrabBuilderConfig, NoAuth, NotLayerReady>

let client = BaseUriLayer::new(uri).layer(client);

let client = AuthHeaderLayer::new(auth_header).layer(client);

Ok(Octocrab::new(client, auth_state))
}
}
Expand Down Expand Up @@ -1515,10 +1513,16 @@ impl Octocrab {
};

if let Some(mut auth_header) = auth_header {
auth_header.set_sensitive(true);
parts
.headers
.insert(http::header::AUTHORIZATION, auth_header);
// Only set the auth_header if the authority (host) is empty (destined for
// GitHub). Otherwise, leave it off as we could have been redirected
// away from GitHub (via follow_location_to_data()), and we don't
// want to give our credentials to third-party services.
if parts.uri.authority().is_none() {
auth_header.set_sensitive(true);
parts
.headers
.insert(http::header::AUTHORIZATION, auth_header);
}
}

let request = http::Request::from_parts(parts, body);
Expand Down
65 changes: 65 additions & 0 deletions src/service/middleware/auth_header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::sync::Arc;

use http::{header::AUTHORIZATION, request::Request, HeaderValue};
use tower::{Layer, Service};

#[derive(Clone)]
/// Layer that adds the authentication header to github-bound requests
pub struct AuthHeaderLayer {
pub(crate) auth_header: Arc<Option<HeaderValue>>,
}

impl AuthHeaderLayer {
pub fn new(auth_header: Option<HeaderValue>) -> Self {
AuthHeaderLayer {
auth_header: Arc::new(auth_header),
}
}
}

impl<S> Layer<S> for AuthHeaderLayer {
type Service = AuthHeader<S>;

fn layer(&self, inner: S) -> Self::Service {
AuthHeader {
inner,
auth_header: self.auth_header.clone(),
}
}
}

#[derive(Clone)]
/// Service that adds a static set of extra headers to each request
pub struct AuthHeader<S> {
inner: S,
pub(crate) auth_header: Arc<Option<HeaderValue>>,
}

impl<S, ReqBody> Service<Request<ReqBody>> for AuthHeader<S>
where
S: Service<Request<ReqBody>>,
{
type Error = S::Error;
type Future = S::Future;
type Response = S::Response;

fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}

fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
// Only set the auth_header if the authority (host) is empty (destined for
// GitHub). Otherwise, leave it off as we could have been redirected
// away from GitHub (via follow_location_to_data()), and we don't
// want to give our credentials to third-party services.
if req.uri().authority().is_none() {
if let Some(auth_header) = &*self.auth_header {
req.headers_mut().append(AUTHORIZATION, auth_header.clone());
}
}
self.inner.call(req)
}
}
1 change: 1 addition & 0 deletions src/service/middleware/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod auth_header;
pub mod base_uri;
pub mod extra_headers;
#[cfg(feature = "retry")]
Expand Down

0 comments on commit 3ce474a

Please sign in to comment.