devise_token_auth 0.1.16 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -8
  3. data/app/controllers/devise_token_auth/auth_controller.rb +1 -1
  4. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +37 -15
  5. data/app/controllers/devise_token_auth/confirmations_controller.rb +1 -1
  6. data/app/controllers/devise_token_auth/registrations_controller.rb +1 -1
  7. data/app/controllers/devise_token_auth/sessions_controller.rb +1 -1
  8. data/app/models/user.rb +80 -6
  9. data/lib/devise_token_auth/engine.rb +12 -0
  10. data/lib/devise_token_auth/version.rb +1 -1
  11. data/lib/generators/devise_token_auth/USAGE +4 -3
  12. data/lib/generators/devise_token_auth/install_generator.rb +27 -0
  13. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +17 -0
  14. data/{db/migrate/20140628234942_devise_token_auth_create_users.rb → lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb} +1 -1
  15. data/test/controllers/demo_controller_test.rb +233 -0
  16. data/test/controllers/devise_token_auth/registrations_controller_test.rb +0 -1
  17. data/test/dummy/config/database.yml +22 -16
  18. data/test/dummy/config/initializers/devise_token_auth.rb +20 -0
  19. data/test/dummy/db/development.sqlite3 +0 -0
  20. data/test/dummy/db/migrate/{20140705000006_devise_token_auth_create_users.devise_token_auth.rb → 20140713062503_devise_token_auth_create_users.rb} +1 -2
  21. data/test/dummy/db/schema.rb +8 -5
  22. data/test/dummy/db/test.sqlite3 +0 -0
  23. data/test/dummy/log/development.log +2129 -0
  24. data/test/dummy/log/test.log +54231 -0
  25. data/test/fixtures/users.yml +9 -2
  26. data/test/lib/generators/devise_token_auth/{devise_token_auth_generator_test.rb → install_generator_test.rb} +3 -3
  27. data/test/test_helper.rb +13 -0
  28. metadata +34 -15
  29. data/lib/generators/devise_token_auth/devise_token_auth_generator.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d5d45596f8f30bd6b931a282f8f204892ea575a
4
- data.tar.gz: 7666d30af08d983a82461a6a98d6af6b5cb26c71
3
+ metadata.gz: d6fddec01685e543399f2fa156c7c5b3a1a00cc8
4
+ data.tar.gz: d51505ea9e19cac45f566c8a0159202363d34062
5
5
  SHA512:
6
- metadata.gz: 9b75f68a2fb8ec69db145524b383938458640daf1e4beea25ee24c04d9a028e848c5eadc227cc47e70a4c501c0d6c6924848c7c464529aa9d20af09fc49157a3
7
- data.tar.gz: 386781828692d7ccbb847b43915622c9c2406ac496c157b2364f98ef479121eacade9841ae6b5a4ecc415c76636c8ccb30e467951add3ae2d2d4831a3b22180e
6
+ metadata.gz: bf7d4346373fab8211fb5b09aaf7671178d17630a2ee7a496baa5fd528d0f10977acc64e1e28baa42103a371c3044f13ec606bec96caec69dc98824c22143818
7
+ data.tar.gz: 1812e0973a0bff3a54567b38cc862669b6cc7990383b9b48245ff6c38c0f2198934b4925e5305a88c3fa91adee221137f88af529fb7e1d26d5105e0bd9b713e9
data/README.md CHANGED
@@ -31,19 +31,25 @@ Then install the gem using bundle:
31
31
  bundle install
32
32
  ~~~
33
33
 
34
- ## Migrations
35
- You will need to create a user model. Run the following to generate and run the `User` model migration:
34
+ ## Configuration
35
+ You will need to create a user model, and you may want to alter some of the default settings. Run the following to generate the migrations and initializer files:
36
36
 
37
37
  ~~~bash
38
- rake devise_token_auth:install:migrations
38
+ rails g devise_token_auth:install
39
39
  ~~~
40
40
 
41
- Then run the migration:
41
+ This will create a migrations file in the `db/migrate` directory. Inspect the migrations file and add additional columns if necessary, then run the migration:
42
42
 
43
43
  ~~~bash
44
44
  rake db:migrate
45
45
  ~~~
46
46
 
47
+ An initializer will also be created at `config/initializers/devise_token_auth.rb`. The following settings are available for configuration:
48
+
49
+ * **`change_headers_on_each_request`** _Default: true_. By default the authorization headers will change after each request. The client is responsible for keeping track of the changing tokens. The [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module for angular.js does this out of the box. While this implementation is more secure, it can be difficult to manage. Set this to false to prevent the `Authorization` header from changing after each request.
50
+ * **`token_lifespan`** _Default: 2.weeks_. Set the length of your tokens' lifespans. Users will need to re-authenticate after this duration of time has passed since their last login.
51
+ * **`batch_request_buffer_throttle`** _Default: 2.seconds_. Sometimes it's necessary to make several requests to the API at the same time. In this case, each request in the batch will need to share the same auth token. This setting determines how far apart the requests can be while still using the same auth token.
52
+
47
53
  ## Omniauth authentication
48
54
 
49
55
  If you wish to use omniauth authentication, add all of your desired authentication provider gems as well.
@@ -165,19 +171,34 @@ The following routes are available for use by your client. These routes live rel
165
171
 
166
172
  If you're using [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) for angular.js, then your client is ready to go.
167
173
 
174
+
168
175
  ## Identifying users in controllers
169
176
 
170
177
  The authentication information should be included by the client in the `Authorization` header of each request. The header should follow this format:
171
178
 
179
+ ##### Authorization header example:
172
180
  ~~~
173
- token=xxxxx client=yyyyy uid=zzzzz
181
+ token=wwwww client=xxxxx expiry=yyyyy uid=zzzzz
174
182
  ~~~
175
183
 
176
- Replace `xxxxx` with the user's `auth_token` and `zzzzz` with the user's `uid`. The `client` field exists to allow for multiple simultaneous sessions per user. The client field defaults to `default` if omitted.
184
+ The `Authorization` header is made up of the following components:
185
+
186
+ * **`token`**: This serves as the user's password for each request. A hashed version of this value is stored in the database for later comparison. This value should be changed on each request.
187
+ * **`client`**: This enables the use of multiple simultaneous sessions on different clients. (For example, a user may want to be authenticated on both their phone and their laptop at the same time.)
188
+ * **`expiry`**: The date at which the current session will expire. This can be used by clients to invalidate expired tokens without the need for an API request.
189
+ * **`uid`**: A unique value that is used to identify the user. This is necessary because searching the DB for users by their access token will open the API up to timing attacks.
190
+
191
+ The `Authorization` header required for each request will be available in the response from the previous request. If you are using the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module for angular.js, this functionality is already provided.
192
+
193
+
194
+ ## The `User` model
177
195
 
178
- This all happens effortlessly and invisibly when using [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth).
196
+ The user model will contain the following public methods (read the above section for context on `token` and `client`):
197
+ * **`valid_token?`**: check if an authentication token is valid. Accepts `token` and `client` as arguments. Returns a boolean.
198
+ * **`create_new_auth_token`**: creates a new auth token with all of the necessary metadata. Accepts `client` as an optional argument. Will generate a new `client` if none is provided. Returns the `Authorization` header that should be sent by the client as a string.
199
+ * **`build_auth_header`**: generates the auth header that should be sent to the client with the next request. Accepts `token` and `client` as arguments. Returns a string.
179
200
 
180
- ### DeviseTokenAuth::Concerns::SetUserByToken
201
+ ## DeviseTokenAuth::Concerns::SetUserByToken
181
202
 
182
203
  This gem includes a [Rails concern](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) that can be used to identify users by the `Authorization` header.
183
204
 
@@ -45,7 +45,7 @@ module DeviseTokenAuth
45
45
 
46
46
  @user.tokens[@client_id] = {
47
47
  token: BCrypt::Password.create(@token),
48
- expiry: Time.now + 2.weeks
48
+ expiry: Time.now + DeviseTokenAuth.token_lifespan
49
49
  }
50
50
 
51
51
  # sync user info with provider, update/generate auth token
@@ -6,6 +6,7 @@ module DeviseTokenAuth::Concerns::SetUserByToken
6
6
  after_action :update_auth_header
7
7
  end
8
8
 
9
+
9
10
  # user auth
10
11
  def set_user_by_token
11
12
  auth_header = request.headers["Authorization"]
@@ -13,35 +14,56 @@ module DeviseTokenAuth::Concerns::SetUserByToken
13
14
  # missing auth token
14
15
  return false unless auth_header
15
16
 
16
- token = auth_header[/token=(.*?) /,1]
17
+ # parse header for values necessary for authentication
17
18
  uid = auth_header[/uid=(.*?)$/,1]
19
+ @token = auth_header[/token=(.*?) /,1]
18
20
  @client_id = auth_header[/client=(.*?) /,1]
19
21
 
22
+ # client_id isn't required, set to 'default' if absent
20
23
  @client_id ||= 'default'
21
24
 
22
25
  # mitigate timing attacks by finding by uid instead of auth token
23
26
  @user = @current_user = uid && User.find_by_uid(uid)
24
27
 
25
- if @user && @user.valid_token?(@client_id, token)
28
+ if @user && @user.valid_token?(@token, @client_id)
26
29
  sign_in(:user, @user, store: false, bypass: true)
30
+
31
+ # check this now so that the duration of the request itself doesn't eat
32
+ # away the buffer
33
+ @is_batch_request = is_batch_request?(@user, @client_id)
34
+
27
35
  else
28
- @user = @current_user = nil
36
+ # zero all values previously set values
37
+ @user = @current_user = @is_batch_request = nil
29
38
  end
30
39
  end
31
40
 
41
+
32
42
  def update_auth_header
33
- if @user
34
- # update user's auth token (should happen on each request)
35
- token = SecureRandom.urlsafe_base64(nil, false)
36
- token_hash = BCrypt::Password.create(token)
37
- @user.tokens[@client_id] = {
38
- token: token_hash,
39
- expiry: Time.now + 2.weeks
40
- }
41
- @user.save
42
-
43
- # update Authorization response header with new token
44
- response.headers["Authorization"] = "token=#{token} client=#{@client_id} uid=#{@user.uid}"
43
+
44
+ auth_header = nil
45
+ if not DeviseTokenAuth.change_headers_on_each_request
46
+ auth_header = @user.build_auth_header(@token, @client_id)
47
+
48
+ # extend expiration of batch buffer to account for the duration of
49
+ # this request
50
+ elsif @is_batch_request and @client_id and @user
51
+ auth_header = @user.extend_batch_buffer(@token, @client_id)
52
+
53
+ # update Authorization response header with new token
54
+ elsif @user and @client_id
55
+ auth_header = @user.create_new_auth_token(@client_id)
45
56
  end
57
+
58
+ response.headers["Authorization"] = auth_header if auth_header
59
+ end
60
+
61
+
62
+ private
63
+
64
+ def is_batch_request?(user, client_id)
65
+ user.tokens[client_id] and
66
+ user.tokens[client_id]['updated_at'] and
67
+ user.tokens[client_id]['updated_at'] > Time.now - DeviseTokenAuth.batch_request_buffer_throttle
46
68
  end
47
69
  end
@@ -12,7 +12,7 @@ module DeviseTokenAuth
12
12
  token_hash = BCrypt::Password.create(token)
13
13
  @user.tokens[client_id] = {
14
14
  token: token_hash,
15
- expiry: Time.now + 2.weeks
15
+ expiry: Time.now + DeviseTokenAuth.token_lifespan
16
16
  }
17
17
 
18
18
  @user.save!
@@ -22,7 +22,7 @@ module DeviseTokenAuth
22
22
  render json: {
23
23
  status: 'error',
24
24
  data: @resource,
25
- errors: ["An account already exists for #{@resource.email}"]
25
+ errors: @resource.errors
26
26
  }, status: 403
27
27
  end
28
28
  rescue ActiveRecord::RecordNotUnique
@@ -17,7 +17,7 @@ module DeviseTokenAuth
17
17
 
18
18
  @user.tokens[@client_id] = {
19
19
  token: BCrypt::Password.create(@token),
20
- expiry: Time.now + 2.weeks
20
+ expiry: Time.now + DeviseTokenAuth.token_lifespan
21
21
  }
22
22
  @user.save
23
23
 
@@ -7,36 +7,110 @@ class User < ActiveRecord::Base
7
7
 
8
8
  serialize :tokens, JSON
9
9
 
10
- # only validate unique emails among email registration users
11
10
  validates_presence_of :email, if: Proc.new { |u| u.provider == 'email' }
12
11
  validates_presence_of :confirm_success_url, if: Proc.new {|u| u.provider == 'email'}
13
12
 
13
+ # only validate unique emails among email registration users
14
14
  validate :unique_email_user, on: :create
15
15
 
16
- def valid_token?(client_id, token)
17
- return false unless self.tokens[client_id]['expiry'] > 2.weeks.ago
18
- return false unless BCrypt::Password.new(self.tokens[client_id]['token']) == token
16
+ def valid_token?(token, client_id='default')
17
+ client_id ||= 'default'
18
+
19
+ return true if (
20
+ # ensure that expiry and token are set
21
+ self.tokens[client_id]['expiry'] and
22
+ self.tokens[client_id]['token'] and
23
+
24
+ # ensure that the token was created within the last two weeks
25
+ self.tokens[client_id]['expiry'] > DeviseTokenAuth.token_lifespan.ago.to_f * 1000 and
26
+
27
+ # ensure that the token is valid
28
+ BCrypt::Password.new(self.tokens[client_id]['token']) == token
29
+ )
30
+
31
+ return true if (
32
+ # ensure that the last token and its creation time exist
33
+ self.tokens[client_id]['updated_at'] and
34
+ self.tokens[client_id]['last_token'] and
35
+
36
+ # ensure that previous token falls within the batch buffer throttle time of the last request
37
+ Time.parse(self.tokens[client_id]['updated_at']) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle and
38
+
39
+ # ensure that the token is valid
40
+ BCrypt::Password.new(self.tokens[client_id]['last_token']) == token
41
+ )
42
+
43
+ # return false if none of the above conditions are met
44
+ return false
45
+ end
46
+
47
+
48
+ # update user's auth token (should happen on each request)
49
+ def create_new_auth_token(client_id=nil)
50
+ client_id ||= SecureRandom.urlsafe_base64(nil, false)
51
+ last_token ||= nil
52
+ token = SecureRandom.urlsafe_base64(nil, false)
53
+ token_hash = BCrypt::Password.create(token)
54
+ expiry = (Time.now.to_f + DeviseTokenAuth.token_lifespan).to_i * 1000
55
+
56
+ if self.tokens[client_id] and self.tokens[client_id]['token']
57
+ last_token = self.tokens[client_id]['token']
58
+ end
59
+
60
+ self.tokens[client_id] = {
61
+ token: token_hash,
62
+ expiry: expiry,
63
+ last_token: last_token,
64
+ updated_at: Time.now
65
+ }
66
+
67
+ self.save!
19
68
 
20
- return true
69
+ return build_auth_header(token, client_id)
21
70
  end
22
71
 
72
+
73
+ def build_auth_header(token, client_id='default')
74
+ client_id ||= 'default'
75
+
76
+ # client may use expiry to prevent validation request if expired
77
+ expiry = self.tokens[client_id]['expiry']
78
+
79
+ return "token=#{token} client=#{client_id} expiry=#{expiry} uid=#{self.uid}"
80
+ end
81
+
82
+
83
+ def extend_batch_buffer(token, client_id)
84
+ self.tokens[client_id]['updated_at'] = Time.now
85
+ self.save!
86
+
87
+ return build_auth_header(token, client_id)
88
+ end
89
+
90
+
91
+ private
92
+
93
+
23
94
  def serializable_hash(options={})
24
95
  options ||= {}
25
96
  options[:except] ||= [:tokens]
26
97
  super(options)
27
98
  end
28
99
 
100
+
29
101
  # don't use default devise email validation
30
102
  def email_changed?
31
103
  false
32
104
  end
33
105
 
106
+
34
107
  def unique_email_user
35
108
  if provider == 'email' and User.where(provider: 'email', email: email).count > 0
36
- errors.add(:email, "Your email address is already in use")
109
+ errors.add(:email, "This email address is already in use")
37
110
  end
38
111
  end
39
112
 
113
+
40
114
  def email_required?
41
115
  provider == 'email'
42
116
  end
@@ -2,4 +2,16 @@ module DeviseTokenAuth
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace DeviseTokenAuth
4
4
  end
5
+
6
+ mattr_accessor :change_headers_on_each_request,
7
+ :token_lifespan,
8
+ :batch_request_buffer_throttle
9
+
10
+ self.change_headers_on_each_request = true
11
+ self.token_lifespan = 2.weeks
12
+ self.batch_request_buffer_throttle = 2.seconds
13
+
14
+ def self.setup(&block)
15
+ yield self
16
+ end
5
17
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseTokenAuth
2
- VERSION = "0.1.16"
2
+ VERSION = "0.1.17"
3
3
  end
@@ -1,8 +1,9 @@
1
1
  Description:
2
- Explain the generator
2
+ This generator will install all the necessary configuration and migration files for the devies_token_auth gem
3
3
 
4
4
  Example:
5
- rails generate devise_token_auth Thing
5
+ rails generate devise_token_auth:install
6
6
 
7
7
  This will create:
8
- what/will/it/create
8
+ config/initializers/devise_token_auth.rb
9
+ db/migrate/xxxxxxxx_create_devise_token_auth_user.rb
@@ -0,0 +1,27 @@
1
+ module DeviseTokenAuth
2
+ class InstallGenerator < Rails::Generators::Base
3
+ include Rails::Generators::Migration
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ desc "This generator creates an initializer file at config/initializers/devise_token_auth.rb"
8
+ def create_initializer_file
9
+ copy_file("devise_token_auth.rb", "config/initializers/devise_token_auth.rb")
10
+ end
11
+
12
+ desc "This generator creates a user migration file at db/migrate/<%= migration_id %>_devise_token_auth_create_users.rb"
13
+ def copy_migrations
14
+ if self.class.migration_exists?("db/migrate", "devise_token_auth_create_users")
15
+ say_status("skipped", "Migration 'devise_token_auth' already exists")
16
+ else
17
+ migration_template("devise_token_auth_create_users.rb", "db/migrate/devise_token_auth_create_users.rb")
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def self.next_migration_number(path)
24
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ DeviseTokenAuth.setup do |config|
2
+ # By default the authorization headers will change after each request. The
3
+ # client is responsible for keeping track of the changing tokens. Change
4
+ # this to false to prevent the Authorization header from changing after
5
+ # each request.
6
+ #config.change_headers_on_each_request = true
7
+
8
+ # By default, users will need to re-authenticate after 2 weeks. This setting
9
+ # determines how long tokens will remain valid after they are issued.
10
+ #config.token_lifespan = 2.weeks
11
+
12
+ # Sometimes it's necessary to make several requests to the API at the same
13
+ # time. In this case, each request in the batch will need to share the same
14
+ # auth token. This setting determines how far apart the requests can be while
15
+ # still using the same auth token.
16
+ #config.batch_request_buffer_throttle = 2.seconds
17
+ end
@@ -41,7 +41,7 @@ class DeviseTokenAuthCreateUsers < ActiveRecord::Migration
41
41
  t.string :uid, :null => false, :default => ""
42
42
 
43
43
  ## Tokens
44
- t.text :tokens, default: "{}"
44
+ t.string :tokens, default: "{}"
45
45
 
46
46
  t.timestamps
47
47
  end
@@ -0,0 +1,233 @@
1
+ require 'test_helper'
2
+
3
+ # was the web request successful?
4
+ # was the user redirected to the right page?
5
+ # was the user successfully authenticated?
6
+ # was the correct object stored in the response?
7
+ # was the appropriate message delivered in the json payload?
8
+
9
+ class DemoControllerTest < ActionController::TestCase
10
+ describe DemoController, "Token access" do
11
+ setup do
12
+ @routes = Dummy::Application.routes
13
+ end
14
+
15
+ before do
16
+ @user = users(:confirmed_email_user)
17
+ @user.skip_confirmation!
18
+ @user.save!
19
+
20
+ @auth_header = @user.create_new_auth_token
21
+
22
+ @token = @auth_header[/token=(.*?) /,1]
23
+ @client_id = @auth_header[/client=(.*?) /,1]
24
+ @expiry = @auth_header[/expiry=(.*?) /,1]
25
+ end
26
+
27
+ describe 'successful request' do
28
+ before do
29
+ # ensure that request is not treated as batch request
30
+ age_token(@user, @client_id)
31
+
32
+ request.headers['Authorization'] = @auth_header
33
+ xhr :get, :members_only
34
+
35
+ @resp_auth_header = response.headers['Authorization']
36
+ @resp_token = @resp_auth_header[/token=(.*?) /,1]
37
+ @resp_client_id = @resp_auth_header[/client=(.*?) /,1]
38
+ @resp_expiry = @resp_auth_header[/expiry=(.*?) /,1]
39
+ @resp_uid = @resp_auth_header[/uid=(.*?)$/,1]
40
+ end
41
+
42
+ it 'should return success status' do
43
+ assert_equal 200, response.status
44
+ end
45
+
46
+ it 'should receive new token after successful request' do
47
+ refute_equal @token, @resp_token
48
+ end
49
+
50
+ it 'should preserve the client id from the first request' do
51
+ assert_equal @client_id, @resp_client_id
52
+ end
53
+
54
+ it "should return the user's uid in the auth header" do
55
+ assert_equal @user.uid, @resp_uid
56
+ end
57
+
58
+ it 'should not treat this request as a batch request' do
59
+ refute assigns(:is_batch_request)
60
+ end
61
+
62
+ describe 'succesive requests' do
63
+ before do
64
+ @user.reload
65
+ # ensure that request is not treated as batch request
66
+ age_token(@user, @client_id)
67
+
68
+ request.headers['Authorization'] = @resp_auth_header
69
+
70
+ xhr :get, :members_only
71
+ end
72
+
73
+ it 'should not treat this request as a batch request' do
74
+ refute assigns(:is_batch_request)
75
+ end
76
+
77
+ it "should allow a new request to be made using new token" do
78
+ assert_equal 200, response.status
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'failed request' do
84
+ before do
85
+ request.headers['Authorization'] = "token=bogus client=#{@client_id} uid=#{@user.uid}"
86
+ xhr :get, :members_only
87
+ end
88
+
89
+ it 'should not return any auth headers' do
90
+ refute response.headers['Authorization']
91
+ end
92
+
93
+ it 'should return error: unauthorized status' do
94
+ assert_equal 401, response.status
95
+ end
96
+ end
97
+
98
+ describe 'disable change_headers_on_each_request' do
99
+ before do
100
+ DeviseTokenAuth.change_headers_on_each_request = false
101
+ @user.reload
102
+ age_token(@user, @client_id)
103
+
104
+ request.headers['Authorization'] = @auth_header
105
+ xhr :get, :members_only
106
+
107
+ @first_is_batch_request = assigns(:is_batch_request)
108
+ @first_user = assigns(:user).dup
109
+ @first_auth_headers = response.headers['Authorization'].clone
110
+ @first_response_status = response.status
111
+
112
+ @user.reload
113
+ age_token(@user, @client_id)
114
+
115
+ # use expired auth header
116
+ request.headers['Authorization'] = @auth_header
117
+ xhr :get, :members_only
118
+
119
+ @second_is_batch_request = assigns(:is_batch_request)
120
+ @second_user = assigns(:user)
121
+ @second_auth_headers = response.headers['Authorization']
122
+ @second_response_status = response.status
123
+ end
124
+
125
+ after do
126
+ DeviseTokenAuth.change_headers_on_each_request = true
127
+ end
128
+
129
+ it 'should allow the first request through' do
130
+ assert_equal 200, @first_response_status
131
+ end
132
+
133
+ it 'should allow the second request through' do
134
+ assert_equal 200, @second_response_status
135
+ end
136
+
137
+ it 'should return auth headers from the first request' do
138
+ assert @first_auth_headers
139
+ end
140
+
141
+ it 'should return auth headers from the second request' do
142
+ assert @second_auth_headers
143
+ end
144
+
145
+ it 'should define user during first request' do
146
+ assert @first_user
147
+ end
148
+
149
+ it 'should define user during second request' do
150
+ assert @second_user
151
+ end
152
+ end
153
+
154
+ describe 'batch requests' do
155
+ describe 'success' do
156
+ before do
157
+ request.headers['Authorization'] = @auth_header
158
+ xhr :get, :members_only
159
+
160
+ @first_is_batch_request = assigns(:is_batch_request)
161
+ @first_user = assigns(:user)
162
+ @first_auth_headers = response.headers['Authorization']
163
+
164
+ request.headers['Authorization'] = @auth_header
165
+ xhr :get, :members_only
166
+
167
+ @second_is_batch_request = assigns(:is_batch_request)
168
+ @second_user = assigns(:user)
169
+ @second_auth_headers = response.headers['Authorization']
170
+ end
171
+
172
+ it 'should allow both requests through' do
173
+ assert_equal 200, response.status
174
+ end
175
+
176
+ it 'should return the same auth headers for both requests' do
177
+ assert_equal @first_auth_headers, @second_auth_headers
178
+ end
179
+ end
180
+
181
+ describe 'time out' do
182
+ before do
183
+ @user.reload
184
+ age_token(@user, @client_id)
185
+
186
+ request.headers['Authorization'] = @auth_header
187
+ xhr :get, :members_only
188
+
189
+ @first_is_batch_request = assigns(:is_batch_request)
190
+ @first_user = assigns(:user).dup
191
+ @first_auth_headers = response.headers['Authorization'].clone
192
+ @first_response_status = response.status
193
+
194
+ @user.reload
195
+ age_token(@user, @client_id)
196
+
197
+ # use expired auth header
198
+ request.headers['Authorization'] = @auth_header
199
+ xhr :get, :members_only
200
+
201
+ @second_is_batch_request = assigns(:is_batch_request)
202
+ @second_user = assigns(:user)
203
+ @second_auth_headers = response.headers['Authorization']
204
+ @second_response_status = response.status
205
+ end
206
+
207
+ it 'should allow the first request through' do
208
+ assert_equal 200, @first_response_status
209
+ end
210
+
211
+ it 'should not allow the second request through' do
212
+ assert_equal 401, @second_response_status
213
+ end
214
+
215
+ it 'should return auth headers from the first request' do
216
+ assert @first_auth_headers
217
+ end
218
+
219
+ it 'should not return auth headers from the second request' do
220
+ refute @second_auth_headers
221
+ end
222
+
223
+ it 'should define user during first request' do
224
+ assert @first_user
225
+ end
226
+
227
+ it 'should not define user during second request' do
228
+ refute @second_user
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end