115 lines
2.8 KiB
Rust
115 lines
2.8 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)]
|
|
pub enum AuthError {
|
|
InvalidCredentials,
|
|
ServerError(String),
|
|
}
|
|
|
|
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 {
|
|
Self::ServerError(e.to_string())
|
|
}
|
|
}
|