Skip to content

Commit 336e3a4

Browse files
CodeF0xTobias Oettl
and
Tobias Oettl
authored
make bearer tokens great again (#33)
* proper bearer token * fix: formatting --------- Co-authored-by: Tobias Oettl <tobias.oettl@hetzner-cloud.de>
1 parent 8707461 commit 336e3a4

File tree

3 files changed

+90
-12
lines changed

3 files changed

+90
-12
lines changed

src/main.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use hmac::Hmac;
3535
use jwt::SignWithKey;
3636
use sha2::Sha256;
3737
use std::io::Result;
38+
use std::time::{SystemTime, UNIX_EPOCH};
3839

3940
#[actix_web::main]
4041
async fn main() -> Result<()> {
@@ -222,8 +223,14 @@ async fn login(payload: Json<LoginPayload>) -> impl Responder {
222223
.unwrap();
223224

224225
if is_valid {
226+
let expiry_date = SystemTime::now()
227+
.duration_since(UNIX_EPOCH)
228+
.unwrap()
229+
.as_millis()
230+
+ 24 * 60 * 60 * 1000;
225231
let claims = TokenClaims {
226232
id: database_user.id,
233+
expiry_date,
227234
};
228235
let token_str = claims.sign_with_key(&jwt_secret).unwrap();
229236

@@ -816,27 +823,89 @@ mod tests {
816823
use crate::database::DataBase;
817824
use crate::get_tickets;
818825
use crate::middleware::validator;
819-
use crate::models::NewSession;
826+
use crate::models::{NewSession, TokenClaims};
820827
use crate::schema::sessions::dsl::sessions;
821828
use actix_web::http::StatusCode;
822829
use actix_web::{test, web};
823830
use actix_web_httpauth::middleware::HttpAuthentication;
824831
use diesel::RunQueryDsl;
832+
use hmac::{Hmac, Mac};
833+
use jwt::SignWithKey;
825834
use serial_test::serial;
835+
use sha2::Sha256;
836+
use std::time::{SystemTime, UNIX_EPOCH};
837+
838+
#[actix_web::test]
839+
#[serial]
840+
async fn test_expiry_date() {
841+
setup_database();
842+
843+
let jwt_secret: Hmac<Sha256> = Hmac::new_from_slice(
844+
std::env::var("JWT_SECRET")
845+
.expect("JWT_SECRET must be set!")
846+
.as_bytes(),
847+
)
848+
.unwrap();
849+
850+
let expiry_date = SystemTime::now()
851+
.duration_since(UNIX_EPOCH)
852+
.unwrap()
853+
.as_millis()
854+
// 24 hours in present
855+
- 24 * 60 * 60 * 1000;
856+
let claims = TokenClaims { id: 1, expiry_date };
857+
let token_str = claims.sign_with_key(&jwt_secret).unwrap();
858+
859+
let mut db = DataBase::new();
860+
diesel::insert_into(sessions)
861+
.values(NewSession {
862+
token: token_str.clone(),
863+
})
864+
.execute(&mut db.connection)
865+
.unwrap();
866+
867+
let app = test::init_service(
868+
App::new().service(
869+
web::scope("")
870+
.wrap(HttpAuthentication::bearer(validator))
871+
.service(get_tickets),
872+
),
873+
)
874+
.await;
875+
let req = TestRequest::get()
876+
.uri("/tickets")
877+
.insert_header(("Authorization", format!("Bearer {token_str}")))
878+
.to_request();
879+
880+
let response = test::call_service(&app, req).await;
881+
882+
assert_eq!(response.status().as_u16(), StatusCode::UNAUTHORIZED);
883+
}
826884

827885
#[actix_web::test]
828886
#[serial]
829887
async fn test_middleware() {
830888
setup_database();
831889

832-
// bearer for "123"
833-
let bearer_token =
834-
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.oi92tHHWj5HdQO8Hd9vIYD6suTWosoiBnpdRBIcNGpM";
890+
let jwt_secret: Hmac<Sha256> = Hmac::new_from_slice(
891+
std::env::var("JWT_SECRET")
892+
.expect("JWT_SECRET must be set!")
893+
.as_bytes(),
894+
)
895+
.unwrap();
896+
897+
let expiry_date = SystemTime::now()
898+
.duration_since(UNIX_EPOCH)
899+
.unwrap()
900+
.as_millis()
901+
+ 24 * 60 * 60 * 60 * 1000;
902+
let claims = TokenClaims { id: 1, expiry_date };
903+
let token_str = claims.sign_with_key(&jwt_secret).unwrap();
835904

836905
let mut db = DataBase::new();
837906
diesel::insert_into(sessions)
838907
.values(NewSession {
839-
token: bearer_token.to_string(),
908+
token: token_str.clone(),
840909
})
841910
.execute(&mut db.connection)
842911
.unwrap();
@@ -851,7 +920,7 @@ mod tests {
851920
.await;
852921
let req = TestRequest::get()
853922
.uri("/tickets")
854-
.insert_header(("Authorization", format!("Bearer {bearer_token}")))
923+
.insert_header(("Authorization", format!("Bearer {token_str}")))
855924
.to_request();
856925

857926
let response = test::call_service(&app, req).await;

src/middleware.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use crate::database::{session_in_db, DataBase};
22
use crate::models::TokenClaims;
33
use actix_web::dev::ServiceRequest;
44
use actix_web::{Error, HttpMessage};
5-
use actix_web_httpauth::extractors::bearer::BearerAuth;
5+
use actix_web_httpauth::extractors::bearer::{BearerAuth, Config};
66
use actix_web_httpauth::extractors::{bearer, AuthenticationError};
77
use dotenvy::dotenv;
88
use hmac::digest::KeyInit;
99
use hmac::Hmac;
1010
use jwt::VerifyWithKey;
1111
use sha2::Sha256;
12+
use std::time::{SystemTime, UNIX_EPOCH};
1213

1314
pub async fn validator(
1415
req: ServiceRequest,
@@ -38,15 +39,22 @@ pub async fn validator(
3839

3940
match claims {
4041
Ok(value) => {
42+
if value.expiry_date
43+
< SystemTime::now()
44+
.duration_since(UNIX_EPOCH)
45+
.unwrap()
46+
.as_millis()
47+
{
48+
let config = Config::default().scope("");
49+
50+
return Err((AuthenticationError::from(config).into(), req));
51+
}
52+
4153
req.extensions_mut().insert(value);
4254
Ok(req)
4355
}
4456
Err(_) => {
45-
let config = req
46-
.app_data::<bearer::Config>()
47-
.cloned()
48-
.unwrap_or_default()
49-
.scope("");
57+
let config = Config::default().scope("");
5058

5159
Err((AuthenticationError::from(config).into(), req))
5260
}

src/models.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub struct NewUser {
113113
#[derive(Serialize, Deserialize)]
114114
pub struct TokenClaims {
115115
pub id: i32,
116+
pub expiry_date: u128,
116117
}
117118

118119
#[derive(Serialize, Deserialize, Queryable, Debug)]

0 commit comments

Comments
 (0)