Integrated the new authentication into Actix
This commit is contained in:
parent
a7a0ea60d5
commit
b34a06387f
5 changed files with 28 additions and 57 deletions
|
@ -1,12 +1,8 @@
|
|||
use jsonwebtoken::{
|
||||
decode, encode, errors::Result as JwtResult, DecodingKey, EncodingKey, Header, Validation,
|
||||
};
|
||||
use log::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const DAYS_VALID: i64 = 7;
|
||||
const JWT_SECRET: &[u8] = "secret".as_bytes();
|
||||
|
||||
/// Claims holds the data that will be encoded into the JWT token.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
|
@ -33,6 +29,7 @@ impl Claims {
|
|||
|
||||
/// Authentication holds the data needed to encode and decode JWT tokens.
|
||||
// This is then passed to the AuthenticationMiddleware
|
||||
#[derive(Clone)]
|
||||
pub struct Authentication {
|
||||
encoding_key: EncodingKey,
|
||||
decoding_key: DecodingKey,
|
||||
|
@ -42,7 +39,7 @@ pub struct Authentication {
|
|||
|
||||
impl Authentication {
|
||||
/// Create a new Authentication struct
|
||||
fn new(secret: &[u8]) -> Self {
|
||||
pub fn new(secret: &[u8]) -> Self {
|
||||
Authentication {
|
||||
encoding_key: EncodingKey::from_secret(secret),
|
||||
decoding_key: DecodingKey::from_secret(secret),
|
||||
|
@ -57,58 +54,18 @@ impl Authentication {
|
|||
}
|
||||
|
||||
/// Wrapper for encode_raw that takes a username (sub) and creates a Claims struct
|
||||
fn encode(&self, sub: &str) -> JwtResult<String> {
|
||||
pub fn encode(&self, sub: &str) -> JwtResult<String> {
|
||||
let claims = Claims::new(sub, self.days_valid);
|
||||
self.encode_raw(claims)
|
||||
}
|
||||
|
||||
/// Decode a JWT token into a Claims struct
|
||||
// If this faie, it means the token is invalid
|
||||
fn decode(&self, token: &str) -> JwtResult<Claims> {
|
||||
pub fn decode(&self, token: &str) -> JwtResult<Claims> {
|
||||
decode::<Claims>(token, &self.decoding_key, &self.validation).map(|data| data.claims)
|
||||
}
|
||||
}
|
||||
|
||||
// JwtResult is just a predefined error from the jsonwebtoken crate
|
||||
pub fn token_factory(user: &str) -> JwtResult<String> {
|
||||
info!("Issuing JWT token for {}", user);
|
||||
|
||||
let token = encode(
|
||||
&Header::default(),
|
||||
&Claims {
|
||||
sub: user.to_string(),
|
||||
iss: "frostbyte".to_string(),
|
||||
iat: chrono::Utc::now().timestamp() as usize,
|
||||
exp: (chrono::Utc::now() + chrono::Duration::days(DAYS_VALID)).timestamp() as usize,
|
||||
},
|
||||
&EncodingKey::from_secret(JWT_SECRET),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
// JwtResult is just a predefined error from the jsonwebtoken crate
|
||||
// This function is incomplete and should be expanded to check for more things
|
||||
pub fn validate_token(token: &str) -> JwtResult<Claims> {
|
||||
let token_data = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(JWT_SECRET),
|
||||
&Validation::default(),
|
||||
);
|
||||
|
||||
match token_data {
|
||||
Ok(token_data) => {
|
||||
info!("Token validated for {}", token_data.claims.sub);
|
||||
Ok(token_data.claims)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Token validation failed: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -12,6 +12,7 @@ mod state;
|
|||
mod types;
|
||||
mod util;
|
||||
|
||||
use jwt::Authentication;
|
||||
use routes::{get_comments, get_posts, login, new_comment, new_post, post_by_id, register};
|
||||
use state::CaptchaState;
|
||||
use state::ServerState;
|
||||
|
@ -23,6 +24,7 @@ async fn main() -> std::io::Result<()> {
|
|||
|
||||
let data = ServerState::new().await;
|
||||
let capt_db = CaptchaState::new();
|
||||
let auth = Authentication::new("secret".as_bytes());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -56,7 +58,8 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(login)
|
||||
.service(register)
|
||||
.app_data(Data::new(data.clone()))
|
||||
.app_data(Data::new(capt_db.clone())),
|
||||
.app_data(Data::new(capt_db.clone()))
|
||||
.app_data(Data::new(auth.clone())),
|
||||
)
|
||||
.service(
|
||||
Files::new("/", "./public")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::db::{db_get_comments, db_new_comment};
|
||||
use crate::jwt::validate_token;
|
||||
use crate::jwt::Authentication;
|
||||
use crate::types::{CommentQueryParams, NewComment};
|
||||
use crate::ServerState;
|
||||
|
||||
|
@ -31,8 +31,9 @@ pub async fn get_comments(
|
|||
pub async fn new_comment(
|
||||
data: Json<NewComment>,
|
||||
state: Data<ServerState>,
|
||||
auth: Data<Authentication>,
|
||||
) -> Result<impl Responder> {
|
||||
let user_claims = validate_token(&data.user_token);
|
||||
let user_claims = auth.decode(&data.user_token);
|
||||
|
||||
// Bail if the token is invalid
|
||||
if let Err(e) = user_claims {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::db::{db_get_latest_posts, db_get_post, db_new_post};
|
||||
use crate::jwt::validate_token;
|
||||
use crate::jwt::Authentication;
|
||||
use crate::types::{NewPost, PostQueryParams};
|
||||
use crate::ServerState;
|
||||
|
||||
|
@ -23,8 +23,12 @@ pub async fn get_posts(
|
|||
|
||||
/// Creates a new post, requires a token in release mode
|
||||
#[post("/posts")]
|
||||
pub async fn new_post(new_post: Json<NewPost>, state: Data<ServerState>) -> Result<impl Responder> {
|
||||
let user_claims = validate_token(&new_post.token);
|
||||
pub async fn new_post(
|
||||
new_post: Json<NewPost>,
|
||||
state: Data<ServerState>,
|
||||
auth: Data<Authentication>,
|
||||
) -> Result<impl Responder> {
|
||||
let user_claims = auth.decode(&new_post.token);
|
||||
|
||||
if let Err(e) = user_claims {
|
||||
info!("Error validating token: {}", e);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::db::{db_new_user, db_user_login};
|
||||
use crate::jwt::token_factory;
|
||||
use crate::jwt::Authentication;
|
||||
// use crate::jwt::token_factory;
|
||||
use crate::state::CaptchaState;
|
||||
use crate::types::{AuthResponse, LoginData, RegisterData};
|
||||
use crate::ServerState;
|
||||
|
@ -14,6 +15,7 @@ pub async fn register(
|
|||
data: Json<RegisterData>,
|
||||
state: Data<ServerState>,
|
||||
captcha_state: Data<CaptchaState>,
|
||||
auth: Data<Authentication>,
|
||||
) -> Result<impl Responder> {
|
||||
if !captcha_state
|
||||
.capthca_db
|
||||
|
@ -30,7 +32,7 @@ pub async fn register(
|
|||
info!("User: {} registered", &user.username);
|
||||
Ok(HttpResponse::Ok().json(AuthResponse {
|
||||
username: user.username.clone(),
|
||||
token: token_factory(&user.username).unwrap(),
|
||||
token: auth.encode(&user.username).unwrap(),
|
||||
}))
|
||||
}
|
||||
None => {
|
||||
|
@ -41,14 +43,18 @@ pub async fn register(
|
|||
}
|
||||
|
||||
#[post("/login")]
|
||||
pub async fn login(data: Json<LoginData>, state: Data<ServerState>) -> Result<impl Responder> {
|
||||
pub async fn login(
|
||||
data: Json<LoginData>,
|
||||
state: Data<ServerState>,
|
||||
auth: Data<Authentication>,
|
||||
) -> Result<impl Responder> {
|
||||
let result = db_user_login(data.username.clone(), data.password.clone(), &state.pool).await;
|
||||
|
||||
match result {
|
||||
Some(_) => {
|
||||
return Ok(HttpResponse::Ok().json(AuthResponse {
|
||||
username: data.username.clone(),
|
||||
token: token_factory(&data.username).unwrap(),
|
||||
token: auth.encode(&data.username).unwrap(),
|
||||
}));
|
||||
}
|
||||
None => {
|
||||
|
|
Loading…
Reference in a new issue