2021/11/23
[Rust][TOTP]Rustで2段階認証を実装する
概要
TOTP(Time-Based One Time Password)ベースの2段階認証をサイトに追加することがあったので
調査して実装しました。
Google AuthenticatorとかAuthyとかで利用できるよくあるやつです。
google-authenticatorという便利なライブラリがあったので、そちらを利用しています。
実装方法
install
Cargo toml
google-authenticator = "0.3.0"
once_cell = "1.8.0" # authenticatorのsingleton instance生成用
percent-encoding = "2.1.0" # qrcode生成用のurl文字列生成用
rust code
ユーザーに表示させたい時
use google_authenticator::GoogleAuthenticator;
use once_cell::sync::OnceCell;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
static GA_AUTH: OnceCell<GoogleAuthenticator> = OnceCell::new();
fn ga_auth() -> &'static GoogleAuthenticator {
GA_AUTH.get_or_init(|| GoogleAuthenticator::new())
}
pub fn generate_otp_secret() -> String {
// random base32
ga_auth().create_secret(32)
}
pub fn build_otp_url(otp_secret: &str, account_name: &str, issuer_name: &str) -> String {
let account_name = utf8_percent_encode(account_name, NON_ALPHANUMERIC);
let issuer_name = utf8_percent_encode(issuer_name, NON_ALPHANUMERIC);
format!(
"otpauth://totp/{}?secret={}&issuer={}",
account_name, otp_secret, issuer_name
)
}
otp_secret
がsecretです。これはユーザーが設定する毎に生成して、DBに保存して利用します。otp_url
QRcodeなどでアプリに読ませる情報ですotpauth://totp/your-app-name:user-email%40gmail.com?secret=BASE32STRING&issuer=your-app-name
みたいなURL文字列です
認証用
pub fn verify_otp_code(otp_secret: &str, otp_code: &str) -> bool {
ga_auth().verify_code(otp_secret, otp_code, 3, 0)
}
test code
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_otp() {
let otp_secret = generate_otp_secret();
assert_eq!(otp_secret.len(), 32);
let otp_url = build_otp_url(otp_secret.as_str(), "[email protected]", "test-app");
let expected_url = format!(
"otpauth://totp/test%40example%2Ecom?secret={}&issuer=test%2Dapp",
otp_secret
);
assert_eq!(otp_url, expected_url);
let otp_code = ga_auth().get_code(otp_secret.as_str(), 0).unwrap();
assert!(verify_otp_code(otp_secret.as_str(), otp_code.as_str()));
let otp_code = "000000".to_owned();
assert!(!verify_otp_code(otp_secret.as_str(), otp_code.as_str()));
}
}
以上になります。
関連する記事
Concordiumノードをローカルで動かしてみた
Concordiumの調査のために、ローカルでソースコードをビルドしてノードを動かしてみました
[Rust]axumとdragonflyを使ったWebsocket Chatのサンプル実装
redis互換のdragonflyをPUBSUBとして利用して、Websocket Chatアプリのサンプル実装を行いました。
[Rust]TiDBを使ったサンプルアプリケーションの実装
RustからTiDBを使ったアプリケーションの実装を行いました。
[Rust]Google Cloud Storageを利用する
GCSやNFSのファイルを扱えるpackageをRustで実装しました。