A ruby library for generating and validating one time passwords (HOTP & TOTP) according to RFC 4226 and RFC 6238.
POTP aims to be compatible with Google Authenticator.
The Base32 format conforms to RFC 4648 Base32
sudo gem install potpIf you like to run the executable (instead of writing a one-liner for
yourself), you have to install the appl gem.
sudo gem install applrequire "potp"
totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
totp.now #=> "152201"
totp.verify "152201" #=> 1735417500 # ok, value is the timestamp
sleep 30
totp.verify "152201" #=> nil # not okhotp = POTP::HOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
hotp.at 0 #=> "178748"
hotp.at 1 #=> "584373"
hotp.at 73 #=> "309764"
# OTP verifying with a counter
hotp.verify "309764", 73 #=> 73
hotp.verify "309764", 74 #=> nil
hotp.verify "309764", 70, retries: 2 #=> nil
hotp.verify "309764", 70, retries: 3 #=> 73require "potp"
totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
code = totp.now #=> "054626"
last_verify = totp.verify code #=> 1735527390
totp.verify code, after: last_verify #=> nil
sleep 30
code = totp.now #=> "481150"
totp.verify code, after: last_verify #=> 1735527420In case a user entered a code just after it has expired, you can allow the token to remain valid.
totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
now = Time.now - 30
code = totp.at now #=> "455335"
totp.verify code #=> nil
totp.verify code, drift_behind: 27 #=> 1735530510Returns a 160 bit (32 character) Base32 secret.
require "potp/random"
POTP::Base32.random #=> "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"require "potp"
totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
uri = totp.provisioning_uri name: "[email protected]", issuer: "ACME Service"
#=> "otpauth://totp/ACME%20Service:jdoe%40example.net?secret=GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A&issuer=ACME%20Service"
hotp = POTP::HOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
uri = hotp.provisioning_uri name: "[email protected]", issuer: "ACME Service", counter: 0
#=> "otpauth://hotp/ACME%20Service:jdoe%40example.net?secret=GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A&issuer=ACME%20Service&counter=0"
# Then, do something like this:
system *%w(qrencode -t xpm -s 1 -o), "qr.xpm", uriGenerates a time-based one-time password:
potp --secret GYS5L3N3E4AAYNMN562LW76TMWHQBJ4AGenerates a counter-based one-time password:
potp --hmac --secret GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A --counter 42What you expect:
potp --help- (C) 2025 Bertram Scharpf [email protected]
- License: BSD-2-Clause+