* Refactored secret_value generation to be behind an easy to use helper

* Refactored serde facing puller functions to be more clear
- Removing incorrect commentary
* Changed login handler to start using mysql based api
! Further refactoring in the previous to be more readable
This commit is contained in:
shockrah 2020-12-28 22:00:59 -08:00
parent 9d01912670
commit 5366ba9690

View File

@ -29,7 +29,7 @@ impl Claim {
.checked_add_signed(Duration::weeks(1)) .checked_add_signed(Duration::weeks(1))
.expect("Couldn't generate an expirey date") .expect("Couldn't generate an expirey date")
.timestamp(), .timestamp(),
cookie: generate_secret() cookie: generate_cookie()
} }
} }
} }
@ -69,16 +69,25 @@ fn valid_perms(member: Member, path: &str) -> bool {
} }
} }
fn rng_secret(length: usize) -> String {
use getrandom::getrandom;
use base64::{encode_config, URL_SAFE};
let mut buf: Vec<u8> = vec![0;length];
getrandom(&mut buf).unwrap();
encode_config(buf,URL_SAFE)
}
pub fn generate_secret() -> String { pub fn generate_secret() -> String {
/* /*
* Generates a url-safe-plaintext secret for our db * Generates a url-safe-plaintext secret for our db
* */ * */
use getrandom::getrandom; return rng_secret(64);
use base64::{encode_config, URL_SAFE}; }
let mut buf: Vec<u8> = vec![0;64]; pub fn generate_cookie() -> String {
getrandom(&mut buf).unwrap(); return rng_secret(32)
encode_config(buf,URL_SAFE)
} }
pub fn encrypt_secret(raw: &str) -> BcryptResult<String> { pub fn encrypt_secret(raw: &str) -> BcryptResult<String> {
@ -86,51 +95,37 @@ pub fn encrypt_secret(raw: &str) -> BcryptResult<String> {
return bcrypt::hash(raw, BCRYPT_COST); return bcrypt::hash(raw, BCRYPT_COST);
} }
fn get_jwt_json(params: &serde_json::Value) -> Option<&str> { fn jwt_from_serde(params: &serde_json::Value) -> Option<&str> {
// gets the `token` from the parameters // gets the `token` from the parameters
// option<value> -> some(value) -> string // option<value> -> some(value) -> string
return params.get("token")?.as_str(); return params.get("token")?.as_str();
} }
async fn valid_jwt(token: &str) -> AuthReason { async fn valid_jwt(p: &Pool, token: &str) -> AuthReason {
use jsonwebtoken::{ use jsonwebtoken::{
decode, DecodingKey, decode, DecodingKey,
Validation, Algorithm Validation, Algorithm
}; };
// TODO: add a blacklist in redis to make sure we don't ever accidently authenticate a bad
// token
// NOTE: for now we're doing purely stateless validation with a bs key
// crypto things that should prolly not fail assuming we're configured correctly // crypto things that should prolly not fail assuming we're configured correctly
let algo = Algorithm::HS512; let algo = Algorithm::HS512;
let dk = DecodingKey::from_base64_secret(&HMAC_SECRET).unwrap(); let dk = DecodingKey::from_base64_secret(&HMAC_SECRET).unwrap();
let raw = decode::<Claim>(token, &dk, &Validation::new(algo)); if let Ok(decoded) = decode::<Claim>(token, &dk, &Validation::new(algo)) {
// if the decoding worked then check on the redis cache for the jwt // subject used for querying speed NOT security
// recall we have the id as a lookup but it is mapped to a session-id let listed = db::auth::listed_jwt(p, decoded.claims.sub, token).await.unwrap();
// that mapping should be the same as the temporary usermapping let active = Utc::now().timestamp() < decoded.claims.exp;
// Additionally: invalidating a session id is as easy as just making a new new for the user
if raw.is_err() { return match listed && active {
true => AuthReason::Good,
false => AuthReason::BadKey
};
}
else {
return AuthReason::BadKey; return AuthReason::BadKey;
} }
let raw = raw.unwrap();
let id = raw.claims.sub;
let sesh_id = raw.claims.cookie;
return match db::auth::active_jwt(id, &sesh_id).await {
Ok(active) => {
match active {
true => AuthReason::Good,
false => AuthReason::BadKey
}
},
Err(err) => {
AuthReason::ServerIssue(format!("{}", err))
}
};
} }
fn get_login(params: &serde_json::Value) -> Option<(db::UBigInt, &str)> { fn login_params_from_serde(params: &serde_json::Value) -> Option<(db::UBigInt, &str)> {
let id_v = params.get("id"); let id_v = params.get("id");
let secret_v = params.get("secret"); let secret_v = params.get("secret");
return match (id_v, secret_v) { return match (id_v, secret_v) {
@ -153,7 +148,7 @@ pub async fn wall_entry<'path, 'pool, 'params>(
// Dont need to auth if it's not required // Dont need to auth if it's not required
let open_path = routes::is_open(path); let open_path = routes::is_open(path);
let jwt = get_jwt_json(params); let jwt = jwt_from_serde(params);
if open_path { // ignore the parameters since they're irelevant if open_path { // ignore the parameters since they're irelevant
return AuthReason::OpenAuth; return AuthReason::OpenAuth;
@ -161,9 +156,9 @@ pub async fn wall_entry<'path, 'pool, 'params>(
if let Some(jwt) = jwt { if let Some(jwt) = jwt {
// get the headers here // get the headers here
return valid_jwt(jwt).await; return valid_jwt(pool, jwt).await;
} }
if let Some((id, secret)) = get_login(params) { if let Some((id, secret)) = login_params_from_serde(params) {
// Last chance we might be hitting the /login route so we have to do the heavy auth flow // Last chance we might be hitting the /login route so we have to do the heavy auth flow
if path != routes::AUTH_LOGIN { if path != routes::AUTH_LOGIN {
@ -188,7 +183,7 @@ pub async fn wall_entry<'path, 'pool, 'params>(
return AuthReason::NoKey; return AuthReason::NoKey;
} }
pub async fn login_get_jwt(response: &mut hyper::Response<hyper::Body>, params: serde_json::Value) { pub async fn login_get_jwt(p: &Pool, response: &mut hyper::Response<hyper::Body>, params: serde_json::Value) {
// basically this route generates a jwt for the user and returns via the jwt key // basically this route generates a jwt for the user and returns via the jwt key
// in the json response // in the json response
use jsonwebtoken::{ use jsonwebtoken::{
@ -199,24 +194,28 @@ pub async fn login_get_jwt(response: &mut hyper::Response<hyper::Body>, params:
let id = params.get("id").unwrap().as_u64().unwrap(); // only route where we have the "id is there guarantee" let id = params.get("id").unwrap().as_u64().unwrap(); // only route where we have the "id is there guarantee"
let claim = Claim::new(id); let claim = Claim::new(id);
let header = Header::new(Algorithm::HS512); let header = Header::new(Algorithm::HS512);
println!("{:?}-{:?}", header, claim);
let encoded = encode( let encoded = encode(
&header, &header,
&claim, &claim,
&EncodingKey::from_base64_secret(HMAC_SECRET.as_ref()).expect("Couldn't encode from secret")) &EncodingKey::from_base64_secret(HMAC_SECRET.as_ref()).expect("Couldn't encode from secret"))
.expect("Could not encode JWT"); .expect("Could not encode JWT");
if let Ok(_) = db::auth::add_jwt(id, &encoded).await { match db::auth::add_jwt(p, encoded.as_ref()).await {
response.headers_mut().insert("Content-Type", Ok(_) => {
response.headers_mut().insert("Content-Type",
HeaderValue::from_static("application/json")); HeaderValue::from_static("application/json"));
let payload = serde_json::json!({ let payload = serde_json::json!({
"jwt": encoded "jwt": encoded
}); });
*response.body_mut() = hyper::Body::from(payload.to_string()); *response.body_mut() = hyper::Body::from(payload.to_string());
} },
else { Err(e) => {
*response.status_mut() = hyper::StatusCode::INTERNAL_SERVER_ERROR; eprintln!("{}", e);
} *response.status_mut() = hyper::StatusCode::INTERNAL_SERVER_ERROR;
}
};
} }