1use actix_web::{
2 dev::{Service, ServiceRequest, ServiceResponse, Transform},
3 error::ErrorUnauthorized,
4 Error, FromRequest, HttpMessage,
5};
6use actix_web_httpauth::extractors::bearer::BearerAuth;
7use futures::future::{ready, Ready};
8use std::future::Future;
9use std::pin::Pin;
10use std::rc::Rc;
11use std::task::{Context, Poll};
12
13pub trait TokenValidator: Clone + Send + Sync + 'static {
15 fn validate_token<'a>(
17 &'a self,
18 token: &'a str,
19 ) -> Pin<Box<dyn Future<Output = Result<AuthInfo, Error>> + Send + 'a>>;
20}
21
22#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct AuthInfo {
25 pub name: String,
26 pub sub: String,
27 pub groups: Vec<String>,
28}
29
30impl std::fmt::Display for AuthInfo {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(
33 f,
34 "user.name={} user.sub={} user.groups=[{}]",
35 self.name,
36 self.sub,
37 if self.groups.is_empty() {
38 String::from("(none)")
39 } else {
40 self.groups.join(", ")
41 }
42 )
43 }
44}
45
46#[derive(Clone)]
48pub struct Authentication<V> {
49 validator: V,
50}
51impl<V: TokenValidator> Authentication<V> {
52 pub fn new(validator: V) -> Self {
53 Self { validator }
54 }
55}
56
57impl<V: TokenValidator> Authentication<Box<V>> {
58 pub fn new_boxed(validator: Box<V>) -> Self {
59 Self { validator }
60 }
61}
62
63impl<S, B, V> Transform<S, ServiceRequest> for Authentication<V>
64where
65 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
66 S::Future: 'static,
67 B: 'static,
68 V: TokenValidator,
69{
70 type Response = ServiceResponse<B>;
71 type Error = Error;
72 type Transform = AuthenticationMiddleware<S, V>;
73 type InitError = ();
74 type Future = Ready<Result<Self::Transform, Self::InitError>>;
75
76 fn new_transform(&self, service: S) -> Self::Future {
77 ready(Ok(AuthenticationMiddleware {
78 service: Rc::new(service),
79 validator: self.validator.clone(),
80 }))
81 }
82}
83
84pub struct AuthenticationMiddleware<S, V: TokenValidator> {
85 service: Rc<S>,
86 validator: V,
87}
88
89impl<S, B, V> Service<ServiceRequest> for AuthenticationMiddleware<S, V>
90where
91 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
92 S::Future: 'static,
93 B: 'static,
94 V: TokenValidator,
95{
96 type Response = ServiceResponse<B>;
97 type Error = Error;
98 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
99
100 fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
101 self.service.poll_ready(cx)
102 }
103
104 fn call(&self, req: ServiceRequest) -> Self::Future {
105 let validator = self.validator.clone();
106 let srv = self.service.clone();
107
108 Box::pin(async move {
109 let req_head = req.request();
111 let bearer_result = BearerAuth::extract(req_head).await;
112
113 match bearer_result {
114 Ok(bearer) => {
115 let token = bearer.token();
116
117 match validator.validate_token(token).await {
119 Ok(auth_info) => {
120 req.extensions_mut().insert(auth_info);
122 }
123 Err(e) => {
124 return Err(e);
126 }
127 }
128 }
129 Err(_) => {
130 return Err(ErrorUnauthorized("Missing or invalid Bearer token"));
132 }
133 }
134
135 let res = srv.call(req).await?;
137 Ok(res)
138 })
139 }
140}