diff --git a/trifid-api/migrations/2023-11-20-034741_create_totp_authenticators/up.sql b/trifid-api/migrations/2023-11-20-034741_create_totp_authenticators/up.sql index aa7c7fd..4a68122 100644 --- a/trifid-api/migrations/2023-11-20-034741_create_totp_authenticators/up.sql +++ b/trifid-api/migrations/2023-11-20-034741_create_totp_authenticators/up.sql @@ -1,7 +1,10 @@ CREATE TABLE totp_authenticators ( - id VARCHAR NOT NULL PRIMARY KEY, - user_id VARCHAR NOT NULL REFERENCES users(id), - secret VARCHAR NOT NULL, - verified BOOLEAN NOT NULL + id VARCHAR NOT NULL PRIMARY KEY, + user_id VARCHAR NOT NULL REFERENCES users (id) ON DELETE CASCADE, + secret VARCHAR NOT NULL, + verified BOOLEAN NOT NULL, + name VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL, + last_seen_at TIMESTAMP NOT NULL ); \ No newline at end of file diff --git a/trifid-api/src/models.rs b/trifid-api/src/models.rs index a2e5ea4..612db29 100644 --- a/trifid-api/src/models.rs +++ b/trifid-api/src/models.rs @@ -44,6 +44,9 @@ pub struct TotpAuthenticator { pub user_id: String, pub secret: String, pub verified: bool, + pub name: String, + pub created_at: SystemTime, + pub last_seen_at: SystemTime } #[derive( diff --git a/trifid-api/src/routes/v1/auth/totp.rs b/trifid-api/src/routes/v1/auth/totp.rs index 4a7355a..6b6ac10 100644 --- a/trifid-api/src/routes/v1/auth/totp.rs +++ b/trifid-api/src/routes/v1/auth/totp.rs @@ -8,7 +8,7 @@ use crate::response::JsonAPIResponse; use diesel::{QueryDsl, ExpressionMethods, SelectableHelper, BelongingToDsl}; use diesel_async::RunQueryDsl; 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}; #[derive(Deserialize, Debug)] @@ -42,19 +42,22 @@ pub async fn totp_req(req: Json, state: Data, req_info: H let authenticators: Vec = handle_error!(TotpAuthenticator::belonging_to(&user).load::(&mut conn).await); let mut found_valid_code = false; + let mut chosen_auther = None; for totp_auther in authenticators { 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 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 { 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 let new_token = AuthToken { diff --git a/trifid-api/src/routes/v1/totp_authenticators.rs b/trifid-api/src/routes/v1/totp_authenticators.rs index d692092..7dcac85 100644 --- a/trifid-api/src/routes/v1/totp_authenticators.rs +++ b/trifid-api/src/routes/v1/totp_authenticators.rs @@ -12,6 +12,7 @@ use totp_rs::{Algorithm, Secret, TOTP}; use crate::schema::totp_authenticators; use crate::schema::users; use crate::models::User; +use std::time::SystemTime; #[derive(Deserialize)] pub struct TotpAuthenticatorReq {} @@ -54,7 +55,10 @@ pub async fn create_totp_auth_req( id: randid!(id "totp"), user_id: session_token.user_id, secret: secret.to_encoded().to_string(), - verified: false + verified: false, + name: "".to_string(), + created_at: SystemTime::now(), + last_seen_at: SystemTime::now() }; handle_error!( diff --git a/trifid-api/src/routes/v1/verify_totp_authenticator.rs b/trifid-api/src/routes/v1/verify_totp_authenticator.rs index 8069d37..bb6c853 100644 --- a/trifid-api/src/routes/v1/verify_totp_authenticator.rs +++ b/trifid-api/src/routes/v1/verify_totp_authenticator.rs @@ -74,7 +74,7 @@ pub async fn verify_totp_req(req: Json, state: Data 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 diff --git a/trifid-api/src/schema.rs b/trifid-api/src/schema.rs index 0e22283..d6cc5b6 100644 --- a/trifid-api/src/schema.rs +++ b/trifid-api/src/schema.rs @@ -30,6 +30,9 @@ diesel::table! { user_id -> Varchar, secret -> Varchar, verified -> Bool, + name -> Varchar, + created_at -> Timestamp, + last_seen_at -> Timestamp, } }