1use crate::auth::core::{AuthInfo, TokenValidator};
2use actix_web::error::ErrorUnauthorized;
3use actix_web::Error;
4use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
5use serde::{Deserialize, Serialize};
6use std::fs;
7use std::future::Future;
8use std::path::Path;
9use std::pin::Pin;
10use std::sync::Arc;
11
12pub struct JWTAuthConfig {
13 pub jwt_key_path: String,
14 pub jwt_audience: String,
15}
16
17#[derive(Debug, Serialize, Deserialize)]
18pub struct Claims {
19 pub sub: String,
21 #[serde(default)]
22 pub exp: Option<u64>,
23 #[serde(default)]
24 pub iat: Option<u64>,
25 #[serde(default)]
26 pub nbf: Option<u64>,
27 #[serde(default)]
28 pub iss: Option<String>,
29 #[serde(default)]
30 pub aud: Option<String>,
31
32 #[serde(default)]
34 pub groups: Vec<String>,
35 #[serde(default)]
36 pub name: Option<String>,
37}
38
39#[derive(Clone)]
40pub struct JWTValidator {
41 jwt_key: Arc<DecodingKey>,
42 validation: Arc<Validation>,
43}
44
45#[derive(Default)]
46pub struct JWTValidatorBuilder {
47 jwt_key: Option<DecodingKey>,
48 audiences: Vec<String>,
49 issuer: Option<String>,
50 algorithm: Algorithm,
51 leeway: u64,
52 validate_exp: bool,
53 validate_nbf: bool,
54 validate_aud: bool,
55}
56
57impl JWTValidatorBuilder {
58 pub fn new() -> Self {
60 Self {
61 jwt_key: None,
62 audiences: Vec::new(),
63 issuer: None,
64 algorithm: Algorithm::RS256,
65 leeway: 0,
66 validate_exp: true,
67 validate_nbf: true,
68 validate_aud: true,
69 }
70 }
71
72 pub fn with_rsa_pem_key_file<P: AsRef<Path>>(mut self, file_path: P) -> Result<Self, Error> {
74 let key_content = fs::read_to_string(file_path)
75 .map_err(|e| ErrorUnauthorized(format!("Failed to read JWT key file: {}", e)))?;
76
77 self.jwt_key = Some(
78 DecodingKey::from_rsa_pem(key_content.as_bytes())
79 .map_err(|e| ErrorUnauthorized(format!("Invalid RSA public key: {}", e)))?,
80 );
81
82 Ok(self)
83 }
84
85 pub fn with_rsa_pem_key(mut self, key_content: &str) -> Result<Self, Error> {
87 self.jwt_key = Some(
88 DecodingKey::from_rsa_pem(key_content.as_bytes())
89 .map_err(|e| ErrorUnauthorized(format!("Invalid RSA public key: {}", e)))?,
90 );
91
92 Ok(self)
93 }
94
95 pub fn with_key(mut self, key: DecodingKey) -> Self {
97 self.jwt_key = Some(key);
98 self
99 }
100
101 pub fn with_audience(mut self, audience: impl Into<String>) -> Self {
103 self.audiences.push(audience.into());
104 self
105 }
106
107 pub fn with_audiences(
109 mut self,
110 audiences: impl IntoIterator<Item = impl Into<String>>,
111 ) -> Self {
112 self.audiences
113 .extend(audiences.into_iter().map(|a| a.into()));
114 self
115 }
116
117 pub fn with_issuer(mut self, issuer: impl Into<String>) -> Self {
119 self.issuer = Some(issuer.into());
120 self
121 }
122
123 pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
125 self.algorithm = algorithm;
126 self
127 }
128
129 pub fn with_leeway(mut self, seconds: u64) -> Self {
131 self.leeway = seconds;
132 self
133 }
134
135 pub fn validate_expiration(mut self, validate: bool) -> Self {
137 self.validate_exp = validate;
138 self
139 }
140
141 pub fn validate_not_before(mut self, validate: bool) -> Self {
143 self.validate_nbf = validate;
144 self
145 }
146
147 pub fn validate_audience(mut self, validate: bool) -> Self {
149 self.validate_aud = validate;
150 self
151 }
152
153 pub fn build(self) -> Result<JWTValidator, Error> {
155 let jwt_key = self
156 .jwt_key
157 .ok_or_else(|| ErrorUnauthorized("JWT key is required"))?;
158
159 let mut validation = Validation::new(self.algorithm);
160
161 if !self.audiences.is_empty() {
163 validation.set_audience(&self.audiences);
164 }
165
166 if let Some(issuer) = self.issuer {
168 validation.set_issuer(&[issuer]);
169 }
170
171 validation.leeway = self.leeway;
173 validation.validate_exp = self.validate_exp;
174 validation.validate_nbf = self.validate_nbf;
175 validation.validate_aud = self.validate_aud;
176
177 Ok(JWTValidator {
178 jwt_key: Arc::new(jwt_key),
179 validation: Arc::new(validation),
180 })
181 }
182}
183
184impl JWTValidator {
185 pub fn builder() -> JWTValidatorBuilder {
187 JWTValidatorBuilder::new()
188 }
189
190 pub fn new<P: AsRef<Path>>(
192 jwt_key_file: P,
193 audience: impl Into<String>,
194 ) -> Result<Self, Error> {
195 Self::builder()
196 .with_rsa_pem_key_file(jwt_key_file)?
197 .with_audience(audience)
198 .build()
199 }
200}
201
202impl TokenValidator for JWTValidator {
203 fn validate_token<'a>(
204 &'a self,
205 token: &'a str,
206 ) -> Pin<Box<dyn Future<Output = Result<AuthInfo, Error>> + Send + 'a>> {
207 Box::pin(async move {
208 match decode::<Claims>(token, &self.jwt_key, &self.validation) {
210 Ok(token_data) => {
211 let name = token_data
214 .claims
215 .name
216 .clone()
217 .unwrap_or_else(|| token_data.claims.sub.clone());
218 Ok(AuthInfo {
219 name,
220 sub: token_data.claims.sub,
221 groups: token_data.claims.groups,
222 })
223 }
224 Err(e) => {
225 Err(ErrorUnauthorized(format!("Invalid JWT: {}", e)))
227 }
228 }
229 })
230 }
231}