Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Command-line Google Authenticator (TOTP)
#!/usr/bin/env ruby
# frozen_string_literal: true
# encoding: utf-8
#
# TOTP implementation (google authenticator)
#
# Reads ~/.google-authenticator-accounts for account info.
# For security, you should run `chmod 600 ~/.google-authenticator-accounts
#
# Changelog:
# - Modified by Spencer Bliven
# https://gist.github.com/sbliven/b198c199e0bd080a289340e21ba288f1
# - Original by Dan Q
# https://danq.me/2018/12/10/second-factor-safety-net/
# https://gist.github.com/Dan-Q/9f03057cddbda08ee7dfe4910f686150
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rotp'
gem 'thor'
end
class GoogleAuthenticator < Thor
@@authfile = File.expand_path('~/.google-authenticator-accounts')
desc 'add', 'add a new account'
option :auth, :default => @@authfile
def add
print 'New account name: '
acName = STDIN.gets.strip
print 'New account code: '
acCode = STDIN.gets.strip
File.open(options[:auth], 'a'){|f| f.printf "%-23s %s\n", acName, acCode}
end
desc 'generate', 'generate TOTP codes'
option :auth, :default => @@authfile
def generate
if not File.file?(options[:auth])
printf "Account file not found (%s). Add an account first?\n", options[:auth]
exit(false)
end
printf "%-30s %3s (%02ds) %4s\n", 'Account', 'Now', (30 - (Time::now.utc.to_i % 30)), 'Next'
puts '-' * 47
File.read(options[:auth]).split("\n").reject{|l| l.strip == '' or l[0] == '#'}.each do |account|
if account =~ /^(.+) ([\w\d]+)$/
totp = ROTP::TOTP.new($2)
printf "%-30s %06s %06s\n", $1, totp.at(Time::now.utc), totp.at(Time::now.utc + 30)
end
end
end
default_task :generate
end
GoogleAuthenticator.start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.