1use std::collections::HashMap;
8use std::future::{Future, Ready, ready};
9use std::pin::Pin;
10use std::sync::Arc;
11use std::time::Duration;
12
13use actix_web::cookie::{Cookie, SameSite};
14use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
15use actix_web::http::header;
16use actix_web::{HttpMessage, HttpRequest, HttpResponse, HttpResponseBuilder};
17use base64::Engine;
18use base64::engine::general_purpose::URL_SAFE;
19use chrono::{DateTime, Utc};
20use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation};
21use serde_json::Value;
22use tracing::warn;
23
24use crate::core::{Token, TokenStore};
25use crate::errors::JwtError;
26use crate::store::InMemoryRefreshTokenStore;
27
28#[derive(Debug, Clone)]
32pub struct JwtPayload(pub HashMap<String, Value>);
33
34#[derive(Debug, Clone)]
38pub struct JwtTokenString(pub String);
39
40#[derive(Debug, Clone)]
44pub struct JwtIdentity(pub Value);
45
46pub struct ActixJwtMiddleware {
117 pub realm: String,
119 pub identity_key: String,
121
122 pub signing_algorithm: String,
124 pub key: Vec<u8>,
126 pub key_func:
129 Option<Arc<dyn Fn(&jsonwebtoken::Header) -> Result<DecodingKey, JwtError> + Send + Sync>>,
130
131 pub timeout: Duration,
133 pub timeout_func: Option<Arc<dyn Fn(&Value) -> Duration + Send + Sync>>,
135 pub max_refresh: Duration,
137 pub time_func: Arc<dyn Fn() -> DateTime<Utc> + Send + Sync>,
139
140 pub authenticator: Option<
147 Arc<
148 dyn Fn(
149 &HttpRequest,
150 &[u8],
151 )
152 -> Pin<Box<dyn Future<Output = Result<Value, JwtError>> + Send>>
153 + Send
154 + Sync,
155 >,
156 >,
157 pub authorizer: Arc<dyn Fn(&HttpRequest, &Value) -> bool + Send + Sync>,
159 pub payload_func: Option<Arc<dyn Fn(&Value) -> HashMap<String, Value> + Send + Sync>>,
161 pub identity_handler: Arc<dyn Fn(&HttpRequest) -> Option<Value> + Send + Sync>,
163
164 pub unauthorized: Arc<dyn Fn(&HttpRequest, u16, &str) -> HttpResponse + Send + Sync>,
166 pub login_response: Arc<dyn Fn(&HttpRequest, &Token) -> HttpResponse + Send + Sync>,
168 pub logout_response: Arc<dyn Fn(&HttpRequest) -> HttpResponse + Send + Sync>,
170 pub refresh_response: Arc<dyn Fn(&HttpRequest, &Token) -> HttpResponse + Send + Sync>,
172 pub http_status_message_func: Arc<dyn Fn(&HttpRequest, &JwtError) -> String + Send + Sync>,
174
175 pub token_lookup: String,
178 pub token_head_name: String,
180 pub exp_field: String,
182
183 pub priv_key_file: Option<String>,
185 pub priv_key_bytes: Option<Vec<u8>>,
187 pub pub_key_file: Option<String>,
189 pub pub_key_bytes: Option<Vec<u8>>,
191 pub private_key_passphrase: Option<String>,
193 encoding_key: Option<EncodingKey>,
194 decoding_key: Option<DecodingKey>,
195
196 pub send_cookie: bool,
198 pub cookie_max_age: Duration,
200 pub secure_cookie: bool,
202 pub cookie_http_only: bool,
204 pub cookie_domain: Option<String>,
206 pub cookie_name: String,
208 pub cookie_same_site: SameSite,
210 pub send_authorization: bool,
213
214 pub refresh_token_timeout: Duration,
216 pub refresh_token_cookie_name: String,
218 pub refresh_token_length: usize,
220 pub refresh_token_store: Arc<dyn TokenStore>,
222
223 pub skipper: Option<Arc<dyn Fn(&ServiceRequest) -> bool + Send + Sync>>,
226 pub before_func: Option<Arc<dyn Fn(&ServiceRequest) + Send + Sync>>,
228 pub success_handler: Option<Arc<dyn Fn(&HttpRequest) -> Result<(), JwtError> + Send + Sync>>,
230 pub error_handler:
233 Option<Arc<dyn Fn(&HttpRequest, JwtError) -> Option<JwtError> + Send + Sync>>,
234 pub continue_on_ignored_error: bool,
238}
239
240impl ActixJwtMiddleware {
241 pub fn new() -> Self {
248 Self {
249 realm: "actix jwt".to_string(),
250 identity_key: "identity".to_string(),
251
252 signing_algorithm: "HS256".to_string(),
253 key: Vec::new(),
254 key_func: None,
255
256 timeout: Duration::from_secs(3600), timeout_func: None,
258 max_refresh: Duration::ZERO,
259 time_func: Arc::new(Utc::now),
260
261 authenticator: None,
262 authorizer: Arc::new(|_req, _data| true),
263 payload_func: None,
264 identity_handler: Arc::new(|req| {
265 let ext = req.extensions();
266 let payload = ext.get::<JwtPayload>()?;
267 payload.0.get("identity").cloned()
268 }),
269
270 unauthorized: Arc::new(|_req, code, message| {
271 HttpResponse::build(
272 actix_web::http::StatusCode::from_u16(code)
273 .unwrap_or(actix_web::http::StatusCode::UNAUTHORIZED),
274 )
275 .json(serde_json::json!({
276 "code": code,
277 "message": message,
278 }))
279 }),
280 login_response: Arc::new(|_req, token| {
281 HttpResponse::Ok().json(Self::generate_token_response_static(token))
282 }),
283 logout_response: Arc::new(|_req| {
284 HttpResponse::Ok().json(serde_json::json!({ "code": 200 }))
285 }),
286 refresh_response: Arc::new(|_req, token| {
287 HttpResponse::Ok().json(Self::generate_token_response_static(token))
288 }),
289 http_status_message_func: Arc::new(|_req, err| err.to_string()),
290
291 token_lookup: "header:Authorization".to_string(),
292 token_head_name: "Bearer".to_string(),
293 exp_field: "exp".to_string(),
294
295 priv_key_file: None,
296 priv_key_bytes: None,
297 pub_key_file: None,
298 pub_key_bytes: None,
299 private_key_passphrase: None,
300 encoding_key: None,
301 decoding_key: None,
302
303 send_cookie: false,
304 cookie_max_age: Duration::from_secs(3600),
305 secure_cookie: false,
306 cookie_http_only: false,
307 cookie_domain: None,
308 cookie_name: "jwt".to_string(),
309 cookie_same_site: SameSite::Lax,
310 send_authorization: false,
311
312 refresh_token_timeout: Duration::from_secs(30 * 24 * 3600), refresh_token_cookie_name: "refresh_token".to_string(),
314 refresh_token_length: 32,
315 refresh_token_store: Arc::new(InMemoryRefreshTokenStore::new()),
316
317 skipper: None,
318 before_func: None,
319 success_handler: None,
320 error_handler: None,
321 continue_on_ignored_error: false,
322 }
323 }
324
325 pub fn init(&mut self) -> Result<(), JwtError> {
336 if self.token_lookup.is_empty() {
337 self.token_lookup = "header:Authorization".to_string();
338 }
339
340 if self.signing_algorithm.is_empty() {
341 self.signing_algorithm = "HS256".to_string();
342 }
343
344 if self.timeout == Duration::ZERO {
345 self.timeout = Duration::from_secs(3600);
346 }
347
348 let token_head = self.token_head_name.trim().to_string();
349 self.token_head_name = if token_head.is_empty() {
350 "Bearer".to_string()
351 } else {
352 token_head
353 };
354
355 if self.realm.is_empty() {
356 self.realm = "actix jwt".to_string();
357 }
358
359 if self.cookie_max_age == Duration::ZERO {
360 self.cookie_max_age = self.timeout;
361 }
362
363 if self.cookie_name.is_empty() {
364 self.cookie_name = "jwt".to_string();
365 }
366
367 if self.refresh_token_cookie_name.is_empty() {
368 self.refresh_token_cookie_name = "refresh_token".to_string();
369 }
370
371 if self.exp_field.is_empty() {
372 self.exp_field = "exp".to_string();
373 }
374
375 if self.identity_key.is_empty() {
376 self.identity_key = "identity".to_string();
377 }
378
379 if self.refresh_token_timeout == Duration::ZERO {
380 self.refresh_token_timeout = Duration::from_secs(30 * 24 * 3600);
381 }
382
383 if self.refresh_token_length == 0 {
384 self.refresh_token_length = 32;
385 }
386
387 if self.key_func.is_some() {
389 return Ok(());
390 }
391
392 if self.using_public_key_algo() {
393 return self.read_keys();
394 }
395
396 if self.key.is_empty() {
397 return Err(JwtError::MissingSecretKey);
398 }
399
400 self.encoding_key = Some(EncodingKey::from_secret(&self.key));
401 self.decoding_key = Some(DecodingKey::from_secret(&self.key));
402
403 Ok(())
404 }
405
406 pub fn using_public_key_algo(&self) -> bool {
408 matches!(self.signing_algorithm.as_str(), "RS256" | "RS384" | "RS512")
409 }
410
411 fn algorithm(&self) -> Result<Algorithm, JwtError> {
413 match self.signing_algorithm.as_str() {
414 "HS256" => Ok(Algorithm::HS256),
415 "HS384" => Ok(Algorithm::HS384),
416 "HS512" => Ok(Algorithm::HS512),
417 "RS256" => Ok(Algorithm::RS256),
418 "RS384" => Ok(Algorithm::RS384),
419 "RS512" => Ok(Algorithm::RS512),
420 _ => Err(JwtError::InvalidSigningAlgorithm),
421 }
422 }
423
424 fn read_keys(&mut self) -> Result<(), JwtError> {
425 self.load_private_key()?;
426 self.load_public_key()?;
427 Ok(())
428 }
429
430 fn load_private_key(&mut self) -> Result<(), JwtError> {
431 let key_data = if let Some(ref path) = self.priv_key_file {
432 std::fs::read(path).map_err(|e| {
433 warn!("Failed to read private key file {}: {}", path, e);
434 JwtError::NoPrivKeyFile
435 })?
436 } else if let Some(ref bytes) = self.priv_key_bytes {
437 bytes.clone()
438 } else {
439 return Err(JwtError::NoPrivKeyFile);
440 };
441
442 if let Some(ref passphrase) = self.private_key_passphrase {
443 let pem_str = std::str::from_utf8(&key_data).map_err(|_| JwtError::InvalidPrivKey)?;
445 let doc = pkcs8::EncryptedPrivateKeyInfo::try_from(pem_str.as_bytes())
446 .map_err(|_| JwtError::InvalidPrivKey)?;
447
448 let decrypted = doc
449 .decrypt(passphrase.as_bytes())
450 .map_err(|_| JwtError::InvalidPrivKey)?;
451
452 let der_bytes = decrypted.as_bytes();
453
454 let pem = pem::encode(&pem::Pem::new("PRIVATE KEY", der_bytes.to_vec()));
456 self.encoding_key = Some(
457 EncodingKey::from_rsa_pem(pem.as_bytes()).map_err(|_| JwtError::InvalidPrivKey)?,
458 );
459 } else {
460 self.encoding_key =
461 Some(EncodingKey::from_rsa_pem(&key_data).map_err(|_| JwtError::InvalidPrivKey)?);
462 }
463
464 Ok(())
465 }
466
467 fn load_public_key(&mut self) -> Result<(), JwtError> {
468 let key_data = if let Some(ref path) = self.pub_key_file {
469 std::fs::read(path).map_err(|e| {
470 warn!("Failed to read public key file {}: {}", path, e);
471 JwtError::NoPubKeyFile
472 })?
473 } else if let Some(ref bytes) = self.pub_key_bytes {
474 bytes.clone()
475 } else {
476 return Err(JwtError::NoPubKeyFile);
477 };
478
479 self.decoding_key =
480 Some(DecodingKey::from_rsa_pem(&key_data).map_err(|_| JwtError::InvalidPubKey)?);
481
482 Ok(())
483 }
484
485 pub fn generate_access_token(&self, data: &Value) -> Result<(String, DateTime<Utc>), JwtError> {
487 let alg = self.algorithm()?;
488
489 let mut claims = serde_json::Map::new();
490
491 let framework_claims: &[&str] = &["exp", "orig_iat"];
493
494 if let Some(ref pf) = self.payload_func {
495 for (k, v) in pf(data) {
496 if !framework_claims.contains(&k.as_str()) {
497 claims.insert(k, v);
498 }
499 }
500 }
501
502 let now = (self.time_func)();
503 let timeout = self
504 .timeout_func
505 .as_ref()
506 .map(|f| f(data))
507 .unwrap_or(self.timeout);
508 let expire = now
509 + chrono::Duration::from_std(timeout)
510 .unwrap_or_else(|_| chrono::Duration::seconds(3600));
511
512 claims.insert(
513 self.exp_field.clone(),
514 Value::Number(expire.timestamp().into()),
515 );
516 claims.insert(
517 "orig_iat".to_string(),
518 Value::Number(now.timestamp().into()),
519 );
520
521 let header = Header::new(alg);
522 let claims_value = Value::Object(claims);
523
524 let encoding_key = self
525 .encoding_key
526 .as_ref()
527 .ok_or(JwtError::MissingSecretKey)?;
528
529 let token_string = jsonwebtoken::encode(&header, &claims_value, encoding_key)
530 .map_err(|_| JwtError::FailedTokenCreation)?;
531
532 Ok((token_string, expire))
533 }
534
535 pub fn generate_refresh_token(&self) -> Result<String, JwtError> {
537 use rand::RngCore;
538 let mut buf = vec![0u8; self.refresh_token_length];
539 rand::thread_rng()
540 .try_fill_bytes(&mut buf)
541 .map_err(|e| JwtError::Internal(format!("RNG failure: {e}")))?;
542 Ok(URL_SAFE.encode(&buf))
543 }
544
545 async fn store_refresh_token(&self, token: &str, user_data: &Value) -> Result<(), JwtError> {
547 let expiry = (self.time_func)()
548 + chrono::Duration::from_std(self.refresh_token_timeout)
549 .unwrap_or_else(|_| chrono::Duration::days(30));
550 self.refresh_token_store
551 .set(token, user_data.clone(), expiry)
552 .await
553 }
554
555 async fn validate_refresh_token(&self, token: &str) -> Result<Value, JwtError> {
557 self.refresh_token_store
558 .get(token)
559 .await
560 .map_err(|e| match e {
561 JwtError::RefreshTokenNotFound => JwtError::InvalidRefreshToken,
562 other => other,
563 })
564 }
565
566 async fn revoke_refresh_token(&self, token: &str) -> Result<(), JwtError> {
568 self.refresh_token_store.delete(token).await
569 }
570
571 pub async fn token_generator(&self, data: &Value) -> Result<Token, JwtError> {
575 let (access_token, expire) = self.generate_access_token(data)?;
576 let refresh_token = self.generate_refresh_token()?;
577
578 self.store_refresh_token(&refresh_token, data).await?;
579
580 let now = (self.time_func)();
581 Ok(Token {
582 access_token,
583 token_type: "Bearer".to_string(),
584 refresh_token: Some(refresh_token),
585 expires_at: expire.timestamp(),
586 created_at: now.timestamp(),
587 })
588 }
589
590 pub async fn token_generator_with_revocation(
592 &self,
593 data: &Value,
594 old_refresh_token: &str,
595 ) -> Result<Token, JwtError> {
596 let token_pair = self.token_generator(data).await?;
597
598 if let Err(e) = self.revoke_refresh_token(old_refresh_token).await {
600 if !matches!(e, JwtError::RefreshTokenNotFound) {
601 return Err(e);
602 }
603 }
604
605 Ok(token_pair)
606 }
607
608 pub fn parse_token_from_request(
610 &self,
611 req: &HttpRequest,
612 ) -> Result<TokenData<Value>, JwtError> {
613 let token_str = self.extract_token_string(req)?;
614
615 req.extensions_mut()
617 .insert(JwtTokenString(token_str.clone()));
618
619 self.parse_token_string(&token_str)
620 }
621
622 pub fn parse_token_string(&self, token: &str) -> Result<TokenData<Value>, JwtError> {
624 let alg = self.algorithm()?;
625
626 if let Some(ref kf) = self.key_func {
627 let header = jsonwebtoken::decode_header(token)
629 .map_err(|e| JwtError::TokenParsing(e.to_string()))?;
630 let dk = kf(&header)?;
631 let mut validation = Validation::new(alg);
632 validation.validate_exp = true;
633 validation.validate_aud = false;
634 validation.required_spec_claims.clear();
635 return jsonwebtoken::decode::<Value>(token, &dk, &validation)
636 .map_err(|e| JwtError::TokenParsing(e.to_string()));
637 }
638
639 let decoding_key = self
640 .decoding_key
641 .as_ref()
642 .ok_or(JwtError::MissingSecretKey)?;
643
644 let mut validation = Validation::new(alg);
645 validation.validate_exp = true;
646 validation.validate_aud = false;
647 validation.required_spec_claims.clear();
648
649 jsonwebtoken::decode::<Value>(token, decoding_key, &validation)
650 .map_err(|e| JwtError::TokenParsing(e.to_string()))
651 }
652
653 fn extract_token_string(&self, req: &HttpRequest) -> Result<String, JwtError> {
655 let methods: Vec<&str> = self.token_lookup.split(',').collect();
656 let mut last_err: Option<JwtError> = None;
657
658 for method in methods {
659 let parts: Vec<&str> = method.trim().splitn(2, ':').collect();
660 if parts.len() != 2 {
661 continue;
662 }
663 let source = parts[0].trim();
664 let name = parts[1].trim();
665
666 let result = match source {
667 "header" => self.jwt_from_header(req, name),
668 "query" => self.jwt_from_query(req, name),
669 "cookie" => self.jwt_from_cookie(req, name),
670 "param" => self.jwt_from_param(req, name),
671 "form" => self.jwt_from_form(req, name),
672 _ => continue,
673 };
674
675 match result {
676 Ok(t) if !t.is_empty() => return Ok(t),
677 Ok(_) => {}
678 Err(e) => {
679 last_err = Some(e);
680 }
681 }
682 }
683
684 Err(last_err.unwrap_or(JwtError::TokenExtraction(
685 "no token found in request".to_string(),
686 )))
687 }
688
689 fn jwt_from_header(&self, req: &HttpRequest, key: &str) -> Result<String, JwtError> {
690 let auth_header = req
691 .headers()
692 .get(key)
693 .and_then(|v| v.to_str().ok())
694 .unwrap_or("");
695
696 if auth_header.is_empty() {
697 return Err(JwtError::EmptyAuthHeader);
698 }
699
700 let parts: Vec<&str> = auth_header.splitn(2, ' ').collect();
701 if parts.len() != 2 || parts[0] != self.token_head_name {
702 return Err(JwtError::InvalidAuthHeader);
703 }
704
705 Ok(parts[1].to_string())
706 }
707
708 fn jwt_from_query(&self, req: &HttpRequest, key: &str) -> Result<String, JwtError> {
709 let qs = req.query_string();
710 for pair in qs.split('&') {
712 let mut kv = pair.splitn(2, '=');
713 if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
714 if k == key && !v.is_empty() {
715 return Ok(v.to_string());
716 }
717 }
718 }
719 Err(JwtError::EmptyQueryToken)
720 }
721
722 fn jwt_from_cookie(&self, req: &HttpRequest, key: &str) -> Result<String, JwtError> {
723 req.cookie(key)
724 .map(|c| c.value().to_string())
725 .filter(|v| !v.is_empty())
726 .ok_or(JwtError::EmptyCookieToken)
727 }
728
729 fn jwt_from_param(&self, req: &HttpRequest, key: &str) -> Result<String, JwtError> {
730 let val = req.match_info().get(key).unwrap_or("");
731 if val.is_empty() {
732 return Err(JwtError::EmptyParamToken);
733 }
734 Ok(val.to_string())
735 }
736
737 fn jwt_from_form(&self, _req: &HttpRequest, _key: &str) -> Result<String, JwtError> {
738 Err(JwtError::EmptyParamToken)
742 }
743
744 fn get_claims_from_jwt(&self, req: &HttpRequest) -> Result<HashMap<String, Value>, JwtError> {
746 let token_data = self.parse_token_from_request(req)?;
747
748 let claims_map = match token_data.claims {
753 Value::Object(map) => map.into_iter().collect(),
754 _ => HashMap::new(),
755 };
756
757 Ok(claims_map)
758 }
759
760 fn middleware_impl(&self, req: &HttpRequest) -> Result<(), JwtError> {
762 let claims = self
763 .get_claims_from_jwt(req)
764 .map_err(|e| JwtError::TokenParsing(e.to_string()))?;
765
766 if !claims.contains_key("exp") {
768 return Err(JwtError::TokenExtraction(
769 JwtError::MissingExpField.to_string(),
770 ));
771 }
772
773 req.extensions_mut().insert(JwtPayload(claims));
774
775 let identity = (self.identity_handler)(req);
776 if let Some(ref id) = identity {
777 req.extensions_mut().insert(JwtIdentity(id.clone()));
778 }
779
780 let auth_data = identity.unwrap_or(Value::Null);
781 if !(self.authorizer)(req, &auth_data) {
782 return Err(JwtError::Forbidden);
783 }
784
785 Ok(())
786 }
787
788 fn unauthorized_response(&self, req: &HttpRequest, code: u16, message: &str) -> HttpResponse {
790 let mut resp = (self.unauthorized)(req, code, message);
791 resp.headers_mut().insert(
792 header::WWW_AUTHENTICATE,
793 format!("Bearer realm=\"{}\"", self.realm).parse().unwrap(),
794 );
795 resp
796 }
797
798 fn handle_middleware_error(&self, req: &HttpRequest, err: &JwtError) -> HttpResponse {
800 match err {
801 JwtError::Forbidden => {
802 let msg = (self.http_status_message_func)(req, &JwtError::Forbidden);
803 self.unauthorized_response(req, 403, &msg)
804 }
805 JwtError::TokenParsing(inner) => self.handle_token_error(req, inner),
806 JwtError::TokenExtraction(inner) => {
807 let msg = inner.clone();
808 self.unauthorized_response(req, 400, &msg)
809 }
810 other => {
811 let msg = (self.http_status_message_func)(req, other);
812 self.unauthorized_response(req, 401, &msg)
813 }
814 }
815 }
816
817 fn handle_token_error(&self, req: &HttpRequest, detail: &str) -> HttpResponse {
818 let lower = detail.to_lowercase();
819 if lower.contains("expired") {
820 let msg = (self.http_status_message_func)(req, &JwtError::ExpiredToken);
821 self.unauthorized_response(req, 401, &msg)
822 } else if lower.contains("exp") && lower.contains("invalid") {
823 let msg = (self.http_status_message_func)(req, &JwtError::WrongFormatOfExp);
824 self.unauthorized_response(req, 400, &msg)
825 } else if lower.contains("exp") && lower.contains("required") {
826 let msg = (self.http_status_message_func)(req, &JwtError::MissingExpField);
827 self.unauthorized_response(req, 400, &msg)
828 } else {
829 let err = JwtError::TokenParsing(detail.to_string());
830 let msg = (self.http_status_message_func)(req, &err);
831 self.unauthorized_response(req, 401, &msg)
832 }
833 }
834
835 pub fn set_cookie(builder: &mut HttpResponseBuilder, config: &CookieConfig, value: &str) {
837 let mut cookie = Cookie::build(config.name.clone(), value.to_string())
838 .path("/")
839 .max_age(actix_web::cookie::time::Duration::seconds(
840 config.max_age.as_secs() as i64,
841 ))
842 .secure(config.secure)
843 .http_only(config.http_only)
844 .same_site(config.same_site)
845 .finish();
846
847 if let Some(ref domain) = config.domain {
848 cookie.set_domain(domain.clone());
849 }
850
851 builder.cookie(cookie);
852 }
853
854 pub fn access_cookie_config(&self) -> CookieConfig {
856 CookieConfig {
857 name: self.cookie_name.clone(),
858 max_age: self.cookie_max_age,
859 secure: self.secure_cookie,
860 http_only: self.cookie_http_only,
861 domain: self.cookie_domain.clone(),
862 same_site: self.cookie_same_site,
863 }
864 }
865
866 pub fn refresh_cookie_config(&self) -> CookieConfig {
868 CookieConfig {
869 name: self.refresh_token_cookie_name.clone(),
870 max_age: self.refresh_token_timeout,
871 secure: true, http_only: true, domain: self.cookie_domain.clone(),
874 same_site: self.cookie_same_site,
875 }
876 }
877
878 fn append_cookie(
883 headers: &mut actix_web::http::header::HeaderMap,
884 config: &CookieConfig,
885 value: &str,
886 ) {
887 let mut cookie = Cookie::build(config.name.clone(), value.to_string())
888 .path("/")
889 .max_age(actix_web::cookie::time::Duration::seconds(
890 config.max_age.as_secs() as i64,
891 ))
892 .secure(config.secure)
893 .http_only(config.http_only)
894 .same_site(config.same_site)
895 .finish();
896
897 if let Some(ref domain) = config.domain {
898 cookie.set_domain(domain.clone());
899 }
900
901 headers.append(header::SET_COOKIE, cookie.to_string().parse().unwrap());
902 }
903
904 fn append_delete_cookie(
907 headers: &mut actix_web::http::header::HeaderMap,
908 config: &CookieConfig,
909 ) {
910 let mut cookie = Cookie::build(config.name.clone(), "")
911 .path("/")
912 .max_age(actix_web::cookie::time::Duration::seconds(-1))
913 .secure(config.secure)
914 .http_only(config.http_only)
915 .same_site(config.same_site)
916 .finish();
917
918 if let Some(ref domain) = config.domain {
919 cookie.set_domain(domain.clone());
920 }
921
922 headers.append(header::SET_COOKIE, cookie.to_string().parse().unwrap());
923 }
924
925 pub fn delete_cookie(builder: &mut HttpResponseBuilder, config: &CookieConfig) {
927 let mut cookie = Cookie::build(config.name.clone(), "")
928 .path("/")
929 .max_age(actix_web::cookie::time::Duration::seconds(-1))
930 .secure(config.secure)
931 .http_only(config.http_only)
932 .same_site(config.same_site)
933 .finish();
934
935 if let Some(ref domain) = config.domain {
936 cookie.set_domain(domain.clone());
937 }
938
939 builder.cookie(cookie);
940 }
941
942 pub async fn login_handler(&self, req: &HttpRequest, body: &[u8]) -> HttpResponse {
955 let authenticator = match self.authenticator {
956 Some(ref auth) => auth,
957 None => {
958 let msg = (self.http_status_message_func)(req, &JwtError::MissingAuthenticator);
959 return self.unauthorized_response(req, 500, &msg);
960 }
961 };
962
963 let data = match authenticator(req, body).await {
964 Ok(d) => d,
965 Err(e) => {
966 let msg = (self.http_status_message_func)(req, &e);
967 return self.unauthorized_response(req, 401, &msg);
968 }
969 };
970
971 let token_pair = match self.token_generator(&data).await {
972 Ok(t) => t,
973 Err(_) => {
974 let msg = (self.http_status_message_func)(req, &JwtError::FailedTokenCreation);
975 return self.unauthorized_response(req, 500, &msg);
976 }
977 };
978
979 let mut resp = (self.login_response)(req, &token_pair);
980
981 if self.send_cookie {
982 Self::append_cookie(
983 resp.headers_mut(),
984 &self.access_cookie_config(),
985 &token_pair.access_token,
986 );
987 if let Some(ref rt) = token_pair.refresh_token {
988 Self::append_cookie(resp.headers_mut(), &self.refresh_cookie_config(), rt);
989 }
990 }
991
992 resp
993 }
994
995 pub fn extract_refresh_token(&self, req: &HttpRequest, body: &[u8]) -> Option<String> {
997 if let Some(cookie) = req.cookie(&self.refresh_token_cookie_name) {
999 let val = cookie.value().to_string();
1000 if !val.is_empty() {
1001 return Some(val);
1002 }
1003 }
1004
1005 let content_type = req
1007 .headers()
1008 .get(header::CONTENT_TYPE)
1009 .and_then(|v| v.to_str().ok())
1010 .unwrap_or("");
1011
1012 if content_type.contains("application/x-www-form-urlencoded")
1013 || content_type.contains("multipart/form-data")
1014 {
1015 let body_str = std::str::from_utf8(body).unwrap_or("");
1017 for pair in body_str.split('&') {
1018 let mut kv = pair.splitn(2, '=');
1019 if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
1020 if k == "refresh_token" && !v.is_empty() {
1021 return Some(v.to_string());
1022 }
1023 }
1024 }
1025 } else if content_type.contains("application/json") {
1026 #[derive(serde::Deserialize)]
1027 struct RefreshBody {
1028 refresh_token: Option<String>,
1029 }
1030 if let Ok(parsed) = serde_json::from_slice::<RefreshBody>(body) {
1031 if let Some(rt) = parsed.refresh_token {
1032 if !rt.is_empty() {
1033 return Some(rt);
1034 }
1035 }
1036 }
1037 }
1038
1039 None
1040 }
1041
1042 pub async fn logout_handler(&self, req: &HttpRequest, body: &[u8]) -> HttpResponse {
1044 if let Ok(claims) = self.get_claims_from_jwt(req) {
1046 req.extensions_mut().insert(JwtPayload(claims));
1047 let identity = (self.identity_handler)(req);
1048 if let Some(ref id) = identity {
1049 req.extensions_mut().insert(JwtIdentity(id.clone()));
1050 }
1051 }
1052
1053 if let Some(ref rt) = self.extract_refresh_token(req, body) {
1055 if let Err(e) = self.revoke_refresh_token(rt).await {
1056 warn!("Failed to revoke refresh token on logout: {}", e);
1057 }
1058 }
1059
1060 let mut resp = (self.logout_response)(req);
1061
1062 if self.send_cookie {
1063 Self::append_delete_cookie(resp.headers_mut(), &self.access_cookie_config());
1064 Self::append_delete_cookie(resp.headers_mut(), &self.refresh_cookie_config());
1065 }
1066
1067 resp
1068 }
1069
1070 pub async fn refresh_handler(&self, req: &HttpRequest, body: &[u8]) -> HttpResponse {
1073 let refresh_token = match self.extract_refresh_token(req, body) {
1074 Some(rt) => rt,
1075 None => {
1076 let msg = (self.http_status_message_func)(req, &JwtError::MissingRefreshToken);
1077 return self.unauthorized_response(req, 400, &msg);
1078 }
1079 };
1080
1081 let user_data = match self.validate_refresh_token(&refresh_token).await {
1082 Ok(d) => d,
1083 Err(e) => {
1084 let msg = (self.http_status_message_func)(req, &e);
1085 return self.unauthorized_response(req, 401, &msg);
1086 }
1087 };
1088
1089 let token_pair = match self
1090 .token_generator_with_revocation(&user_data, &refresh_token)
1091 .await
1092 {
1093 Ok(t) => t,
1094 Err(e) => {
1095 let msg = (self.http_status_message_func)(req, &e);
1096 return self.unauthorized_response(req, 500, &msg);
1097 }
1098 };
1099
1100 let mut resp = (self.refresh_response)(req, &token_pair);
1101
1102 if self.send_cookie {
1103 Self::append_cookie(
1104 resp.headers_mut(),
1105 &self.access_cookie_config(),
1106 &token_pair.access_token,
1107 );
1108 if let Some(ref rt) = token_pair.refresh_token {
1109 Self::append_cookie(resp.headers_mut(), &self.refresh_cookie_config(), rt);
1110 }
1111 }
1112
1113 resp
1114 }
1115
1116 pub fn generate_token_response(token: &Token) -> serde_json::Map<String, Value> {
1120 let mut map = serde_json::Map::new();
1121 map.insert(
1122 "access_token".into(),
1123 Value::String(token.access_token.clone()),
1124 );
1125 map.insert("token_type".into(), Value::String(token.token_type.clone()));
1126 map.insert(
1127 "expires_in".into(),
1128 Value::Number(token.expires_in().into()),
1129 );
1130
1131 if let Some(ref rt) = token.refresh_token {
1132 map.insert("refresh_token".into(), Value::String(rt.clone()));
1133 }
1134
1135 map
1136 }
1137
1138 fn generate_token_response_static(token: &Token) -> Value {
1141 let map = Self::generate_token_response(token);
1142 Value::Object(map)
1143 }
1144
1145 pub fn middleware(self: &Arc<Self>) -> JwtAuth {
1149 JwtAuth {
1150 inner: self.clone(),
1151 }
1152 }
1153}
1154
1155impl Default for ActixJwtMiddleware {
1156 fn default() -> Self {
1157 Self::new()
1158 }
1159}
1160
1161pub struct CookieConfig {
1167 pub name: String,
1169 pub max_age: Duration,
1171 pub secure: bool,
1173 pub http_only: bool,
1175 pub domain: Option<String>,
1177 pub same_site: SameSite,
1179}
1180
1181pub fn extract_claims(req: &HttpRequest) -> HashMap<String, Value> {
1198 req.extensions()
1199 .get::<JwtPayload>()
1200 .map(|p| p.0.clone())
1201 .unwrap_or_default()
1202}
1203
1204pub fn get_token(req: &HttpRequest) -> Option<String> {
1208 req.extensions()
1209 .get::<JwtTokenString>()
1210 .map(|t| t.0.clone())
1211}
1212
1213pub fn get_identity(req: &HttpRequest) -> Option<Value> {
1218 req.extensions().get::<JwtIdentity>().map(|i| i.0.clone())
1219}
1220
1221pub struct JwtAuth {
1227 inner: Arc<ActixJwtMiddleware>,
1228}
1229
1230impl<S, B> Transform<S, ServiceRequest> for JwtAuth
1231where
1232 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
1233 B: 'static,
1234{
1235 type Response = ServiceResponse<actix_web::body::EitherBody<B>>;
1236 type Error = actix_web::Error;
1237 type Transform = JwtAuthMiddleware<S>;
1238 type InitError = ();
1239 type Future = Ready<Result<Self::Transform, Self::InitError>>;
1240
1241 fn new_transform(&self, service: S) -> Self::Future {
1242 ready(Ok(JwtAuthMiddleware {
1243 service: Arc::new(service),
1244 inner: self.inner.clone(),
1245 }))
1246 }
1247}
1248
1249pub struct JwtAuthMiddleware<S> {
1251 service: Arc<S>,
1252 inner: Arc<ActixJwtMiddleware>,
1253}
1254
1255impl<S, B> Service<ServiceRequest> for JwtAuthMiddleware<S>
1256where
1257 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
1258 B: 'static,
1259{
1260 type Response = ServiceResponse<actix_web::body::EitherBody<B>>;
1261 type Error = actix_web::Error;
1262 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
1263
1264 fn poll_ready(
1265 &self,
1266 ctx: &mut std::task::Context<'_>,
1267 ) -> std::task::Poll<Result<(), Self::Error>> {
1268 self.service.poll_ready(ctx)
1269 }
1270
1271 fn call(&self, req: ServiceRequest) -> Self::Future {
1272 let mw = self.inner.clone();
1273 let service = self.service.clone();
1274
1275 Box::pin(async move {
1276 if let Some(ref skipper) = mw.skipper {
1278 if skipper(&req) {
1279 let res = service.call(req).await?;
1280 return Ok(res.map_into_left_body());
1281 }
1282 }
1283
1284 if let Some(ref bf) = mw.before_func {
1286 bf(&req);
1287 }
1288
1289 let mw_result = mw.middleware_impl(req.request());
1296
1297 if let Err(err) = mw_result {
1298 if let Some(ref eh) = mw.error_handler {
1300 let maybe_err = eh(req.request(), err);
1301 if maybe_err.is_none() && mw.continue_on_ignored_error {
1302 let res = service.call(req).await?;
1306 return Ok(res.map_into_left_body());
1307 }
1308 if let Some(e) = maybe_err {
1309 let resp = mw.handle_middleware_error(req.request(), &e);
1310 return Ok(req.into_response(resp).map_into_right_body());
1311 }
1312 return Ok(req
1314 .into_response(HttpResponse::Ok().finish())
1315 .map_into_right_body());
1316 }
1317
1318 let resp = mw.handle_middleware_error(req.request(), &err);
1320 return Ok(req.into_response(resp).map_into_right_body());
1321 }
1322
1323 if let Some(ref sh) = mw.success_handler {
1325 if let Err(e) = sh(req.request()) {
1326 let resp = mw.handle_middleware_error(req.request(), &e);
1327 return Ok(req.into_response(resp).map_into_right_body());
1328 }
1329 }
1330
1331 let send_auth = if mw.send_authorization {
1333 let ext = req.extensions();
1334 ext.get::<JwtTokenString>()
1335 .map(|t| format!("{} {}", mw.token_head_name, t.0))
1336 } else {
1337 None
1338 };
1339
1340 let mut res = service.call(req).await?;
1342
1343 if let Some(val) = send_auth {
1344 res.headers_mut()
1345 .insert(header::AUTHORIZATION, val.parse().unwrap());
1346 }
1347
1348 Ok(res.map_into_left_body())
1349 })
1350 }
1351}