Moved JWT config to config file
parent
3cf7661faf
commit
2cc4d53961
6
Rb.yaml
6
Rb.yaml
|
@ -12,7 +12,11 @@ debug:
|
||||||
|
|
||||||
admin_user: "admin"
|
admin_user: "admin"
|
||||||
admin_pass: "password"
|
admin_pass: "password"
|
||||||
jwt_key: "secret"
|
jwt:
|
||||||
|
key: "secret"
|
||||||
|
refresh_token_size: 64
|
||||||
|
# Just 5 seconds for debugging
|
||||||
|
refresh_token_expire: 5
|
||||||
|
|
||||||
databases:
|
databases:
|
||||||
postgres_rb:
|
postgres_rb:
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
||||||
db::{tokens::NewRefreshToken, users::User},
|
db::{tokens::NewRefreshToken, users::User},
|
||||||
errors::{RbError, RbResult},
|
errors::{RbError, RbResult},
|
||||||
schema::{refresh_tokens::dsl as refresh_tokens, users::dsl as users},
|
schema::{refresh_tokens::dsl as refresh_tokens, users::dsl as users},
|
||||||
|
RbJwtConf,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -30,10 +31,13 @@ pub struct Claims
|
||||||
pub exp: i64,
|
pub exp: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> RbResult<JWTResponse>
|
pub fn generate_jwt_token(
|
||||||
|
conn: &PgConnection,
|
||||||
|
jwt: &RbJwtConf,
|
||||||
|
user: &User,
|
||||||
|
) -> RbResult<JWTResponse>
|
||||||
{
|
{
|
||||||
let secret = std::env::var("JWT_KEY").map_err(|_| RbError::Custom("Missing JWT key."))?;
|
let key: Hmac<Sha256> = Hmac::new_from_slice(jwt.key.as_bytes())
|
||||||
let key: Hmac<Sha256> = Hmac::new_from_slice(secret.as_bytes())
|
|
||||||
.map_err(|_| RbError::Custom("Couldn't create Hmac key."))?;
|
.map_err(|_| RbError::Custom("Couldn't create Hmac key."))?;
|
||||||
|
|
||||||
let current_time = Utc::now();
|
let current_time = Utc::now();
|
||||||
|
@ -43,7 +47,7 @@ pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> RbResult<JWTRespo
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username.clone(),
|
username: user.username.clone(),
|
||||||
admin: user.admin,
|
admin: user.admin,
|
||||||
exp: current_time.timestamp() + crate::JWT_EXP_SECONDS,
|
exp: current_time.timestamp() + jwt.refresh_token_expire,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sign the claims into a new token
|
// Sign the claims into a new token
|
||||||
|
@ -52,11 +56,11 @@ pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> RbResult<JWTRespo
|
||||||
.map_err(|_| RbError::Custom("Couldn't sign JWT."))?;
|
.map_err(|_| RbError::Custom("Couldn't sign JWT."))?;
|
||||||
|
|
||||||
// Generate a random refresh token
|
// Generate a random refresh token
|
||||||
let mut refresh_token = [0u8; crate::REFRESH_TOKEN_N_BYTES];
|
let mut refresh_token = vec![0u8; jwt.refresh_token_size];
|
||||||
thread_rng().fill(&mut refresh_token[..]);
|
thread_rng().fill(&mut refresh_token[..]);
|
||||||
|
|
||||||
let refresh_expire =
|
let refresh_expire =
|
||||||
(current_time + chrono::Duration::seconds(crate::REFRESH_TOKEN_EXP_SECONDS)).naive_utc();
|
(current_time + chrono::Duration::seconds(jwt.refresh_token_expire)).naive_utc();
|
||||||
|
|
||||||
// Store refresh token in database
|
// Store refresh token in database
|
||||||
db::tokens::create(
|
db::tokens::create(
|
||||||
|
@ -74,7 +78,11 @@ pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> RbResult<JWTRespo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> RbResult<JWTResponse>
|
pub fn refresh_token(
|
||||||
|
conn: &PgConnection,
|
||||||
|
jwt: &RbJwtConf,
|
||||||
|
refresh_token: &str,
|
||||||
|
) -> RbResult<JWTResponse>
|
||||||
{
|
{
|
||||||
let token_bytes =
|
let token_bytes =
|
||||||
base64::decode(refresh_token).map_err(|_| RbError::AuthInvalidRefreshToken)?;
|
base64::decode(refresh_token).map_err(|_| RbError::AuthInvalidRefreshToken)?;
|
||||||
|
@ -108,5 +116,5 @@ pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> RbResult<JWTRe
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map_err(|_| RbError::Custom("Couldn't update last used time."))?;
|
.map_err(|_| RbError::Custom("Couldn't update last used time."))?;
|
||||||
|
|
||||||
generate_jwt_token(conn, &user)
|
generate_jwt_token(conn, jwt, &user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use rocket::serde::json::Json;
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
jwt::{generate_jwt_token, JWTResponse},
|
jwt::{generate_jwt_token, JWTResponse},
|
||||||
pass::verify_user,
|
pass::verify_user,
|
||||||
};
|
};
|
||||||
use crate::{errors::RbResult, guards::User, RbDbConn};
|
use crate::{errors::RbResult, guards::User, RbConfig, RbDbConn, RbJwtConf};
|
||||||
|
|
||||||
pub mod jwt;
|
pub mod jwt;
|
||||||
pub mod pass;
|
pub mod pass;
|
||||||
|
@ -24,16 +24,24 @@ pub async fn already_logged_in(_user: User) -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<credentials>", rank = 2)]
|
#[post("/login", data = "<credentials>", rank = 2)]
|
||||||
pub async fn login(conn: RbDbConn, credentials: Json<Credentials>) -> RbResult<Json<JWTResponse>>
|
pub async fn login(
|
||||||
|
conn: RbDbConn,
|
||||||
|
conf: &State<RbConfig>,
|
||||||
|
credentials: Json<Credentials>,
|
||||||
|
) -> RbResult<Json<JWTResponse>>
|
||||||
{
|
{
|
||||||
let credentials = credentials.into_inner();
|
let credentials = credentials.into_inner();
|
||||||
|
let jwt = conf.jwt.clone();
|
||||||
|
|
||||||
// Get the user, if credentials are valid
|
// Get the user, if credentials are valid
|
||||||
let user = conn
|
let user = conn
|
||||||
.run(move |c| verify_user(c, &credentials.username, &credentials.password))
|
.run(move |c| verify_user(c, &credentials.username, &credentials.password))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(conn.run(move |c| generate_jwt_token(c, &user)).await?))
|
Ok(Json(
|
||||||
|
conn.run(move |c| generate_jwt_token(c, &jwt, &user))
|
||||||
|
.await?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -46,13 +54,15 @@ pub struct RefreshTokenRequest
|
||||||
#[post("/refresh", data = "<refresh_token_request>")]
|
#[post("/refresh", data = "<refresh_token_request>")]
|
||||||
pub async fn refresh_token(
|
pub async fn refresh_token(
|
||||||
conn: RbDbConn,
|
conn: RbDbConn,
|
||||||
|
conf: &State<RbConfig>,
|
||||||
refresh_token_request: Json<RefreshTokenRequest>,
|
refresh_token_request: Json<RefreshTokenRequest>,
|
||||||
) -> RbResult<Json<JWTResponse>>
|
) -> RbResult<Json<JWTResponse>>
|
||||||
{
|
{
|
||||||
let refresh_token = refresh_token_request.into_inner().refresh_token;
|
let refresh_token = refresh_token_request.into_inner().refresh_token;
|
||||||
|
let jwt = conf.jwt.clone();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
conn.run(move |c| crate::auth::jwt::refresh_token(c, &refresh_token))
|
conn.run(move |c| crate::auth::jwt::refresh_token(c, &jwt, &refresh_token))
|
||||||
.await?,
|
.await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
31
src/main.rs
31
src/main.rs
|
@ -8,9 +8,13 @@ extern crate diesel_migrations;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
|
|
||||||
|
use figment::{
|
||||||
|
providers::{Env, Format, Yaml},
|
||||||
|
Figment,
|
||||||
|
};
|
||||||
use rocket::{fairing::AdHoc, Build, Rocket};
|
use rocket::{fairing::AdHoc, Build, Rocket};
|
||||||
use rocket_sync_db_pools::database;
|
use rocket_sync_db_pools::database;
|
||||||
use figment::{Figment, providers::Env, providers::Yaml, providers::Format};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod admin;
|
mod admin;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
@ -19,14 +23,6 @@ pub mod errors;
|
||||||
pub mod guards;
|
pub mod guards;
|
||||||
pub(crate) mod schema;
|
pub(crate) mod schema;
|
||||||
|
|
||||||
// Any import defaults are defined here
|
|
||||||
/// Expire time for the JWT tokens in seconds.
|
|
||||||
const JWT_EXP_SECONDS: i64 = 600;
|
|
||||||
/// Amount of bytes the refresh tokens should consist of
|
|
||||||
const REFRESH_TOKEN_N_BYTES: usize = 64;
|
|
||||||
/// Expire time for refresh tokens; here: one week
|
|
||||||
const REFRESH_TOKEN_EXP_SECONDS: i64 = 604800;
|
|
||||||
|
|
||||||
#[database("postgres_rb")]
|
#[database("postgres_rb")]
|
||||||
pub struct RbDbConn(diesel::PgConnection);
|
pub struct RbDbConn(diesel::PgConnection);
|
||||||
|
|
||||||
|
@ -61,6 +57,22 @@ async fn create_admin_user(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocke
|
||||||
Ok(rocket)
|
Ok(rocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct RbJwtConf
|
||||||
|
{
|
||||||
|
key: String,
|
||||||
|
refresh_token_size: usize,
|
||||||
|
refresh_token_expire: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct RbConfig
|
||||||
|
{
|
||||||
|
admin_user: String,
|
||||||
|
admin_pass: String,
|
||||||
|
jwt: RbJwtConf,
|
||||||
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _
|
fn rocket() -> _
|
||||||
{
|
{
|
||||||
|
@ -75,6 +87,7 @@ fn rocket() -> _
|
||||||
run_db_migrations,
|
run_db_migrations,
|
||||||
))
|
))
|
||||||
.attach(AdHoc::try_on_ignite("Create admin user", create_admin_user))
|
.attach(AdHoc::try_on_ignite("Create admin user", create_admin_user))
|
||||||
|
.attach(AdHoc::config::<RbConfig>())
|
||||||
.mount(
|
.mount(
|
||||||
"/api/auth",
|
"/api/auth",
|
||||||
routes![auth::already_logged_in, auth::login, auth::refresh_token,],
|
routes![auth::already_logged_in, auth::login, auth::refresh_token,],
|
||||||
|
|
Reference in New Issue