1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
//! Overview
//! ========
//! One way to design a web backend is to split it into three parts: the API layer (for reusable actions used in the presentation layer), data layer (for interacting with the database), and presentation layer (for handling requests). This crate aims to provide the API layer for authentication. Example routes are provided in the examples folder, so you can get the presentation layer as well, although you are almost certainly going to want to make some changes so it is not included in the crate by default.
//!
//! Features
//! ========
//!  - Easy: Makes it easy to add authentication to your web application by providing structure and doing the hard parts for you
//!  - Flexible: not opinionated on what your URL structure, UI, or database looks like
//!  - Fast: fully async, and does not require a database query unless logging in or registering (e.g. normal authenticated requests verify without a database query)
//!  - Stateless: no need for server pinning with this library, only cryptography is used to verify sessions, so it's fine if a user sporadically switches backend servers
//!
//! Usage
//! =====
//! Roughly speaking, there are three steps in using this library: create the data layer, integrate with Actix-Web, make the routes, and make the frontend. You can view a complete example [here](https://github.com/john01dav/actix-plus/blob/master/actix-plus-auth/examples/basic_use_in_memory_db.rs).
//!
//! Create the Data Layer
//! ---------------------
//! To create a data layer, you must create a Rust struct to hold the information that you want to associate with each user, except the password hash which is stored separately. You must store the email in this struct. An example struct follows:
//! ```rust,ignore
//! // !!!!!WARNING: anything in your account type is visible to users as it is encoded as a JWT!!!!!
//! #[derive(Serialize, Deserialize, Debug)]
//! pub struct ExampleAccount {
//!     pub username: String, //example of custom data to include with one's account type
//!     pub email: String,
//! }
//!
//! impl Account for ExampleAccount {
//!     fn email(&self) -> &str {
//!         &self.email
//!     }
//! }
//! ```
//! As you can see, after the struct is created, the `Account` trait is implemented to allow actix-plus-auth to access the email, and to signal that this struct represents an account. The `Account` trait, due to [JWT](https://jwt.io/) (Json Web Token) serialization and deserialization, requires that the serde `Serialize` and `DeserializeOwned` traits are implemented. To implement `DeserializeOwned` simply have a struct that owns all of its types (e.g. no reference members) and then derive `Deserialize`. Additionally, the `Account` trait requires the `Debug` trait. Of course, you can implement other traits as you see fit.
//!
//! **Note that anything in your account type is visible to users as it is encoded as a JWT for session storage. DO NOT PUT PRIVATE INFORMATION IN THE ACCOUNT TYPE.**
//!
//! Once the account struct is created, a data provider struct is needed. A data provider struct implements the operations that fetch and store this account type from a database. To create a data provider struct, simply implement the `DataProvider` trait:
//! ```rust,ignore
//! #[async_trait]
//! pub trait DataProvider: Clone {
//!     type AccountType: Account;
//!
//!     ///Adds a new account to the database, then returns that account back. You may need to clone the account when implementing this function. If another account exists with this email, then the function should return some sort of error.
//!     async fn insert_account(
//!         &self,
//!         account: Self::AccountType,
//!         password_hash: String,
//!     ) -> ResponseResult<Self::AccountType>;
//!
//!     /// Fetches the account with the given email from the database, case-insensitively. Note that a lowercase email should be passed to this function, but the matching email as stored in the database may be in any case.
//!     async fn fetch_account(
//!         &self,
//!         email: &str,
//!     ) -> ResponseResult<Option<(Self::AccountType, String)>>;
//! }
//! ```
//! Note that the [async_trait](https://crates.io/crates/async-trait) crate is used to facilitate async functions in this trait, and you must use it when implementing this trait as well. Also, note that the `insert_account` and `fetch_account` functions take `&self` and not `&mut self`, and that `DataProvider` requires `Clone`. The model in this library is that a single data store is shared across many instances of your data provider, and references to those instances, like a normal database connection pool (e.g. as in [sqlx](https://crates.io/crates/sqlx)). When your data provider is cloned, make sure to internally reference the same data such that if a change is made on the clone it can be read back from the original (or other clones) and visa-versa. The best way to implement this is to use a database library (such as [sqlx](https://crates.io/crates/sqlx)) that already works in this way.
//!
//! Congratulations! You have now created the data layer, which is 95% of the work to use this library.
//!
//! An example data provider is not shown here as it will vary wildly between different database backends.
//!
//! Integrate with Actix-Web
//! ------------------------
//! Once your data layer is created, you can move on to integrating with Actix Web. To do this, create an instance of `actix_plus_auth::AuthenticationProvider<T: DataProvider>` via the `new` function. To do this, you'll need a secret and an instance of your data provider:
//! ```rust,ignore
//! let auth = AuthenticationProvider::new(
//!     MyDataProvider::new(),
//!     "some secret, you should use a real one"
//!         .as_bytes()
//!         .iter()
//!         .map(|u| *u)
//!         .collect(),
//! );
//! ```
//! The secret is used to verify JWTs, so it should be both secret (if it is leaked then users can forge a JWT for any account on your service) and unchanging (if it changes, existing sessions will be invalidated). If it is leaked, you should change it, and review your logs for any possible abuse. `AuthenticationProvider` is generic over a provided `DataProvider`-implementing struct, and it will infer the account type from your `DataProvider` implementation, also generically.
//!
//! Once you have an `AuthenticationProvider`, you simply need to make it available to each route. This is done via [Actix Web's state system](https://actix.rs/docs/application/#state):
//! ```rust,ignore
//! HttpServer::new(move || { //move your auth variable into the closure
//!     App::new()
//!         .data(auth.clone()) //clone the closure's copy for each Actix Web worker, this is why clones of a data provider must refer to the same data even when cloned
//! })
//! ```
//!
//! Lastly, as this system relies on cookies with the secure flag set, you must enable TLS/HTTPS for it to work. A simple self-signed certificate is sufficient for development. Refer to [Actix Web documentation](https://actix.rs/docs/http2/) for details on enabling TLS.
//!
//! Make the Routes
//! ---------------
//! Once an `AuthenticationProvider` is registered with Actix Web, routes for login, registration, and logout must be created. `AuthenticationProvider` provides functions that facilitate most of the heavy lifting for these operations, except for logout which is to be implemented entirely by the user.
//!
//! ### Register
//! ```rust,ignore
//! pub async fn register(
//!     &self,
//!     account: DataProviderImpl::AccountType,
//!     password: &str,
//! ) -> ResponseResult<RegistrationOutcome<DataProviderImpl::AccountType>>
//! ```
//! Above is the signature of the registration function. To put it simply, it accepts a reference to the authentication provider, the password to register with, and an instance of your account type. It then returns a `ResponseResult` (from the actix-plus-error crate, used to propagate internal server errors such as those from the user-provided data layer) with a `RegistrationOutcome` instance which has three variants:
//! ```rust,ignore
//! ///The non-error outcomes of registration. Error outcomes are used when a genuine error takes place — e.g. the database is not reachable (represented by the functions on your DataProvider implementation returning an error).
//! pub enum RegistrationOutcome<AccountType: Account> {
//!     ///The account is now in the database, and is given here.
//!     Successful(AccountType),
//!     ///The provided email is not a valid email
//!     InvalidEmail,
//!     //The provided email is already taken
//!     EmailTaken,
//! }
//! ```
//!
//! ### Login
//! ```rust,ignore
//! pub async fn login(
//!     &self,
//!     email: &str,
//!     password: &str,
//! ) -> ResponseResult<LoginOutcome<DataProviderImpl::AccountType>>
//! ```
//! Above is the signature of the login function. To put it simply, it accepts a reference to to the authentication provider, an email, and a username (as provided by the user) and returns a `ResponseResult` (from the actix-plus-error crate, used to propagate internal server errors such as those from the user-provided data layer) with a `LoginOutcome` instance which has two variants:
//! ```rust,ignore
//! ///The non-error outcomes of logging in. Error outcomes are used when a genuine error takes place — e.g. the database is not reachable (represented by the functions on your DataProvider implementation returning an error).
//! pub enum LoginOutcome<AccountType> {
//!     ///The credentials were correct, so the account  and a cookie that should be set in the response to the login route are provided.
//!     Successful(AccountType, Cookie<'static>),
//!     //The provided credentials do not correspond to a valid account.
//!     InvalidEmailOrPassword,
//! }
//! ```
//! If `InvalidEmailOrPassword` is returned then this information should be passed in your response in whatever way you see fit. If `Successful` is returned, then the provided cookie should be set on your HTTP response. Additionally, in the successful scenario, you may set additional cookies to share data with your frontend. Note that, following best practices for cookie-based token storage, the token cookie is HTTP only, so Javascript code can't access it. See [here](https://github.com/john01dav/actix-plus/blob/master/actix-plus-auth/examples/basic_use_in_memory_db.rs) for an example.
//!
//! ### Logout
//! To logout, simply delete the `actix-plus-auth-token` cookie, along with any other cookies that you may have set in your login. You do not need to call into the library to delete a session or anything, as the library is stateless:
//! ```rust,ignore
//! #[post("/logout")]
//! async fn logout(request: HttpRequest) -> Response {
//!     let mut response = HttpResponse::Ok();
//!     if let Some(mut session_cookie) = request.cookie("actix-plus-auth-token") { //you must delete this cookie
//!         session_cookie.set_path("/"); //this is needed to ensure that the cookie deletion goes through
//!         session_cookie.set_secure(true); //this is needed to ensure that the cookie deletion goes through
//!         response.del_cookie(&session_cookie);
//!     }
//!     if let Some(mut username_cookie) = request.cookie("username") { //delete optional user-added cookie
//!         username_cookie.set_path("/");
//!         username_cookie.set_secure(true);
//!         response.del_cookie(&username_cookie);
//!     }
//!     Ok(response.await?)
//! }
//! ```
//!
//! ### Authenticating in Other Routes
//! In another route, you can simply call `current_user(req: &HttpRequest)` on `AuthenticationProvider` to get the current user. This function returns `Err(ResponseError)` that encodes a 401 Not Authorized (via the actix-plus-error crate), so if you use the same crate you can simply propagate with `?` to keep code concise:
//! ```rust,ignore
//! #[get("/private_page")]
//! async fn private_page(request: HttpRequest, auth: Data<ExampleAuthProvider>) -> Response {
//!     let account = auth.current_user(&request)?;
//!     Ok(HttpResponse::Ok() //you can do anything here, including more traditional JSON/REST/etc. routes
//!         .body(format!("Hello {}", account.username))
//!         .await?)
//! }
//! ```
//!
//! Make the Frontend
//! -----------------
//! When making the frontend, there are some considerations to ensure that cookies are included with HTTP requests. Specifically, credentials should be set to 'same-origin' (both for requests to login, logout, and registration; and for general authenticated requests):
//! ```javascript
//! let response = await fetch('/login', {
//!     method: 'POST',
//!     credentials: 'same-origin',
//!     headers: {
//!        'Content-Type': 'application/json'
//!     },
//!     body: JSON.stringify({
//!         email: document.getElementById('email').value,
//!         password: document.getElementById('password').value,
//!     })
//! });
//! ```
//!
//! Todo
//! ====
//! - Email Verification
//! - SQLx example
use actix_plus_error::{ResponseError, ResponseResult};
use actix_plus_utils::current_unix_time_secs;
use actix_web::cookie::{Cookie, CookieBuilder, SameSite};
use actix_web::http::StatusCode;
use actix_web::{HttpMessage, HttpRequest};
use argon2::password_hash::SaltString;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
pub use async_trait::async_trait;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use once_cell::sync::Lazy;
use rand::rngs::OsRng;
use regex::Regex;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;

///A struct that implements this trait represents the data that is stored with each account in the authentication system. The only mandatory data to st ore in an account is the email that the account is associated with. The password should not be stored here. Your account object's data and the password, taken together, are usually one row in a SQL database (although other database types can be used as this library does not interact with the database), where the email is the primary key.
///**Note that this data is viewable to the user as it is stored in an unencrypted (but signed) json web token!**
pub trait Account: Serialize + DeserializeOwned + Debug + 'static {
    ///Gets the email associated with this account.
    fn email(&self) -> &str;
}

///A struct that implements this trait provides the functions that Actix+Auth needs to interact with your database (or flatfile, volatile storage in ram, whatever you want) to implement authentication. Although it is not strictly required to work at small scale, it is **strongly** recommended that the email of each account be able to be looked up quickly in a case-insensitive manner. With a SQL database, this can be accomplished by adding an index on the lowercase of the email. The email is also the primary key.
///**Note that when this struct is cloned it should refer to the same datastore. This struct is cloned like a database pool is in normal Actix.**
#[async_trait]
pub trait DataProvider: Clone {
    type AccountType: Account;

    ///Adds a new account to the database, then returns that account back. You may need to clone the account when implementing this function. If another account exists with this email, then the function should return some sort of error.
    async fn insert_account(
        &self,
        account: Self::AccountType,
        password_hash: String,
    ) -> ResponseResult<Self::AccountType>;

    /// Fetches the account with the given email from the database, case-insensitively. Note that a lowercase email should be passed to this function, but the matching email as stored in the database may be in any case.
    async fn fetch_account(
        &self,
        email: &str,
    ) -> ResponseResult<Option<(Self::AccountType, String)>>;
}

///The non-error outcomes of registration. Error outcomes are used when a genuine error takes place — e.g. the database is not reachable (represented by the functions on your DataProvider implementation returning an error).
pub enum RegistrationOutcome<AccountType: Account> {
    ///The account is now in the database, and is given here.
    Successful(AccountType),
    ///The provided email is not a valid email
    InvalidEmail,
    //The provided email is already taken
    EmailTaken,
}

///The non-error outcomes of logging in. Error outcomes are used when a genuine error takes place — e.g. the database is not reachable (represented by the functions on your DataProvider implementation returning an error).
pub enum LoginOutcome<AccountType> {
    ///The credentials were correct, so the account  and a cookie that should be set in the response to the login route are provided.
    Successful(AccountType, Cookie<'static>),
    //The provided credentials do not correspond to a valid account.
    InvalidEmailOrPassword,
}

//from https://emailregex.com/
static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
    Regex::new(
        r###"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"###,
    ).expect("Failed to parse email regex")
});

#[derive(Debug, Serialize, Deserialize)]
struct JsonWebTokenClaims<T> {
    exp: usize,
    account: T,
}

///A clone of this struct is provided to each App instance in Actix as Data, thus providing access to the authentication system in each route.
#[derive(Clone)]
pub struct AuthenticationProvider<DataProviderImpl: DataProvider> {
    provider: DataProviderImpl,
    jwt_encoding_key: EncodingKey,
    jwt_secret: Vec<u8>,
}

impl<DataProviderImpl: DataProvider> AuthenticationProvider<DataProviderImpl> {
    ///Creates a new AuthenticationProvider with the provided jwt_secret and data provider. The jwt secret is used to sign and verify the json web tokens, so it should be secret, long enough to be secure, and persistent over a period of days. Changing this token will invalidate all current sessions, but they may not be cleanly logged out if you set your own cookies in addition to the token.
    pub fn new(provider: DataProviderImpl, jwt_secret: Vec<u8>) -> Self {
        Self {
            provider,
            jwt_encoding_key: EncodingKey::from_secret(&jwt_secret),
            jwt_secret,
        }
    }

    ///Registers the provided account with the provided password. See the documentation on RegistrationOutcome for details on what to do next.
    /// ```rust,ignore
    /// #[post("/register")]
    /// async fn register(auth: Data<ExampleAuthProvider>, dto: Json<RegistrationDto>) -> Response {
    ///     let dto = dto.into_inner();
    ///     Ok(
    ///         match auth.register(
    ///             ExampleAccount {
    ///                 username: dto.username,
    ///                 email: dto.email,
    ///             },
    ///             &dto.password,
    ///         )? {
    ///             RegistrationOutcome::Successful(_account) => {
    ///                 HttpResponse::Ok()
    ///                     .json(RegistrationResponseDto {
    ///                         succeeded: true,
    ///                         message: None,
    ///                     })
    ///                     .await?
    ///             }
    ///             RegistrationOutcome::InvalidEmail => {
    ///                 HttpResponse::Ok()
    ///                     .json(RegistrationResponseDto {
    ///                         succeeded: false,
    ///                         message: Some("Invalid Email".into()),
    ///                     })
    ///                     .await?
    ///             }
    ///             RegistrationOutcome::EmailTaken => {
    ///                 HttpResponse::Ok()
    ///                     .json(RegistrationResponseDto {
    ///                         succeeded: false,
    ///                         message: Some("Email is already taken".into()),
    ///                     })
    ///                     .await?
    ///             }
    ///         },
    ///     )
    /// }
    /// ```
    pub async fn register(
        &self,
        account: DataProviderImpl::AccountType,
        password: &str,
    ) -> ResponseResult<RegistrationOutcome<DataProviderImpl::AccountType>> {
        let lowercase_email = account.email().to_ascii_lowercase();

        //verify that email is a valid email
        if !EMAIL_REGEX.is_match(&lowercase_email) {
            return Ok(RegistrationOutcome::InvalidEmail);
        }

        //check for existing account with same username
        if let Some(_) = self.provider.fetch_account(&lowercase_email).await? {
            return Ok(RegistrationOutcome::EmailTaken);
        }

        //hash password
        let salt = SaltString::generate(&mut OsRng);
        let argon2 = Argon2::default();
        let hash = argon2
            .hash_password_simple(password.as_bytes(), salt.as_ref())?
            .to_string();

        //insert new account
        let account = self.provider.insert_account(account, hash).await?;

        return Ok(RegistrationOutcome::Successful(account));
    }

    ///Attempts to login to the specified account. See the documentation on LoginOutcome for details on what to do next.
    /// ```rust,ignore
    /// #[post("/login")]
    /// async fn login(auth: Data<ExampleAuthProvider>, dto: Json<LoginDto>) -> Response {
    ///     Ok(match auth.login(&dto.email, &dto.password)? {
    ///         LoginOutcome::Successful(account, cookie) => {
    ///             HttpResponse::Ok()
    ///                 .cookie(CookieBuilder::new("username", account.username).finish()) //this is how you make information available to your frontend, note that anything in your account type is visible to users as it is encoded as a JWT!!!!!
    ///                 .cookie(cookie)
    ///                 .json(LoginResponseDto {
    ///                     succeeded: true,
    ///                     message: None,
    ///                 })
    ///                 .await?
    ///         }
    ///         LoginOutcome::InvalidEmailOrPassword => {
    ///             HttpResponse::Ok()
    ///                 .json(LoginResponseDto {
    ///                     succeeded: false,
    ///                     message: Some("Invalid username or password".into()),
    ///                 })
    ///                 .await?
    ///         }
    ///     })
    /// }
    /// ```
    pub async fn login(
        &self,
        email: &str,
        password: &str,
    ) -> ResponseResult<LoginOutcome<DataProviderImpl::AccountType>> {
        //get account & verify exists
        let (account, hash_string) = match self
            .provider
            .fetch_account(&email.to_ascii_lowercase())
            .await?
        {
            Some(account) => account,
            None => return Ok(LoginOutcome::InvalidEmailOrPassword),
        };

        //check password
        let argon2 = Argon2::default();
        let password_hash = PasswordHash::new(&hash_string)?;
        if !argon2
            .verify_password(password.as_bytes(), &password_hash)
            .is_ok()
        {
            return Ok(LoginOutcome::InvalidEmailOrPassword);
        }

        //issue token
        let claims = JsonWebTokenClaims {
            account,
            exp: current_unix_time_secs() as usize + 24 * 3600, //expire 24 hours after issue
        };
        let token = encode(&Header::default(), &claims, &self.jwt_encoding_key)?;

        //create cookie
        let cookie = CookieBuilder::new("actix-plus-auth-token", token)
            .secure(true)
            .http_only(true)
            .path("/")
            .same_site(SameSite::Strict)
            .finish();

        Ok(LoginOutcome::Successful(claims.account, cookie))
    }

    ///Gets the current user if a valid session is present on the provided HTTP request, otherwise returns a ResponseResult that when propagated with the actix-plus-error crate causes Actix web to return 401 Not Authorized.
    /// ```rust,ignore
    /// #[get("/private_page")]
    /// async fn private_page(request: HttpRequest, auth: Data<ExampleAuthProvider>) -> Response {
    ///     let account = auth.current_user(&request)?;
    ///     Ok(HttpResponse::Ok()
    ///         .body(format!("Hello {}", account.username))
    ///         .await?)
    /// }
    /// ```
    pub fn current_user(
        &self,
        request: &HttpRequest,
    ) -> ResponseResult<DataProviderImpl::AccountType> {
        //check for cookie
        let cookie: Cookie<'static> = match request.cookie("actix-plus-auth-token") {
            Some(cookie) => cookie,
            None => {
                return Err(ResponseError::StatusCodeError {
                    message: "Unauthorized".into(),
                    code: StatusCode::UNAUTHORIZED,
                })
            }
        };

        //check token
        let token = match decode::<JsonWebTokenClaims<DataProviderImpl::AccountType>>(
            &cookie.value(),
            &DecodingKey::from_secret(&self.jwt_secret),
            &Validation::default(),
        ) {
            Ok(token) => token,
            Err(_) => {
                return Err(ResponseError::StatusCodeError {
                    message: "Unauthorized".into(),
                    code: StatusCode::UNAUTHORIZED,
                })
            }
        };

        //return user if token is valid
        Ok(token.claims.account)
    }
}