totp auth work pt2
/ build (push) Successful in 47s Details
/ build_x64 (push) Successful in 2m6s Details
/ build_arm64 (push) Successful in 2m32s Details
/ build_win64 (push) Successful in 2m35s Details

This commit is contained in:
core 2023-11-22 22:50:20 -05:00
parent 19332e519b
commit 2a5a2bb910
Signed by: core
GPG Key ID: FDBF740DADDCEECF
6 changed files with 25 additions and 9 deletions

View File

@ -1,7 +1,10 @@
CREATE TABLE totp_authenticators CREATE TABLE totp_authenticators
( (
id VARCHAR NOT NULL PRIMARY KEY, id VARCHAR NOT NULL PRIMARY KEY,
user_id VARCHAR NOT NULL REFERENCES users(id), user_id VARCHAR NOT NULL REFERENCES users (id) ON DELETE CASCADE,
secret VARCHAR NOT NULL, secret VARCHAR NOT NULL,
verified BOOLEAN NOT NULL verified BOOLEAN NOT NULL,
name VARCHAR NOT NULL,
created_at TIMESTAMP NOT NULL,
last_seen_at TIMESTAMP NOT NULL
); );

View File

@ -44,6 +44,9 @@ pub struct TotpAuthenticator {
pub user_id: String, pub user_id: String,
pub secret: String, pub secret: String,
pub verified: bool, pub verified: bool,
pub name: String,
pub created_at: SystemTime,
pub last_seen_at: SystemTime
} }
#[derive( #[derive(

View File

@ -8,7 +8,7 @@ use crate::response::JsonAPIResponse;
use diesel::{QueryDsl, ExpressionMethods, SelectableHelper, BelongingToDsl}; use diesel::{QueryDsl, ExpressionMethods, SelectableHelper, BelongingToDsl};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use totp_rs::{Algorithm, Secret, TOTP}; use totp_rs::{Algorithm, Secret, TOTP};
use crate::schema::{auth_tokens, users}; use crate::schema::{auth_tokens, users, totp_authenticators};
use crate::models::{AuthToken, TotpAuthenticator, User}; use crate::models::{AuthToken, TotpAuthenticator, User};
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -42,19 +42,22 @@ pub async fn totp_req(req: Json<TotpAuthReq>, state: Data<AppState>, req_info: H
let authenticators: Vec<TotpAuthenticator> = handle_error!(TotpAuthenticator::belonging_to(&user).load::<TotpAuthenticator>(&mut conn).await); let authenticators: Vec<TotpAuthenticator> = handle_error!(TotpAuthenticator::belonging_to(&user).load::<TotpAuthenticator>(&mut conn).await);
let mut found_valid_code = false; let mut found_valid_code = false;
let mut chosen_auther = None;
for totp_auther in authenticators { for totp_auther in authenticators {
if totp_auther.verified { if totp_auther.verified {
let secret = Secret::Encoded(totp_auther.secret); let secret = Secret::Encoded(totp_auther.secret.clone());
let totp_machine = handle_error!(TOTP::new(Algorithm::SHA1, 6, 1, 30, handle_error!(secret.to_bytes()), Some("Trifid".to_string()), user.email.clone())); let totp_machine = handle_error!(TOTP::new(Algorithm::SHA1, 6, 1, 30, handle_error!(secret.to_bytes()), Some("Trifid".to_string()), user.email.clone()));
let is_valid = handle_error!(totp_machine.check_current(&req.code)); let is_valid = handle_error!(totp_machine.check_current(&req.code));
if is_valid { found_valid_code = true; break; } if is_valid { found_valid_code = true; chosen_auther = Some(totp_auther); break; }
} }
} }
if !found_valid_code { if !found_valid_code {
err!(StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "unauthorized")); err!(StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "unauthorized"));
} }
handle_error!(diesel::update(&(chosen_auther.unwrap())).set(totp_authenticators::dsl::last_seen_at.eq(SystemTime::now())).execute(&mut conn).await);
// issue auth token // issue auth token
let new_token = AuthToken { let new_token = AuthToken {

View File

@ -12,6 +12,7 @@ use totp_rs::{Algorithm, Secret, TOTP};
use crate::schema::totp_authenticators; use crate::schema::totp_authenticators;
use crate::schema::users; use crate::schema::users;
use crate::models::User; use crate::models::User;
use std::time::SystemTime;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct TotpAuthenticatorReq {} pub struct TotpAuthenticatorReq {}
@ -54,7 +55,10 @@ pub async fn create_totp_auth_req(
id: randid!(id "totp"), id: randid!(id "totp"),
user_id: session_token.user_id, user_id: session_token.user_id,
secret: secret.to_encoded().to_string(), secret: secret.to_encoded().to_string(),
verified: false verified: false,
name: "".to_string(),
created_at: SystemTime::now(),
last_seen_at: SystemTime::now()
}; };
handle_error!( handle_error!(

View File

@ -74,7 +74,7 @@ pub async fn verify_totp_req(req: Json<VerifyTotpAuthReq>, state: Data<AppState>
err!(StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "unauthorized")); err!(StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "unauthorized"));
} }
handle_error!(diesel::update(&authenticator).set(totp_authenticators::dsl::verified.eq(true)).execute(&mut conn).await); handle_error!(diesel::update(&authenticator).set((totp_authenticators::dsl::verified.eq(true), totp_authenticators::dsl::last_seen_at.eq(SystemTime::now()))).execute(&mut conn).await);
// issue auth token // issue auth token

View File

@ -30,6 +30,9 @@ diesel::table! {
user_id -> Varchar, user_id -> Varchar,
secret -> Varchar, secret -> Varchar,
verified -> Bool, verified -> Bool,
name -> Varchar,
created_at -> Timestamp,
last_seen_at -> Timestamp,
} }
} }