google-authenticator-rails 0.0.4 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+ gem "protected_attributes"
7
+
8
+ gemspec :path=>"../"
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ google-authenticator-rails (0.0.11)
5
+ actionpack
6
+ activerecord
7
+ google-qr
8
+ rotp (= 1.6.1)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actionpack (4.0.2)
14
+ activesupport (= 4.0.2)
15
+ builder (~> 3.1.0)
16
+ erubis (~> 2.7.0)
17
+ rack (~> 1.5.2)
18
+ rack-test (~> 0.6.2)
19
+ activemodel (4.0.2)
20
+ activesupport (= 4.0.2)
21
+ builder (~> 3.1.0)
22
+ activerecord (4.0.2)
23
+ activemodel (= 4.0.2)
24
+ activerecord-deprecated_finders (~> 1.0.2)
25
+ activesupport (= 4.0.2)
26
+ arel (~> 4.0.0)
27
+ activerecord-deprecated_finders (1.0.3)
28
+ activesupport (4.0.2)
29
+ i18n (~> 0.6, >= 0.6.4)
30
+ minitest (~> 4.2)
31
+ multi_json (~> 1.3)
32
+ thread_safe (~> 0.1)
33
+ tzinfo (~> 0.3.37)
34
+ appraisal (0.5.2)
35
+ bundler
36
+ rake
37
+ arel (4.0.1)
38
+ atomic (1.1.14)
39
+ builder (3.1.4)
40
+ diff-lcs (1.1.3)
41
+ erubis (2.7.0)
42
+ google-qr (0.2.2)
43
+ i18n (0.6.9)
44
+ minitest (4.7.5)
45
+ multi_json (1.8.4)
46
+ protected_attributes (1.0.8)
47
+ activemodel (>= 4.0.1, < 5.0)
48
+ rack (1.5.2)
49
+ rack-test (0.6.2)
50
+ rack (>= 1.0)
51
+ rake (10.1.1)
52
+ rotp (1.6.1)
53
+ rspec (2.8.0)
54
+ rspec-core (~> 2.8.0)
55
+ rspec-expectations (~> 2.8.0)
56
+ rspec-mocks (~> 2.8.0)
57
+ rspec-core (2.8.0)
58
+ rspec-expectations (2.8.0)
59
+ diff-lcs (~> 1.1.2)
60
+ rspec-mocks (2.8.0)
61
+ sqlite3 (1.3.8)
62
+ thread_safe (0.1.3)
63
+ atomic
64
+ tzinfo (0.3.38)
65
+
66
+ PLATFORMS
67
+ ruby
68
+
69
+ DEPENDENCIES
70
+ activerecord (~> 4.0.0)
71
+ appraisal (~> 0.5.1)
72
+ google-authenticator-rails!
73
+ protected_attributes
74
+ rspec (~> 2.8.0)
75
+ sqlite3
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+ gem "protected_attributes"
7
+
8
+ gemspec :path=>"../"
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ google-authenticator-rails (0.0.11)
5
+ actionpack
6
+ activerecord
7
+ google-qr
8
+ rotp (= 1.6.1)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actionpack (4.1.7)
14
+ actionview (= 4.1.7)
15
+ activesupport (= 4.1.7)
16
+ rack (~> 1.5.2)
17
+ rack-test (~> 0.6.2)
18
+ actionview (4.1.7)
19
+ activesupport (= 4.1.7)
20
+ builder (~> 3.1)
21
+ erubis (~> 2.7.0)
22
+ activemodel (4.1.7)
23
+ activesupport (= 4.1.7)
24
+ builder (~> 3.1)
25
+ activerecord (4.1.7)
26
+ activemodel (= 4.1.7)
27
+ activesupport (= 4.1.7)
28
+ arel (~> 5.0.0)
29
+ activesupport (4.1.7)
30
+ i18n (~> 0.6, >= 0.6.9)
31
+ json (~> 1.7, >= 1.7.7)
32
+ minitest (~> 5.1)
33
+ thread_safe (~> 0.1)
34
+ tzinfo (~> 1.1)
35
+ appraisal (0.5.2)
36
+ bundler
37
+ rake
38
+ arel (5.0.1.20140414130214)
39
+ builder (3.2.2)
40
+ diff-lcs (1.1.3)
41
+ erubis (2.7.0)
42
+ google-qr (0.2.2)
43
+ i18n (0.6.11)
44
+ json (1.8.1)
45
+ minitest (5.4.3)
46
+ protected_attributes (1.0.8)
47
+ activemodel (>= 4.0.1, < 5.0)
48
+ rack (1.5.2)
49
+ rack-test (0.6.2)
50
+ rack (>= 1.0)
51
+ rake (10.3.2)
52
+ rotp (1.6.1)
53
+ rspec (2.8.0)
54
+ rspec-core (~> 2.8.0)
55
+ rspec-expectations (~> 2.8.0)
56
+ rspec-mocks (~> 2.8.0)
57
+ rspec-core (2.8.0)
58
+ rspec-expectations (2.8.0)
59
+ diff-lcs (~> 1.1.2)
60
+ rspec-mocks (2.8.0)
61
+ sqlite3 (1.3.10)
62
+ thread_safe (0.3.4)
63
+ tzinfo (1.2.2)
64
+ thread_safe (~> 0.1)
65
+
66
+ PLATFORMS
67
+ ruby
68
+
69
+ DEPENDENCIES
70
+ activerecord (~> 4.1.0)
71
+ appraisal (~> 0.5.1)
72
+ google-authenticator-rails!
73
+ protected_attributes
74
+ rspec (~> 2.8.0)
75
+ sqlite3
@@ -1,6 +1,12 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require File.expand_path('../lib/google-authenticator-rails/version', __FILE__)
3
3
 
4
+ version_info = RUBY_VERSION.split(".")
5
+
6
+ major = version_info.first.to_i
7
+ minor = version_info[1].to_i
8
+ hotfix = version_info.last.to_i
9
+
4
10
  Gem::Specification.new do |gem|
5
11
  gem.authors = ["Jared McFarland"]
6
12
  gem.email = ["jared.online@gmail.com"]
@@ -14,12 +20,13 @@ Gem::Specification.new do |gem|
14
20
  gem.name = "google-authenticator-rails"
15
21
  gem.require_paths = ["lib"]
16
22
  gem.version = Google::Authenticator::Rails::VERSION
17
-
18
- gem.add_dependency "rotp"
23
+
24
+ gem.add_dependency "rotp", "= 1.6.1"
19
25
  gem.add_dependency "activerecord"
20
26
  gem.add_dependency "google-qr"
21
27
  gem.add_dependency "actionpack"
22
-
23
- gem.add_development_dependency "rspec", "~> 2.8.0"
28
+
29
+ gem.add_development_dependency "rspec", "~> 2.8.0"
30
+ gem.add_development_dependency "appraisal", "~> 0.5.1"
24
31
  gem.add_development_dependency "sqlite3"
25
32
  end
@@ -33,4 +33,6 @@ module GoogleAuthenticatorRails
33
33
  end
34
34
  end
35
35
 
36
- ActionController::Base.send(:include, GoogleAuthenticatorRails::ActionController::Integration)
36
+ if defined?(ActionController::Base)
37
+ ActionController::Base.send(:include, GoogleAuthenticatorRails::ActionController::Integration)
38
+ end
@@ -1,88 +1,98 @@
1
1
  module GoogleAuthenticatorRails # :nodoc:
2
- module ActiveRecord # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
3
  module ActsAsGoogleAuthenticated # :nodoc:
4
4
  def self.included(base)
5
5
  base.extend ClassMethods
6
6
  end
7
7
 
8
8
  # This is the single integration point. Monkey patch ActiveRecord::Base
9
- # to include the ActsAsGoogleAuthenticated module, which allows a user
9
+ # to include the ActsAsGoogleAuthenticated module, which allows a user
10
10
  # to call User.acts_as_google_authenticated.
11
- #
11
+ #
12
12
  # The model being used must have a string column named "google_secret", or an explicitly
13
13
  # named column.
14
- #
14
+ #
15
15
  # Example:
16
- #
16
+ #
17
17
  # class User
18
18
  # acts_as_google_authenticated
19
19
  # end
20
- #
20
+ #
21
21
  # @user = user.new
22
22
  # @user.set_google_secret # => true
23
23
  # @user.google_qr_uri # => http://path.to.google/qr?with=params
24
24
  # @user.google_authentic?(123456) # => true
25
- #
25
+ #
26
26
  # Google Labels
27
27
  # When setting up an account with the GoogleAuthenticator you need to provide
28
28
  # a label for that account (to distinguish it from other accounts).
29
- #
29
+ #
30
30
  # GoogleAuthenticatorRails allows you to customize how the record will create
31
31
  # that label. There are three options:
32
32
  # - The default just uses the column "email" on the model
33
33
  # - You can specify a custom column with the :column_name option
34
34
  # - You can specify a custom method via a symbol or a proc
35
- #
35
+ #
36
36
  # Examples:
37
- #
37
+ #
38
38
  # class User
39
39
  # acts_as_google_authenticated :column => :user_name
40
40
  # end
41
- #
41
+ #
42
42
  # @user = User.new(:user_name => "ted")
43
43
  # @user.google_label # => "ted"
44
- #
44
+ #
45
45
  # class User
46
46
  # acts_as_google_authenticated :method => :user_name_with_label
47
- #
47
+ #
48
48
  # def user_name_with_label
49
49
  # "#{user_name}@example.com"
50
50
  # end
51
51
  # end
52
- #
52
+ #
53
53
  # @user = User.new(:user_name => "ted")
54
54
  # @user.google_label # => "ted@example.com"
55
- #
55
+ #
56
56
  # class User
57
57
  # acts_as_google_authenticated :method => Proc.new { |user| user.user_name_with_label.upcase }
58
- #
58
+ #
59
59
  # def user_name_with_label
60
60
  # "#{user_name}@example.com"
61
61
  # end
62
62
  # end
63
- #
63
+ #
64
64
  # @user = User.new(:user_name => "ted")
65
65
  # @user.google_label # => "TED@EXAMPLE.COM"
66
- #
66
+ #
67
67
  module ClassMethods # :nodoc
68
68
 
69
- # Initializes the class attributes with the specified options and includes the
69
+ # Initializes the class attributes with the specified options and includes the
70
70
  # respective ActiveRecord helper methods
71
- #
71
+ #
72
72
  # Options:
73
73
  # [:column_name] the name of the column used to create the google_label
74
74
  # [:method] name of the method to call to create the google_label
75
75
  # it supercedes :column_name
76
76
  # [:google_secret_column] the column the secret will be stored in, defaults
77
77
  # to "google_secret"
78
+ # [:lookup_token] the column to use to find the record from the DB, defaults
79
+ # to "persistence_token"
80
+ # [:drift] drift the number of seconds that the client and server are
81
+ # allowed to drift apart. Default value is 6.
82
+ #
83
+ # [:issuer] the name of the issuer to appear in the app (optional), defaults
84
+ # to ""
78
85
  def acts_as_google_authenticated(options = {})
79
86
  @google_label_column = options[:column_name] || :email
80
87
  @google_label_method = options[:method] || :default_google_label_method
81
88
  @google_secret_column = options[:google_secret_column] || :google_secret
89
+ @google_lookup_token = options[:lookup_token] || :persistence_token
90
+ @google_drift = options[:drift] || GoogleAuthenticatorRails::DRIFT
91
+ @google_issuer = options[:issuer]
82
92
 
83
93
  puts ":skip_attr_accessible is no longer required. Called from #{Kernel.caller[0]}}" if options.has_key?(:skip_attr_accessible)
84
94
 
85
- [:google_label_column, :google_label_method, :google_secret_column].each do |cattr|
95
+ [:google_label_column, :google_label_method, :google_secret_column, :google_lookup_token, :google_drift, :google_issuer].each do |cattr|
86
96
  self.singleton_class.class_eval { attr_reader cattr }
87
97
  end
88
98
 
@@ -6,24 +6,12 @@ module GoogleAuthenticatorRails # :nodoc:
6
6
  save
7
7
  end
8
8
 
9
- # TODO: Remove this method in version 0.0.4
10
- def set_google_secret!
11
- put "DEPRECATION WARNING: #set_google_secret! is no longer being used, use #set_google_secret instead. #set_google_secret! will be removed in 0.0.4. Called from #{Kernel.caller[0]}"
12
- set_google_secret
13
- end
14
-
15
9
  def google_authentic?(code)
16
- GoogleAuthenticatorRails.valid?(code, google_secret_value)
17
- end
18
-
19
- # TODO: Remove this method in version 0.0.4
20
- def google_authenticate(code)
21
- put "DEPRECATION WARNING: #google_authenticate is no longer being used, use #google_authentic? instead. #google_authenticate will be removed in 0.0.4. Called from #{Kernel.caller[0]}"
22
- google_authentic?(code)
10
+ GoogleAuthenticatorRails.valid?(code, google_secret_value, self.class.google_drift)
23
11
  end
24
12
 
25
13
  def google_qr_uri
26
- GoogleQR.new(:data => ROTP::TOTP.new(google_secret_value).provisioning_uri(google_label), :size => "200x200").to_s
14
+ GoogleQR.new(:data => ROTP::TOTP.new(google_secret_value, :issuer => google_issuer).provisioning_uri(google_label), :size => "200x200").to_s
27
15
  end
28
16
 
29
17
  def google_label
@@ -38,6 +26,10 @@ module GoogleAuthenticatorRails # :nodoc:
38
26
  end
39
27
  end
40
28
 
29
+ def google_token_value
30
+ self.__send__(self.class.google_lookup_token)
31
+ end
32
+
41
33
  private
42
34
  def default_google_label_method
43
35
  self.__send__(self.class.google_label_column)
@@ -46,6 +38,10 @@ module GoogleAuthenticatorRails # :nodoc:
46
38
  def google_secret_value
47
39
  self.__send__(self.class.google_secret_column)
48
40
  end
41
+
42
+ def google_issuer
43
+ self.class.google_issuer
44
+ end
49
45
  end
50
46
  end
51
47
  end
@@ -16,7 +16,7 @@ module GoogleAuthenticatorRails
16
16
  cookie = controller.cookies[cookie_key]
17
17
  if cookie
18
18
  token, user_id = parse_cookie(cookie).values_at(:token, :user_id)
19
- conditions = { :persistence_token => token, :id => user_id }
19
+ conditions = { klass.google_lookup_token => token, :id => user_id }
20
20
  record = __send__(finder, conditions).first
21
21
  session = new(record)
22
22
  session.valid? ? session : nil
@@ -26,11 +26,15 @@ module GoogleAuthenticatorRails
26
26
  end
27
27
 
28
28
  def create(user)
29
- raise GoogleAuthenticatorRails::Session::Persistence::TokenNotFound if !user.respond_to?(:persistence_token) || user.persistence_token.blank?
30
- controller.cookies[cookie_key] = create_cookie(user.persistence_token, user.id)
29
+ raise GoogleAuthenticatorRails::Session::Persistence::TokenNotFound if user.nil? || !user.respond_to?(user.class.google_lookup_token) || user.google_token_value.blank?
30
+ controller.cookies[cookie_key] = create_cookie(user.google_token_value, user.id)
31
31
  new(user)
32
32
  end
33
33
 
34
+ def destroy
35
+ controller.cookies.delete cookie_key
36
+ end
37
+
34
38
  private
35
39
  def finder
36
40
  @_finder ||= klass.public_methods.include?(:where) ? :rails_3_finder : :rails_2_finder
@@ -55,14 +59,16 @@ module GoogleAuthenticatorRails
55
59
 
56
60
  def create_cookie(token, user_id)
57
61
  value = [token, user_id].join('::')
58
- {
62
+ options = GoogleAuthenticatorRails.cookie_options || {}
63
+ options.merge(
59
64
  :value => value,
60
65
  :expires => GoogleAuthenticatorRails.time_until_expiration.from_now
61
- }
66
+ )
62
67
  end
63
68
 
64
69
  def cookie_key
65
- "#{klass.to_s.downcase}_mfa_credentials"
70
+ suffix = GoogleAuthenticatorRails.cookie_key_suffix || 'mfa_credentials'
71
+ "#{klass.to_s.downcase}_#{suffix}"
66
72
  end
67
73
  end
68
74
 
@@ -72,4 +78,4 @@ module GoogleAuthenticatorRails
72
78
  end
73
79
  end
74
80
  end
75
- end
81
+ end
@@ -1,7 +1,7 @@
1
1
  module Google
2
2
  module Authenticator
3
3
  module Rails
4
- VERSION = "0.0.4"
4
+ VERSION = "1.2.1"
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
- # Stuff the gem requireds
2
- #
1
+ # Stuff the gem requires
2
+ #
3
3
  require 'active_support'
4
4
  require 'active_record'
5
5
  require 'openssl'
@@ -12,7 +12,7 @@ GOOGLE_AUTHENTICATOR_RAILS_PATH = File.dirname(__FILE__) + "/google-authenticato
12
12
 
13
13
  [
14
14
  "version",
15
-
15
+
16
16
  "action_controller",
17
17
  "active_record",
18
18
  "session"
@@ -20,15 +20,21 @@ GOOGLE_AUTHENTICATOR_RAILS_PATH = File.dirname(__FILE__) + "/google-authenticato
20
20
  require GOOGLE_AUTHENTICATOR_RAILS_PATH + library
21
21
  end
22
22
 
23
- # Sets up some basic accessors for use with the ROTP module
24
- #
23
+ # Sets up some basic accessors for use with the ROTP module
24
+ #
25
25
  module GoogleAuthenticatorRails
26
- # Drift is set to 6 because ROTP drift is not inclusive. This allows a drift of 5 seconds.
26
+ # Drift is set to 6 because ROTP drift is not inclusive. This allows a drift of 5 seconds.
27
27
  DRIFT = 6
28
28
 
29
29
  # How long a Session::Persistence cookie should last.
30
30
  @@time_until_expiration = 24.hours
31
31
 
32
+ # Last part of a Session::Persistence cookie's key
33
+ @@cookie_key_suffix = nil
34
+
35
+ # Additional configuration passed to a Session::Persistence cookie.
36
+ @@cookie_options = { :httponly => true }
37
+
32
38
  def self.generate_password(secret, iteration)
33
39
  ROTP::HOTP.new(secret).at(iteration)
34
40
  end
@@ -37,8 +43,8 @@ module GoogleAuthenticatorRails
37
43
  ROTP::TOTP.new(secret).now
38
44
  end
39
45
 
40
- def self.valid?(code, secret)
41
- ROTP::TOTP.new(secret).verify_with_drift(code, DRIFT)
46
+ def self.valid?(code, secret, drift = DRIFT)
47
+ ROTP::TOTP.new(secret).verify_with_drift(code, drift)
42
48
  end
43
49
 
44
50
  def self.generate_secret
@@ -52,4 +58,20 @@ module GoogleAuthenticatorRails
52
58
  def self.time_until_expiration=(time_until_expiration)
53
59
  @@time_until_expiration = time_until_expiration
54
60
  end
55
- end
61
+
62
+ def self.cookie_key_suffix
63
+ @@cookie_key_suffix
64
+ end
65
+
66
+ def self.cookie_key_suffix=(suffix)
67
+ @@cookie_key_suffix = suffix
68
+ end
69
+
70
+ def self.cookie_options
71
+ @@cookie_options
72
+ end
73
+
74
+ def self.cookie_options=(options)
75
+ @@cookie_options = options
76
+ end
77
+ end
@@ -5,49 +5,65 @@ describe GoogleAuthenticatorRails do
5
5
  before do
6
6
  ROTP::Base32.stub!(:random_base32).and_return(random32)
7
7
  end
8
-
8
+
9
9
  describe '#generate_password' do
10
10
  subject { GoogleAuthenticatorRails::generate_password("test", counter) }
11
-
11
+
12
12
  context 'counter = 1' do
13
13
  let(:counter) { 1 }
14
- it { should == 812658 }
14
+ it { should == 868864 }
15
15
  end
16
16
 
17
17
  context 'counter = 2' do
18
18
  let(:counter) { 2 }
19
- it { should == 73348 }
19
+ it { should == 304404 }
20
20
  end
21
21
  end
22
-
22
+
23
23
  context 'time-based passwords' do
24
24
  let(:time) { Time.parse("2012-08-07 11:11:11 AM +0700") }
25
25
  let(:secret) { "test" }
26
- let(:code) { 472374 }
26
+ let(:code) { 922511 }
27
27
  before { Time.stub!(:now).and_return(time) }
28
28
 
29
29
  specify { GoogleAuthenticatorRails::time_based_password(secret).should == code }
30
- specify { GoogleAuthenticatorRails::valid?(code, secret).should be true }
30
+ specify { GoogleAuthenticatorRails::valid?(code, secret).should be true }
31
31
 
32
32
  specify { GoogleAuthenticatorRails::valid?(code * 2, secret).should be false }
33
- specify { GoogleAuthenticatorRails::valid?(code, secret * 2).should be false }
33
+ specify { GoogleAuthenticatorRails::valid?(code, secret * 2).should be false }
34
34
  end
35
-
35
+
36
36
  it 'can create a secret' do
37
37
  GoogleAuthenticatorRails::generate_secret.should == random32
38
38
  end
39
-
40
- context 'integration with ActiveRecord' do
39
+
40
+ context 'integration with ActiveRecord' do
41
41
  let(:original_time) { Time.parse("2012-08-07 11:11:00 AM +0700") }
42
42
  let(:time) { original_time }
43
+ let(:user) { User.create(:email => "test@example.com", :user_name => "test_user") }
43
44
  before do
44
45
  Time.stub!(:now).and_return(time)
45
- @user = User.create(:email => "test@example.com", :user_name => "test_user")
46
- @user.google_secret = "test"
46
+ user.google_secret = "test"
47
+ end
48
+
49
+ context "custom drift" do
50
+ # 30 seconds drift
51
+ let(:user) { DriftUser.create(:email => "test@example.com", :user_name => "test_user") }
52
+ subject { user.google_authentic?(922511) }
53
+
54
+ context '6 seconds of drift' do
55
+ let(:time) { original_time + 36.seconds }
56
+ it { should be true }
57
+ end
58
+
59
+ context '30 seconds of drift' do
60
+ let(:time) { original_time + 61.seconds }
61
+ it { should be false }
62
+ end
47
63
  end
48
-
64
+
49
65
  context 'code validation' do
50
- subject { @user.google_authentic?(472374) }
66
+ subject { user.google_authentic?(922511) }
51
67
 
52
68
  it { should be true }
53
69
 
@@ -61,10 +77,10 @@ describe GoogleAuthenticatorRails do
61
77
  it { should be false }
62
78
  end
63
79
  end
64
-
80
+
65
81
  it 'creates a secret' do
66
- @user.set_google_secret
67
- @user.google_secret.should == random32
82
+ user.set_google_secret
83
+ user.google_secret.should == random32
68
84
  end
69
85
 
70
86
  context 'secret column' do
@@ -75,7 +91,7 @@ describe GoogleAuthenticatorRails do
75
91
  end
76
92
 
77
93
  it 'validates code' do
78
- @user.google_authentic?(472374).should be_true
94
+ @user.google_authentic?(922511).should be_true
79
95
  end
80
96
 
81
97
  it 'generates a url for a qr code' do
@@ -89,6 +105,14 @@ describe GoogleAuthenticatorRails do
89
105
  it { should raise_error(NoMethodError) }
90
106
  end
91
107
 
108
+ context "drift value" do
109
+ it { DriftUser.google_drift.should == 31 }
110
+
111
+ context "default value" do
112
+ it { User.google_drift.should == 6 }
113
+ end
114
+ end
115
+
92
116
  context 'qr codes' do
93
117
  let(:options) { { :email => "test@example.com", :user_name => "test_user" } }
94
118
  let(:user) { User.create options }
@@ -96,12 +120,12 @@ describe GoogleAuthenticatorRails do
96
120
  subject { user.google_qr_uri }
97
121
 
98
122
  it { should eq "https://chart.googleapis.com/chart?cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest%40example.com%3Fsecret%3D5qlcip7azyjuwm36&chs=200x200" }
99
-
123
+
100
124
  context 'custom column name' do
101
125
  let(:user) { ColumnNameUser.create options }
102
126
  it { should eq "https://chart.googleapis.com/chart?cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest_user%3Fsecret%3D5qlcip7azyjuwm36&chs=200x200" }
103
127
  end
104
-
128
+
105
129
  context 'custom proc' do
106
130
  let(:user) { ProcUser.create options }
107
131
  it { should eq "https://chart.googleapis.com/chart?cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest_user%40futureadvisor-admin%3Fsecret%3D5qlcip7azyjuwm36&chs=200x200" }
@@ -112,12 +136,12 @@ describe GoogleAuthenticatorRails do
112
136
  it { should eq "https://chart.googleapis.com/chart?cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest%40example.com%3Fsecret%3D5qlcip7azyjuwm36&chs=200x200" }
113
137
  end
114
138
 
115
- context 'method defined by string' do
139
+ context 'method defined by string' do
116
140
  let(:user) { StringUser.create options }
117
141
  it { should eq "https://chart.googleapis.com/chart?cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest%40example.com%3Fsecret%3D5qlcip7azyjuwm36&chs=200x200" }
118
- end
142
+ end
119
143
  end
120
-
144
+
121
145
  end
122
-
123
- end
146
+
147
+ end