google-authenticator-rails 0.0.4 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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