Step by step examples to teach you Authentication.

Learn Authentication implementation via these examples.

1. constantoine/totp-rs

RFC-compliant TOTP implementation with ease of use as a goal and additionnal QoL features..

Authentication Tutorial

This library permits the creation of 2FA authentification tokens per TOTP, the verification of said tokens, with configurable time skew, validity time of each token, algorithm and number of digits! Default features are kept as lightweight as possible to ensure small binaries and short compilation time.

It now supports parsing otpauth URLs into a totp object, with sane default values.

Be aware that some authenticator apps will accept the SHA256 and SHA512 algorithms but silently fallback to SHA1 which will make the check() function fail due to mismatched algorithms.

Features

qr

With optional feature "qr", you can use it to generate a base64 png qrcode. This will enable feature otpauth.
otpauth

otpauth

With optional feature "otpauth", support parsing the TOTP parameters from an otpauth URL, and generating an otpauth URL. It adds 2 fields to TOTP.
otpauth
otpauth

serde_support

With optional feature "serde_support", library-defined types TOTP and Algorithm and will be Deserialize-able and Serialize-able.

gen_secret

With optional feature "gen_secret", a secret will be generated for you to store in database.

Examples

Understanding Secret

This new type was added as a disambiguation between Raw and already base32 encoded secrets.

Secret::Raw("TestSecretSuperSecret".as_bytes().to_vec())

Is equivalent to

Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string())

Generate a token

Add it to your Cargo.toml:

[dependencies]
totp-rs = "^3.0"

You can then do something like:

use std::time::SystemTime;
use totp_rs::{Algorithm, TOTP, Secret};

fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::Raw("TestSecretSuperSecret".as_bytes().to_vec()).to_bytes().unwrap(),
    ).unwrap();
    let token = totp.generate_current().unwrap();
    println!("{}", token);   
}

Which is equivalent to:

use std::time::SystemTime;
use totp_rs::{Algorithm, TOTP, Secret};

fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string()).to_bytes().unwrap(),
    ).unwrap();
    let token = totp.generate_current().unwrap();
    println!("{}", token);   
}

With qrcode generation

Add it to your Cargo.toml:

[dependencies.totp-rs]
version = "^3.0"
features = ["qr"]

You can then do something like:

use totp_rs::{Algorithm, TOTP, Secret};

fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string()).to_bytes().unwrap(),
        Some("Github".to_string()),
        "constantoine@github.com".to_string(),
    ).unwrap();
    let code = totp.get_qr()?;
    println!("{}", code);   
}

With serde support

Add it to your Cargo.toml:

[dependencies.totp-rs]
version = "^3.0"
features = ["serde_support"]

With otpauth url support

Add it to your Cargo.toml:

[dependencies.totp-rs]
version = "^3.0"
features = ["otpauth"]

You can then do something like:

use totp_rs::TOTP;

fn main() {
    let otpauth = "otpauth://totp/GitHub:constantoine@github.com?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&issuer=GitHub";
    let totp = TOTP::from_url(otpauth).unwrap();
    println!("{}", totp.generate_current().unwrap());
}

With gen_secret

Add it to your Cargo.toml:

[dependencies.totp-rs]
version = "^3.0"
features = ["gen_secret"]

You can then do something like:

use totp_rs::{Algorithm, TOTP, Secret};

fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::default().to_bytes().unwrap(),
        Some("Github".to_string()),
        "constantoine@github.com".to_string(),
    ).unwrap();
    let code = totp.get_qr()?;
    println!("{}", code);   
}

Which is equivalent to

use totp_rs::{Algorithm, TOTP, Secret};

fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::generate_secret().to_bytes().unwrap(),
        Some("Github".to_string()),
        "constantoine@github.com".to_string(),
    ).unwrap();
    let code = totp.get_qr()?;
    println!("{}", code);   
}

With RFC-6238 compliant default

You can do something like this

use totp_rs::{Algorithm, TOTP, Secret, Rfc6238};

fn main () {
    let mut rfc = Rfc6238::with_defaults(
            Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string()).to_bytes().unwrap(),
        )
        .unwrap();

    // optional, set digits
    rfc.digits(8).unwrap();

    // create a TOTP from rfc
    let totp = TOTP::from_rfc6238(rfc).unwrap();
    let code = totp.generate_current().unwrap();
    println!("code: {}", code);
}

With gen_secret feature, you can go even further and have all values by default and a secure secret.
gen_secret

Note: With otpauth feature, TOTP.issuer will be None, and TOTP.account_name will be "". Be sure to set those fields before generating an URL/QRCode
otpauth

fn main() {
    let totp = TOTP::default();
    println!("code: {}", code);
}

Full Example

Let us look at a full Example.

Step 1. Write Code

Below are the example codes:

(a). gen_secret.rs

#[cfg(all(feature = "gen_secret", feature = "otpauth"))]
use totp_rs::{Algorithm, Secret, TOTP};

#[cfg(all(feature = "gen_secret", feature = "otpauth"))]
fn main() {
    let secret = Secret::generate_secret();

    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        secret.to_bytes().unwrap(),
        None,
        "account".to_string(),
    )
    .unwrap();

    println!(
        "secret raw: {} ; secret base32 {} ; code: {}",
        secret,
        secret.to_encoded(),
        totp.generate_current().unwrap()
    )
}

#[cfg(not(all(feature = "gen_secret", feature = "otpauth")))]
fn main() {}

(b). rfc-6238.rs

use totp_rs::{Rfc6238, TOTP};

#[cfg(feature = "otpauth")]
fn main() {
    let mut rfc = Rfc6238::with_defaults("totp-sercret-123").unwrap();

    // optional, set digits, issuer, account_name
    rfc.digits(8).unwrap();
    rfc.issuer("issuer".to_string());
    rfc.account_name("user-account".to_string());

    // create a TOTP from rfc
    let totp = TOTP::from_rfc6238(rfc).unwrap();
    let code = totp.generate_current().unwrap();
    println!("code: {}", code);
}

#[cfg(not(feature = "otpauth"))]
fn main() {
    let mut rfc = Rfc6238::with_defaults("totp-sercret-123").unwrap();

    // optional, set digits, issuer, account_name
    rfc.digits(8).unwrap();

    // create a TOTP from rfc
    let totp = TOTP::from_rfc6238(rfc).unwrap();
    let code = totp.generate_current().unwrap();
    println!("code: {}", code);
}

(c). secret.rs

use totp_rs::{Algorithm, Secret, TOTP};

#[cfg(feature = "otpauth")]
fn main() {
    // create TOTP from base32 secret
    let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
    let totp_b32 = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        secret_b32.to_bytes().unwrap(),
        Some("issuer".to_string()),
        "user-account".to_string(),
    )
    .unwrap();

    println!(
        "base32 {} ; raw {}",
        secret_b32,
        secret_b32.to_raw().unwrap()
    );
    println!(
        "code from base32:t{}",
        totp_b32.generate_current().unwrap()
    );

    // create TOTP from raw binary value
    let secret = [
        0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x65,
        0x63, 0x72, 0x65, 0x74, 0x2d, 0x31, 0x32, 0x33,
    ];
    let secret_raw = Secret::Raw(secret.to_vec());
    let totp_raw = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        secret_raw.to_bytes().unwrap(),
        Some("issuer".to_string()),
        "user-account".to_string(),
    )
    .unwrap();

    println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded());
    println!(
        "code from raw secret:t{}",
        totp_raw.generate_current().unwrap()
    );
}

#[cfg(not(feature = "otpauth"))]
fn main() {
    // create TOTP from base32 secret
    let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
    let totp_b32 = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_b32.to_bytes().unwrap()).unwrap();

    println!(
        "base32 {} ; raw {}",
        secret_b32,
        secret_b32.to_raw().unwrap()
    );
    println!(
        "code from base32:t{}",
        totp_b32.generate_current().unwrap()
    );

    // create TOTP from raw binary value
    let secret = [
        0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x65,
        0x63, 0x72, 0x65, 0x74, 0x2d, 0x31, 0x32, 0x33,
    ];
    let secret_raw = Secret::Raw(secret.to_vec());
    let totp_raw = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_raw.to_bytes().unwrap()).unwrap();

    println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded());
    println!(
        "code from raw secret:t{}",
        totp_raw.generate_current().unwrap()
    );
}

(d). ttl.rs

use totp_rs::{Algorithm, TOTP};

#[cfg(not(feature = "otpauth"))]
fn main() {
    let totp = TOTP::new(Algorithm::SHA1, 6, 1, 30, "my-secret".to_string()).unwrap();

    loop {
        println!(
            "code {}t ttl {}t valid until: {}",
            totp.generate_current().unwrap(),
            totp.ttl().unwrap(),
            totp.next_step_current().unwrap()
        );
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
}

#[cfg(feature = "otpauth")]
fn main() {
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        "my-secret".to_string(),
        Some("Github".to_string()),
        "constantoine@github.com".to_string(),
    )
    .unwrap();

    loop {
        println!(
            "code {}t ttl {}t valid until: {}",
            totp.generate_current().unwrap(),
            totp.ttl().unwrap(),
            totp.next_step_current().unwrap()
        );
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
}

Reference

Download the code below:

No. Link
1. Download Full Code
2. Read more here.
3. Follow code author here.

Read More.


More

Here are some more examples related to Authentication.

Categorized in: