From 3605650a90c0710aeee93930ba2622bf00a88b70 Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Wed, 23 Mar 2022 08:22:38 -0700 Subject: Add support for token extensions --- src/lib.rs | 106 +++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 38 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 670a397..2584ab2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,106 +12,128 @@ //! //! # Example //! ``` +//! # use std::future::Future; //! # use actix_web::{ get, HttpResponse, HttpServer, Responder }; -//! # use actix_middleware_rfc7662::{AnyScope, RequireAuthorization, RequireAuthorizationConfig}; +//! # use actix_middleware_rfc7662::{AnyScope, RequireAuthorization, RequireAuthorizationConfig, StandardToken}; //! //! #[get("/protected/api")] //! async fn handle_read(_auth: RequireAuthorization) -> impl Responder { //! HttpResponse::Ok().body("Success!\n") //! } //! -//! #[actix_web::main] -//! async fn main() -> std::io::Result<()> { -//! let oauth_config = RequireAuthorizationConfig::new( +//! fn setup_server() -> std::io::Result { +//! let oauth_config = RequireAuthorizationConfig::::new( //! "client_id".to_string(), //! Some("client_secret".to_string()), //! "https://example.com/oauth/authorize".parse().expect("invalid url"), //! "https://example.com/oauth/introspect".parse().expect("invalid url"), //! ); //! -//! HttpServer::new(move || { +//! Ok(HttpServer::new(move || { //! actix_web::App::new() //! .app_data(oauth_config.clone()) //! .service(handle_read) -//! .service(handle_write) //! }) //! .bind("127.0.0.1:8182".to_string())? -//! .run() -//! .await +//! .run()) //! } //! ``` use actix_web::{dev, FromRequest, HttpRequest}; use futures_util::future::LocalBoxFuture; -use oauth2::basic::{BasicErrorResponseType, BasicTokenType}; +use oauth2::basic::BasicErrorResponseType; use oauth2::url::Url; -use oauth2::*; +use oauth2::{ + reqwest, AccessToken, AuthUrl, ClientId, ClientSecret, IntrospectionUrl, StandardErrorResponse, + StandardRevocableToken, StandardTokenResponse, TokenIntrospectionResponse, +}; use std::future::ready; use std::marker::PhantomData; use std::sync::Arc; +// Re-exports +pub use oauth2::{ + basic::BasicTokenType, EmptyExtraTokenFields as StandardToken, ExtraTokenFields, + StandardTokenIntrospectionResponse, +}; + mod error; -use error::Error; + +#[cfg(feature = "indieauth")] +pub mod indieauth; + +pub use error::Error; const BEARER_TOKEN_PREFIX: &str = "Bearer "; -pub type IntrospectionResponse = - StandardTokenIntrospectionResponse; +pub type IntrospectionResponse = StandardTokenIntrospectionResponse; -pub trait AuthorizationRequirements { - fn authorized(introspection: &IntrospectionResponse) -> Result; +pub trait AuthorizationRequirements +where + T: ExtraTokenFields, +{ + fn authorized(introspection: &IntrospectionResponse) -> Result; } pub trait RequireScope { fn scope() -> &'static str; } -impl AuthorizationRequirements for T +impl AuthorizationRequirements for S where - T: RequireScope, + S: RequireScope, + T: ExtraTokenFields, { - fn authorized(introspection: &IntrospectionResponse) -> Result { + fn authorized(introspection: &IntrospectionResponse) -> Result { Ok(introspection .scopes() - .map(|s| s.iter().find(|s| s.as_ref() == T::scope()).is_some()) + .map(|s| s.iter().find(|s| s.as_ref() == S::scope()).is_some()) .unwrap_or(false)) } } pub struct AnyScope; -impl AuthorizationRequirements for AnyScope { - fn authorized(_: &IntrospectionResponse) -> Result { +impl AuthorizationRequirements for AnyScope +where + T: ExtraTokenFields, +{ + fn authorized(_: &IntrospectionResponse) -> Result { Ok(true) } } -pub struct RequireAuthorization +pub struct RequireAuthorization where - T: AuthorizationRequirements, + R: AuthorizationRequirements, + T: ExtraTokenFields, { - introspection: IntrospectionResponse, - _auth_marker: PhantomData, + introspection: IntrospectionResponse, + _auth_marker: PhantomData, } -impl RequireAuthorization +impl RequireAuthorization where - T: AuthorizationRequirements, + R: AuthorizationRequirements, + T: ExtraTokenFields, { - pub fn introspection(&self) -> &IntrospectionResponse { + pub fn introspection(&self) -> &IntrospectionResponse { &self.introspection } } -impl FromRequest for RequireAuthorization +impl FromRequest for RequireAuthorization where - T: AuthorizationRequirements + 'static, + R: AuthorizationRequirements + 'static, + T: ExtraTokenFields + 'static + Clone, { type Error = Error; type Future = LocalBoxFuture<'static, Result>; fn from_request(req: &actix_web::HttpRequest, _: &mut dev::Payload) -> Self::Future { - let verifier = if let Some(verifier) = req.app_data::() { + let my_req2 = req.clone(); + + let verifier = if let Some(verifier) = my_req2.app_data::>() { verifier.clone() } else { return Box::pin(ready(Err(Error::ConfigurationError))); @@ -124,7 +146,7 @@ where .verify_request(my_req) .await .and_then(|introspection| { - if T::authorized(&introspection)? { + if R::authorized(&introspection)? { Ok(RequireAuthorization { introspection, _auth_marker: PhantomData::default(), @@ -138,21 +160,29 @@ where } #[derive(Clone)] -struct RequireAuthorizationConfigInner { +struct RequireAuthorizationConfigInner +where + T: ExtraTokenFields, +{ client: oauth2::Client< StandardErrorResponse, - StandardTokenResponse, + StandardTokenResponse, BasicTokenType, - StandardTokenIntrospectionResponse, + StandardTokenIntrospectionResponse, StandardRevocableToken, StandardErrorResponse, >, } #[derive(Clone)] -pub struct RequireAuthorizationConfig(Arc); +pub struct RequireAuthorizationConfig(Arc>) +where + T: ExtraTokenFields; -impl RequireAuthorizationConfig { +impl RequireAuthorizationConfig +where + T: ExtraTokenFields, +{ pub fn new( client_id: String, client_secret: Option, @@ -169,7 +199,7 @@ impl RequireAuthorizationConfig { RequireAuthorizationConfig(Arc::new(RequireAuthorizationConfigInner { client })) } - async fn verify_request(&self, req: HttpRequest) -> Result { + async fn verify_request(&self, req: HttpRequest) -> Result, Error> { let access_token = req .headers() .get("Authorization") -- cgit v1.2.3