create totp authenticator
This commit is contained in:
parent
68627b0c92
commit
4180bdd162
|
@ -354,6 +354,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base32"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
|
@ -650,6 +656,12 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -2902,6 +2914,22 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "totp-rs"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3504f96adf86d28e7eb16fa236a7951ec72c15ee100d1b5318e225944bc8cb"
|
||||
dependencies = [
|
||||
"base32",
|
||||
"constant_time_eq 0.2.6",
|
||||
"hmac",
|
||||
"rand",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"url",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
|
@ -2957,6 +2985,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.5",
|
||||
"totp-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3037,6 +3066,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -3417,7 +3452,7 @@ dependencies = [
|
|||
"aes",
|
||||
"byteorder",
|
||||
"bzip2",
|
||||
"constant_time_eq",
|
||||
"constant_time_eq 0.1.5",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
|
|
|
@ -25,3 +25,4 @@ diesel_migrations = "2"
|
|||
bb8 = "0.8"
|
||||
rand = "0.8"
|
||||
mail-send = "0.4"
|
||||
totp-rs = { version = "5.4", features = ["gen_secret", "otpauth"] }
|
|
@ -61,36 +61,61 @@ macro_rules! auth {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! enforce {
|
||||
(sess $i:expr) => {
|
||||
(sess $i:expr) => {{
|
||||
if $i.session_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!("ERR_UNAUTHORIZED", "unauthorized")
|
||||
)
|
||||
}
|
||||
};
|
||||
(auth $i:expr) => {
|
||||
$i.session_token.unwrap()
|
||||
}};
|
||||
(auth $i:expr) => {{
|
||||
if $i.auth_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!("ERR_UNAUTHORIZED", "unauthorized")
|
||||
$crate::make_err!(
|
||||
"ERR_2FA_REQUIRED",
|
||||
"must provide a valid 2FA token to access this endpoint"
|
||||
)
|
||||
)
|
||||
}
|
||||
};
|
||||
(sess auth $i:expr) => {
|
||||
if $i.session_token.is_none() || $i.auth_token.is_none() {
|
||||
$i.auth_token.unwrap()
|
||||
}};
|
||||
(sess auth $i:expr) => {{
|
||||
if $i.session_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!("ERR_UNAUTHORIZED", "unauthorized")
|
||||
)
|
||||
}
|
||||
};
|
||||
(auth sess $i:expr) => {
|
||||
if $i.session_token.is_none() || $i.auth_token.is_none() {
|
||||
if $i.auth_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!(
|
||||
"ERR_2FA_REQUIRED",
|
||||
"must provide a valid 2FA token to access this endpoint"
|
||||
)
|
||||
)
|
||||
}
|
||||
($i.session_token.unwrap(), $i.auth_token.unwrap())
|
||||
}};
|
||||
(auth sess $i:expr) => {{
|
||||
if $i.session_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!("ERR_UNAUTHORIZED", "unauthorized")
|
||||
)
|
||||
}
|
||||
};
|
||||
if $i.auth_token.is_none() {
|
||||
$crate::err!(
|
||||
actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
$crate::make_err!(
|
||||
"ERR_2FA_REQUIRED",
|
||||
"must provide a valid 2FA token to access this endpoint"
|
||||
)
|
||||
)
|
||||
}
|
||||
($i.session_token.unwrap(), $i.auth_token.unwrap())
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ async fn main() {
|
|||
.service(routes::v1::signup::signup_req)
|
||||
.service(routes::v1::auth::verify_magic_link::verify_link_req)
|
||||
.service(routes::v1::auth::magic_link::login_req)
|
||||
.service(routes::v1::totp_authenticators::create_totp_auth_req)
|
||||
.wrap(Logger::default())
|
||||
.wrap(actix_cors::Cors::permissive())
|
||||
.app_data(app_state.clone())
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::models::TotpAuthenticator;
|
||||
use crate::response::JsonAPIResponse;
|
||||
use crate::{auth, enforce, AppState};
|
||||
use crate::{auth, enforce, randid, AppState};
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::{post, HttpRequest};
|
||||
use diesel::ExpressionMethods;
|
||||
|
@ -7,6 +8,10 @@ use diesel::QueryDsl;
|
|||
use diesel::SelectableHelper;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use totp_rs::{Algorithm, Secret, TOTP};
|
||||
use crate::schema::totp_authenticators;
|
||||
use crate::schema::users;
|
||||
use crate::models::User;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct TotpAuthenticatorReq {}
|
||||
|
@ -28,16 +33,50 @@ pub struct TotpAuthResp {
|
|||
pub metadata: TotpAuthRespMeta,
|
||||
}
|
||||
|
||||
#[post("/v1/auth/totp-authenticators")]
|
||||
pub async fn totp_auth_req(
|
||||
req: Json<TotpAuthenticatorReq>,
|
||||
#[post("/v1/totp-authenticators")]
|
||||
pub async fn create_totp_auth_req(
|
||||
_req: Json<TotpAuthenticatorReq>,
|
||||
state: Data<AppState>,
|
||||
req_info: HttpRequest,
|
||||
) -> JsonAPIResponse<TotpAuthResp> {
|
||||
let mut conn = handle_error!(state.pool.get().await);
|
||||
|
||||
let auth_info = auth!(req_info, conn);
|
||||
enforce!(sess auth_info);
|
||||
let session_token = enforce!(sess auth_info);
|
||||
|
||||
todo!()
|
||||
let users_vec = handle_error!(
|
||||
users::dsl::users
|
||||
.filter(users::dsl::id.eq(&session_token.user_id))
|
||||
.select(User::as_select())
|
||||
.load(&mut conn)
|
||||
.await
|
||||
);
|
||||
let user = handle_error!(users_vec.get(0).ok_or("impossible relation"));
|
||||
|
||||
let secret = Secret::generate_secret();
|
||||
|
||||
let totp = handle_error!(TOTP::new(Algorithm::SHA1, 6, 1, 30, handle_error!(secret.to_bytes()), Some("Trifid".to_string()), user.email.clone()));
|
||||
|
||||
let new_totp_authenticator = TotpAuthenticator {
|
||||
id: randid!(id "totp"),
|
||||
user_id: session_token.user_id,
|
||||
secret: secret.to_encoded().to_string(),
|
||||
verified: false
|
||||
};
|
||||
|
||||
handle_error!(
|
||||
diesel::insert_into(totp_authenticators::table)
|
||||
.values(&new_totp_authenticator)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
);
|
||||
|
||||
ok!(TotpAuthResp {
|
||||
data: TotpAuthRespData {
|
||||
totp_token: new_totp_authenticator.id.clone(),
|
||||
secret: new_totp_authenticator.secret.clone(),
|
||||
url: totp.get_url()
|
||||
},
|
||||
metadata: TotpAuthRespMeta {}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue