Skip to content

Instantly share code, notes, and snippets.

@pablotron
Created September 15, 2016 21:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pablotron/89544a840211a8412ff7bfe0f22694aa to your computer and use it in GitHub Desktop.
Save pablotron/89544a840211a8412ff7bfe0f22694aa to your computer and use it in GitHub Desktop.
ROTP and Google Authenticator Example
# install dependency
pabs@meh:~/git/test/rotp> sudo gem install rotp
# generate new secret and QR code URL
pabs@meh:~/git/test/rotp> ruby ./rotp-example.rb new asdf@example.com
QR Code URL: https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2FAlonzo%27s%2520Totally%2520Secure%2520App%3Aasdf%40example.com%3Fsecret%3Dr7eslax2okb2dkr4%26issuer%3DAlonzo%2527s%2BTotally%2BSecure%2BApp
Secret: r7eslax2okb2dkr4
Provisioning URL: otpauth://totp/Alonzo's%20Totally%20Secure%20App:asdf@example.com?secret=r7eslax2okb2dkr4&issuer=Alonzo%27s+Totally+Secure+App
# at this point i used the QR Code URL above to configure Google Authenticator on my phone
# if this was a real application, it would store the secret along with the email in a database here
# then the "510461" code below came from my phone
# test with correct code
pabs@meh:~/git/test/rotp> ruby ./rotp-example.rb test r7eslax2okb2dkr4 510461
Result: success
# test with incorrect code
pabs@meh:~/git/test/rotp> ruby ./rotp-example.rb test r7eslax2okb2dkr4 123356
Result: failure
#!/usr/bin/env ruby
require 'rotp'
require 'erb'
# require 'optparse'
module ROTPExample
APP_NAME = "Alonzo's Totally Secure App"
TEMPLATES = {
qr: %{
https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=%s
}.strip,
new: %{
QR Code URL: %s
Secret: %s
Provisioning URL: %s
}.strip,
usage: %{
Usage: %s [ new EMAIL | test SECRET CODE ]
Examples:
# create new shared secret for hello@example.com
%s new hello@example.com
# test code
%s test
}.strip,
}
class QRCode
def initialize(data)
@data = data
end
def to_s
TEMPLATES[:qr] % [ERB::Util.url_encode(@data)]
end
end
class Generator
def initialize(secret, email, app_name = APP_NAME)
@secret, @email, @app_name = secret, email, app_name
# generate provisioning uri
@uri = ROTP::TOTP.new(@secret, issuer: @app_name).provisioning_uri(@email)
end
def to_s
TEMPLATES[:new] % [QRCode.new(@uri), @secret, @uri]
end
end
def self.print_usage(app)
$stderr.puts TEMPLATES[:usage] % ([app] * 3)
exit -1
end
def self.run(app, args)
print_usage(app) unless args.length > 1
case args.shift
when 'new'
# get email from args
email = args.shift
# generate shared secret
secret = ROTP::Base32.random_base32
# generate and print result
puts Generator.new(secret, email)
when 'test'
print_usage unless args.length == 2
secret, code = args
# verify code with 120 seconds of drift
ok = ROTP::TOTP.new(secret).verify_with_drift(code, 60, Time.now - 30)
# print result
puts 'Result: %s' % [ok ? 'success' : 'failure']
else
print_usage(app)
end
end
end
# run app
ROTPExample.run($0, ARGV) if __FILE__ == $0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment