flabk/flabk/src/svc/auth.rs

125 lines
3.0 KiB
Rust

use std::time::SystemTime;
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation};
use serde::{Deserialize, Serialize};
use tower_cookies::Cookie;
use crate::{
database::{
db::DBError,
keys::Keys,
users::{self, UserSelect, Users},
},
sec,
};
#[derive(Clone)]
pub struct Auth {
secret: String,
users: Users,
}
const KEY_JWT_SECRET: &str = "JWT_SECRET";
const SECS_JWT_EXPIRE: u64 = 60 * 60; // 1hr
impl Auth {
pub async fn new(db: Keys, users: Users) -> Self {
Self {
secret: match db.get_key(KEY_JWT_SECRET).await {
Ok(secret) => secret,
Err(_) => {
// Create new secret and store to db
// If that fails, crash the application
let secret = sec::new_id();
db.set_key(KEY_JWT_SECRET, &secret).await.unwrap();
secret
}
},
users,
}
}
pub async fn login(&self, username: String, password: String) -> Result<String, AuthError> {
let user = self.users.user(UserSelect::Username(username)).await?;
if !sec::compare(&password, &user.password_hash) {
return Err(AuthError::InvalidCredentials);
}
Ok(jsonwebtoken::encode(
&Header::default(),
&Claims::from(user),
&EncodingKey::from_secret(self.secret.as_ref()),
)?)
}
pub fn get_claims(&self, token: String) -> Result<Claims, AuthError> {
Ok(jsonwebtoken::decode::<Claims>(
token.as_str(),
&DecodingKey::from_secret(self.secret.as_ref()),
&Validation::new(Algorithm::HS256),
)?
.claims)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub username: String,
pub exp: u64,
pub iat: u64,
}
impl Claims {
pub fn expired(&self) -> bool {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
>= self.exp
}
}
impl From<users::User> for Claims {
fn from(u: users::User) -> Self {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
Claims {
sub: u.id,
username: u.username,
exp: now + SECS_JWT_EXPIRE,
iat: now,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AuthError {
InvalidCredentials,
Expired,
ServerError(String),
}
impl AuthError {
pub fn expired(&self) -> bool {
*self == Self::Expired
}
}
impl From<DBError> for AuthError {
fn from(_: DBError) -> Self {
Self::InvalidCredentials
}
}
impl From<jsonwebtoken::errors::Error> for AuthError {
fn from(e: jsonwebtoken::errors::Error) -> Self {
match e.kind() {
jsonwebtoken::errors::ErrorKind::ExpiredSignature => Self::Expired,
kind => Self::ServerError(e.to_string()),
}
}
}