diff --git a/baffao-proxy/config/example.toml b/baffao-proxy/config/example.toml index bf108ea..ec2cf17 100644 --- a/baffao-proxy/config/example.toml +++ b/baffao-proxy/config/example.toml @@ -6,5 +6,6 @@ client_id = "client_id" client_secret = "client_secret" authorization_redirect_uri = "http://127.0.0.1:3000/oauth/callback" authorization_endpoint = "http://127.0.0.1:4444/oauth2/auth" -token_endpoint = "http://127.0.0.1/oauth2/token" +token_endpoint = "http://127.0.0.1:3000/oauth2/token" +introspection_endpoint = "http://127.0.0.1:3000/oauth2/introspect" redirect_uri = "http://127.0.0.1:3000/" diff --git a/baffao-proxy/src/main.rs b/baffao-proxy/src/main.rs index 10d6a8c..7989b66 100644 --- a/baffao-proxy/src/main.rs +++ b/baffao-proxy/src/main.rs @@ -46,6 +46,7 @@ async fn main() { let app = Router::new() .route("/oauth/authorize", get(oauth::authorize)) .route("/oauth/callback", get(oauth::callback)) + .route("/oauth/introspect", get(oauth::introspect)) .route("/session", get(session::get_session)) .fallback(any(proxy::handler)) .layer( diff --git a/baffao-proxy/src/oauth.rs b/baffao-proxy/src/oauth.rs index 2dd0902..6b7f272 100644 --- a/baffao-proxy/src/oauth.rs +++ b/baffao-proxy/src/oauth.rs @@ -31,3 +31,10 @@ pub async fn callback( (updated_jar, Redirect::temporary(&url.to_string())) } + +pub async fn introspect( + jar: CookieJar, + State(handler): State, +) -> impl IntoResponse { + handler.introspect(jar).await; +} diff --git a/baffao/src/handlers/introspect.rs b/baffao/src/handlers/introspect.rs new file mode 100644 index 0000000..f76fe66 --- /dev/null +++ b/baffao/src/handlers/introspect.rs @@ -0,0 +1,13 @@ +use axum_extra::extract::CookieJar; +use reqwest::StatusCode; + +use crate::oauth::{IntrospectionTokenResponse, OAuthHttpHandler}; + +pub async fn oauth2_introspect( + handler: OAuthHttpHandler, + jar: CookieJar, +) -> (CookieJar, StatusCode, Option) { + let (updated_jar, response) = handler.introspect(jar).await; + + (updated_jar, response.to_owned().map_or_else(|| StatusCode::UNAUTHORIZED, |_| StatusCode::OK), response) +} diff --git a/baffao/src/handlers/mod.rs b/baffao/src/handlers/mod.rs index d06fb20..24c45f5 100644 --- a/baffao/src/handlers/mod.rs +++ b/baffao/src/handlers/mod.rs @@ -1,9 +1,11 @@ pub use authorize::{oauth2_authorize, AuthorizationQuery}; pub use callback::{oauth2_callback, AuthorizationCallbackQuery}; pub use get_session::get_session_from_cookie; +pub use introspect::oauth2_introspect; pub use proxy::proxy; mod authorize; mod callback; mod get_session; +mod introspect; mod proxy; diff --git a/baffao/src/oauth/client.rs b/baffao/src/oauth/client.rs index 71f1976..a6f3b58 100644 --- a/baffao/src/oauth/client.rs +++ b/baffao/src/oauth/client.rs @@ -1,12 +1,10 @@ use anyhow::{Context, Error}; use oauth2::{ - basic::BasicClient, reqwest::async_http_client, AuthType, AuthUrl, AuthorizationCode, ClientId, - ClientSecret, CsrfToken, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, - TokenUrl, + basic::BasicClient, reqwest::async_http_client, AccessToken as OAuthAccessToken, AuthType, AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IntrospectionUrl, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenUrl }; use reqwest::Url; -use super::{AccessToken, OAuthConfig}; +use super::{AccessToken, IntrospectionTokenResponse, OAuthConfig}; pub struct OAuthClient { config: OAuthConfig, @@ -31,7 +29,7 @@ impl OAuthClient { let token_endpoint = TokenUrl::new(config.token_endpoint.clone()).context("Failed to parse token url")?; - let client = BasicClient::new( + let mut client = BasicClient::new( ClientId::new(config.client_id.clone()), Some(ClientSecret::new(config.client_secret.clone())), auth_url, @@ -40,6 +38,12 @@ impl OAuthClient { .set_auth_type(AuthType::RequestBody) .set_redirect_uri(redirect_uri); + if let Some(introspection_endpoint) = &config.introspection_endpoint { + let introspection_endpoint = IntrospectionUrl::new(introspection_endpoint.clone()) + .context("Failed to parse introspection url")?; + client = client.set_introspection_uri(introspection_endpoint); + } + Ok(Self { config, client }) } @@ -105,4 +109,18 @@ impl OAuthClient { Ok(response.unwrap()) } + + pub async fn introspect_token( + &self, + token: String, + ) -> Result + { + let response = self + .client + .introspect(&OAuthAccessToken::new(token))? + .request_async(async_http_client) + .await?; + + Ok(response) + } } diff --git a/baffao/src/oauth/http.rs b/baffao/src/oauth/http.rs index 5a7b3c2..9d3f588 100644 --- a/baffao/src/oauth/http.rs +++ b/baffao/src/oauth/http.rs @@ -3,7 +3,7 @@ use axum_extra::extract::CookieJar; use chrono::{Duration, Utc}; use oauth2::TokenResponse; -use super::OAuthConfig; +use super::{IntrospectionTokenResponse, OAuthConfig}; use crate::cookies::new_cookie; use crate::session::{update_session, Session}; use crate::{oauth::OAuthClient, settings::CookiesConfig}; @@ -130,4 +130,14 @@ impl OAuthHttpHandler { cookies_config: self.cookies_config.clone(), } } + + pub async fn introspect(&self, jar: CookieJar) -> (CookieJar, Option) { + let (updated_jar, access_token) = self.get_or_refresh_token(jar).await.unwrap(); + if access_token.is_none() { + return (updated_jar, None) + } + + let result = self.client.introspect_token(access_token.unwrap().to_string()).await.unwrap(); + (updated_jar, Some(result)) + } } diff --git a/baffao/src/oauth/mod.rs b/baffao/src/oauth/mod.rs index d2ce4d9..3c1aaaf 100644 --- a/baffao/src/oauth/mod.rs +++ b/baffao/src/oauth/mod.rs @@ -4,7 +4,7 @@ pub use http::OAuthHttpHandler; mod client; mod http; -use oauth2::{basic::BasicTokenType, EmptyExtraTokenFields, StandardTokenResponse}; +use oauth2::{basic::BasicTokenType, EmptyExtraTokenFields, StandardTokenIntrospectionResponse, StandardTokenResponse}; use serde::Deserialize; @@ -22,8 +22,10 @@ pub struct OAuthConfig { pub authorization_redirect_uri: String, pub authorization_endpoint: String, pub token_endpoint: String, + pub introspection_endpoint: Option, pub redirect_uri: Option, pub default_scopes: Option>, } pub type AccessToken = StandardTokenResponse; +pub type IntrospectionTokenResponse = StandardTokenIntrospectionResponse;