devise_token_auth 0.1.20 → 0.1.21.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +274 -77
- data/app/controllers/devise_token_auth/auth_controller.rb +4 -1
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +8 -2
- data/app/controllers/devise_token_auth/confirmations_controller.rb +1 -1
- data/app/controllers/devise_token_auth/passwords_controller.rb +3 -3
- data/app/controllers/devise_token_auth/registrations_controller.rb +1 -1
- data/app/controllers/devise_token_auth/sessions_controller.rb +1 -1
- data/app/models/{user.rb → devise_token_auth/concerns/user.rb} +28 -23
- data/config/initializers/devise.rb +4 -64
- data/config/routes.rb +2 -14
- data/lib/devise_token_auth/engine.rb +5 -1
- data/lib/devise_token_auth/rails/routes.rb +36 -0
- data/lib/devise_token_auth/version.rb +1 -1
- data/lib/generators/devise_token_auth/USAGE +23 -5
- data/lib/generators/devise_token_auth/install_generator.rb +69 -5
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +5 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +56 -0
- data/lib/generators/devise_token_auth/templates/user.rb +3 -0
- data/test/controllers/demo_controller_test.rb +39 -4
- data/test/controllers/devise_token_auth/auth_controller_test.rb +98 -0
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +37 -2
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +38 -2
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +31 -4
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +32 -0
- data/test/dummy/app/assets/images/omniauth-provider-settings.png +0 -0
- data/test/dummy/app/models/mang.rb +3 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/config/initializers/devise_token_auth.rb +5 -0
- data/test/dummy/config/routes.rb +16 -1
- data/test/dummy/db/development.sqlite3 +0 -0
- data/{lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb → test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb} +0 -0
- data/test/dummy/db/migrate/{20140714223034_devise_token_auth_create_users.rb → 20140715061805_devise_token_auth_create_mangs.rb} +7 -7
- data/test/dummy/db/schema.rb +35 -4
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +7601 -0
- data/test/dummy/log/test.log +128490 -0
- data/test/fixtures/mangs.yml +31 -0
- data/test/test_helper.rb +13 -9
- metadata +22 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cceda7b13b2df7fdd541d948992dbc48b7dfd3a1
|
4
|
+
data.tar.gz: 8fd2c90a8d52644a61a38e4e6509c5c089858661
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ef99d9eb62eecc7cfca98cd68ae981e1411e48ddf9da3a1a1c0315dfea9e53e141cb4a2a0e9dcd23f0ab8f4184243cfde9a24e202d5fde5c1532ea094c3bb4b
|
7
|
+
data.tar.gz: 9219e6c4029c2fa29ff17b78078814295327794e322f8228ddcf03ed1795235040a29297100882da3c6b66152ef063c304bae34f597e48d6d7d2f94555cf86af
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Devise Token Auth
|
2
2
|
|
3
3
|
![build](https://travis-ci.org/lynndylanhurley/devise_token_auth.svg)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/lynndylanhurley/devise_token_auth.png)](https://codeclimate.com/github/lynndylanhurley/devise_token_auth)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/lynndylanhurley/devise_token_auth/coverage.png)](https://codeclimate.com/github/lynndylanhurley/devise_token_auth)
|
4
6
|
|
5
7
|
This gem provides simple, secure token based authentication.
|
6
8
|
|
@@ -31,30 +33,74 @@ Then install the gem using bundle:
|
|
31
33
|
bundle install
|
32
34
|
~~~
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
+
# Configuration TLDR;
|
37
|
+
|
38
|
+
You will need to create a user model, define routes, and you may want to alter some of the default settings for this gem. Run the following to append the routes and generate the model, migration, and initializer files:
|
36
39
|
|
37
40
|
~~~bash
|
38
|
-
rails g devise_token_auth:install
|
41
|
+
rails g devise_token_auth:install [USER_CLASS] [MOUNT_PATH]
|
39
42
|
~~~
|
40
43
|
|
41
|
-
This
|
44
|
+
This generator accepts the following optional arguments:
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
+
| Argument | Default | Description |
|
47
|
+
|---|---|---|
|
48
|
+
| USER_CLASS | `User` | The name of the class to use for user authentication. |
|
49
|
+
| MOUNT_PATH | `auth` | The path at which to mount the authentication routes. [Read more](#usage). |
|
50
|
+
|
51
|
+
The following events will take place when using the install generator:
|
52
|
+
|
53
|
+
* An initializer will be created at `config/initializers/devise_token_auth.rb`. [Read more](#initializer-settings).
|
54
|
+
|
55
|
+
* A model will be created in the `app/models` directory. If the model already exists, a concern will be included at the top of the file. [Read more](#model-concerns).
|
56
|
+
|
57
|
+
* Routes will be appended to file at `config/routes.rb`. [Read more](#mounting-routes).
|
58
|
+
|
59
|
+
* A migration file will be created in the `db/migrate` directory. Inspect the migrations file, add additional columns if necessary, and then run the migration:
|
60
|
+
|
61
|
+
~~~bash
|
62
|
+
rake db:migrate
|
63
|
+
~~~
|
64
|
+
|
65
|
+
[Jump here](#configuration-cont) for more configuration information.
|
66
|
+
|
67
|
+
# Usage TLDR;
|
68
|
+
|
69
|
+
The following routes are available for use by your client. These routes live relative to the path at which this engine is mounted (`/auth` by default). These routes correspond to the defaults used by the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module for angular.js.
|
70
|
+
|
71
|
+
| path | method | purpose |
|
72
|
+
|:-----|:-------|:--------|
|
73
|
+
| / | POST | email registration. accepts **`email`**, **`password`**, and **`password_confirmation`** params. |
|
74
|
+
| /sign_in | POST | email authentication. accepts **`email`** and **`password`** as params. |
|
75
|
+
| /sign_out | DELETE | invalidate tokens (end session) |
|
76
|
+
| /:provider | GET | set this route as the destination for client authentication. ideally this will happen in an external window or popup. [Read more](#omniauth-authentication). |
|
77
|
+
| /:provider/callback | GET/POST | destination for the oauth2 provider's callback uri. `postMessage` events containing the authenticated user's data will be sent back to the main client window from this page. [Read more](#omniauth-authentication). |
|
78
|
+
| /validate_token | POST | use this route to validate tokens on return visits to the client. accepts **`uid`** and **`auth_token`** as params. these values should correspond to the columns in your `User` table of the same names. |
|
79
|
+
| /password | POST | send password email to users that registered by email. accepts **`email`** and **`redirect_url`** as params. The user matching the `email` param will be sent instructions on how to reset their password. `redirect_url` is the url to which the user will be redirected after visiting the link contained in the email. |
|
80
|
+
| /password | PUT | password change for users that registered by email. accepts **`password`** and **`password_confirmation`** as params. |
|
81
|
+
| /password/edit | GET | verify user by password reset token. must contain **`reset_password_token`** and **`redirect_url`** as params. These values will be set automatically by the confirmation email that is generated by the password reset request. |
|
82
|
+
|
83
|
+
[Jump here](#usage-cont) for more usage information.
|
46
84
|
|
47
|
-
|
85
|
+
# Configuration cont.
|
86
|
+
|
87
|
+
## Initializer settings
|
88
|
+
|
89
|
+
The following settings are available for configuration in `config/initializers/devise_token_auth.rb`:
|
90
|
+
|
91
|
+
| Name | Default | Description|
|
92
|
+
|---|---|---|
|
93
|
+
| **`change_headers_on_each_request`** | `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. [Read more](#about-token-management). |
|
94
|
+
| **`token_lifespan`** | `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. |
|
95
|
+
| **`batch_request_buffer_throttle`** | `5.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. [Read more](#about-batch-requests). |
|
96
|
+
| **`omniauth_prefix`** | `"/omniauth"` | This route will be the prefix for all oauth2 redirect callbacks. For example, using the default '/omniauth' setting, the github oauth2 provider will redirect successful authentications to '/omniauth/github/callback'. [Read more](#omniauth-provider-settings). |
|
48
97
|
|
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
98
|
|
53
99
|
## Omniauth authentication
|
54
100
|
|
55
|
-
If you wish to use omniauth authentication, add all of your desired authentication provider gems
|
101
|
+
If you wish to use omniauth authentication, add all of your desired authentication provider gems to your `Gemfile`.
|
56
102
|
|
57
|
-
|
103
|
+
**Omniauth example using github, facebook, and google**:
|
58
104
|
~~~ruby
|
59
105
|
gem 'omniauth-github', :git => 'git://github.com/intridea/omniauth-github.git'
|
60
106
|
gem 'omniauth-facebook', :git => 'git://github.com/mkdynamic/omniauth-facebook.git'
|
@@ -65,12 +111,13 @@ Then run `bundle install`.
|
|
65
111
|
|
66
112
|
[List of oauth2 providers](https://github.com/intridea/omniauth/wiki/List-of-Strategies)
|
67
113
|
|
68
|
-
|
114
|
+
## Omniauth provider settings
|
115
|
+
|
69
116
|
In `config/initializers/omniauth.rb`, add the settings for each of your providers.
|
70
117
|
|
71
118
|
These settings must be obtained from the providers themselves.
|
72
119
|
|
73
|
-
|
120
|
+
**Example using github, facebook, and google**:
|
74
121
|
~~~ruby
|
75
122
|
# config/initializers/omniauth.rb
|
76
123
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
@@ -82,8 +129,44 @@ end
|
|
82
129
|
|
83
130
|
The above example assumes that your provider keys and secrets are stored in environmental variables. Use the [figaro](https://github.com/laserlemon/figaro) gem (or [dotenv](https://github.com/bkeepers/dotenv) or [secrets.yml](https://github.com/rails/rails/blob/v4.1.0/railties/lib/rails/generators/rails/app/templates/config/secrets.yml) or equivalent) to accomplish this.
|
84
131
|
|
132
|
+
#### Omniauth callback settings
|
133
|
+
|
134
|
+
The "Callback URL" setting that you set with your provider must correspond to the [omniauth prefix](#initializer-settings) setting defined by this app. **This will be different than the omniauth route that is used by your client application**.
|
135
|
+
|
136
|
+
For example, the demo app uses the default `omniauth_prefix` setting `/omniauth`, so the "Authorization callback URL" for github must be set to "http://devise-token-auth-demo.herokuapp.com**/omniauth**/github/callback".
|
137
|
+
|
138
|
+
**Github example for the demo site**:
|
139
|
+
![password reset flow](https://github.com/lynndylanhurley/devise_token_auth/raw/master/test/dummy/app/assets/images/omniauth-provider-settings.png)
|
140
|
+
|
141
|
+
The url for github authentication will be different for the client. The client should visit the API at `/[MOUNT_PATH]/:provider` for omniauth authentication.
|
142
|
+
|
143
|
+
For example, given that the app is mounted using the following settings:
|
144
|
+
|
145
|
+
~~~ruby
|
146
|
+
# config/routes.rb
|
147
|
+
mount_devise_token_auth_for 'User', at: '/auth'
|
148
|
+
~~~
|
149
|
+
|
150
|
+
The client configuration for github should look like this:
|
151
|
+
|
152
|
+
**Angular.js setting for authenticating using github**:
|
153
|
+
~~~javascript
|
154
|
+
angular.module('myApp', ['ng-token-auth'])
|
155
|
+
.config(function($authProvider) {
|
156
|
+
$authProvider.configure({
|
157
|
+
apiUrl: 'http://api.example.com'
|
158
|
+
authProviderPaths: {
|
159
|
+
github: '/auth/github' // <-- note that this is different than what was set with github
|
160
|
+
}
|
161
|
+
});
|
162
|
+
});
|
163
|
+
~~~
|
164
|
+
|
165
|
+
This incongruence is necessary to support multiple user classes and mounting points.
|
85
166
|
|
86
|
-
|
167
|
+
#### Note for [pow](http://pow.cx/) and [xip.io](http://xip.io) users
|
168
|
+
|
169
|
+
If you receive `redirect-uri-mismatch` errors from your provider when using pow or xip.io urls, set the following in your development config:
|
87
170
|
|
88
171
|
~~~ruby
|
89
172
|
# config/environments/development.rb
|
@@ -95,8 +178,6 @@ OmniAuth.config.full_host = "http://app-name.dev"
|
|
95
178
|
OmniAuth.config.full_host = "http://xxx.xxx.xxx.app-name.xip.io"
|
96
179
|
~~~
|
97
180
|
|
98
|
-
There may be a better way to accomplish this. Please post an issue if you have any suggestions.
|
99
|
-
|
100
181
|
## Email authentication
|
101
182
|
If you wish to use email authentication, you must configure your Rails application to send email. [Read here](http://guides.rubyonrails.org/action_mailer_basics.html) for more information.
|
102
183
|
|
@@ -112,19 +193,6 @@ Rails.application.configure do
|
|
112
193
|
end
|
113
194
|
~~~
|
114
195
|
|
115
|
-
## Routes
|
116
|
-
|
117
|
-
The authentication routes must be mounted to your project.
|
118
|
-
|
119
|
-
In `config/routes.rb`, add the following line:
|
120
|
-
|
121
|
-
~~~ruby
|
122
|
-
# config/routes.rb
|
123
|
-
mount DeviseTokenAuth::Engine => "/auth"
|
124
|
-
~~~
|
125
|
-
|
126
|
-
Note that you can mount this engine to any route that you like. `/auth` is used to conform to the defaults of the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module.
|
127
|
-
|
128
196
|
## CORS
|
129
197
|
|
130
198
|
If your API and client live on different domains, you will need to configure your Rails API to allow cross origin requests. The [rack-cors](https://github.com/cyu/rack-cors) gem can be used to accomplish this.
|
@@ -156,57 +224,33 @@ Make extra sure that the `Access-Control-Expose-Headers` includes `Authorization
|
|
156
224
|
|
157
225
|
CORS may not be possible with older browsers (IE8, IE9). I usually set up a proxy for those browsers. See the [ng-token-auth readme](https://github.com/lynndylanhurley/ng-token-auth) for more information.
|
158
226
|
|
159
|
-
# Usage
|
160
|
-
|
161
|
-
The following routes are available for use by your client. These routes live relative to the path at which this engine is mounted (`/auth` in the example above).
|
162
|
-
|
163
|
-
| path | method | purpose |
|
164
|
-
|:-----|:-------|:--------|
|
165
|
-
| / | POST | email registration. accepts **`email`**, **`password`**, and **`password_confirmation`** params. |
|
166
|
-
| /sign_in | POST | email authentication. accepts **`email`** and **`password`** as params. |
|
167
|
-
| /sign_out | DELETE | invalidate tokens (end session) |
|
168
|
-
| /:provider | GET | set this route as the destination for client authentication. ideally this will happen in an external window or popup. |
|
169
|
-
| /:provider/callback | GET/POST | destination for the oauth2 provider's callback uri. `postMessage` events containing the authenticated user's data will be sent back to the main client window from this page. |
|
170
|
-
| /validate_token | POST | use this route to validate tokens on return visits to the client. accepts **`uid`** and **`auth_token`** as params. these values should correspond to the columns in your `User` table of the same names. |
|
171
|
-
| /password | POST | send password email to users that registered by email. accepts **`email`** and **`redirect_url`** as params. The user matching the `email` param will be sent instructions on how to reset their password. `redirect_url` is the url to which the user will be redirected after visiting the link contained in the email. |
|
172
|
-
| /password | PUT | password change for users that registered by email. accepts **`password`** and **`password_confirmation`** as params. |
|
173
|
-
| /password/edit | GET | verify user by password reset token. must contain **`reset_password_token`** and **`redirect_url`** as params. These values will be set automatically by the confirmation email that is generated by the password reset request. |
|
227
|
+
# Usage cont.
|
174
228
|
|
175
|
-
|
229
|
+
## Mounting Routes
|
176
230
|
|
231
|
+
The authentication routes must be mounted to your project. This gem includes a route helper for this purpose:
|
177
232
|
|
178
|
-
|
233
|
+
**`mount_devise_token_auth_for`** - similar to `devise_for`, this method is used to append the routes necessary for user authentication. This method accepts the following arguments:
|
179
234
|
|
180
|
-
|
235
|
+
| Argument | Type | Description |
|
236
|
+
|---|---|---|
|
237
|
+
|`class_name`| string | The name of the class to use for authentication. This class must include the [model concern described here](#model-concerns). |
|
238
|
+
| `options` | object | The [routes to be used for authentication](#usage) will be prefixed by the path specified in the `at` param of this object. |
|
181
239
|
|
182
|
-
|
183
|
-
~~~
|
184
|
-
|
240
|
+
**Example**:
|
241
|
+
~~~ruby
|
242
|
+
# config/routes.rb
|
243
|
+
mount_devise_token_auth_for 'User', at: '/auth'
|
185
244
|
~~~
|
186
245
|
|
187
|
-
|
188
|
-
|
189
|
-
* **`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.
|
190
|
-
* **`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.)
|
191
|
-
* **`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.
|
192
|
-
* **`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.
|
193
|
-
|
194
|
-
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.
|
195
|
-
|
196
|
-
## Handling batch requests
|
246
|
+
You can mount this engine to any route that you like. `/auth` is used by default to conform with the defaults of the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module.
|
197
247
|
|
198
|
-
Sometimes it's necessary to send several concurrent requests to the API. In these cases, the concurrent requests will need to share the same auth token (tokens are usually changed after each request). [Read here](https://github.com/lynndylanhurley/ng-token-auth#about-batch-requests) for an overview on how this gem deals with batch requests.
|
199
248
|
|
200
|
-
##
|
249
|
+
## Controller Concerns
|
201
250
|
|
202
|
-
|
203
|
-
* **`valid_token?`**: check if an authentication token is valid. Accepts `token` and `client` as arguments. Returns a boolean.
|
204
|
-
* **`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.
|
205
|
-
* **`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.
|
251
|
+
##### DeviseTokenAuth::Concerns::SetUserByToken
|
206
252
|
|
207
|
-
|
208
|
-
|
209
|
-
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.
|
253
|
+
This gem includes a [Rails concern](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) called `DeviseTokenAuth::Concerns::SetUserByToken`. This concern can be used can be used in controllers to identify users by their `Authorization` header.
|
210
254
|
|
211
255
|
This concern runs a [before_action](http://guides.rubyonrails.org/action_controller_overview.html#filters), setting the `@user` variable for use in your controllers. The user will be signed in via devise for the duration of the request.
|
212
256
|
|
@@ -239,12 +283,170 @@ class TestController < ApplicationController
|
|
239
283
|
end
|
240
284
|
~~~
|
241
285
|
|
286
|
+
The authentication information should be included by the client in the `Authorization` header of each request. The header should follow this format:
|
287
|
+
|
288
|
+
##### Authorization header example:
|
289
|
+
~~~
|
290
|
+
token=wwwww client=xxxxx expiry=yyyyy uid=zzzzz
|
291
|
+
~~~
|
292
|
+
|
293
|
+
The `Authorization` header is made up of the following components:
|
294
|
+
|
295
|
+
* **`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.
|
296
|
+
* **`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.)
|
297
|
+
* **`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.
|
298
|
+
* **`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.
|
299
|
+
|
300
|
+
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.
|
301
|
+
|
302
|
+
## Model Concerns
|
303
|
+
|
304
|
+
##### DeviseTokenAuth::Concerns::SetUserByToken
|
305
|
+
|
306
|
+
Typical use of this gem will not require the use of any of the following model methods. All authentication should be handled invisibly by the [controller concerns](#controller-concerns) described above.
|
307
|
+
|
308
|
+
Models that include the `DeviseTokenAuth::Concerns::SetUserByToken` concern will have access to the following public methods (read the above section for context on `token` and `client`):
|
309
|
+
|
310
|
+
* **`valid_token?`**: check if an authentication token is valid. Accepts a `token` and `client` as arguments. Returns a boolean.
|
311
|
+
|
312
|
+
**Example**:
|
313
|
+
~~~ruby
|
314
|
+
# extract token + client_id from auth header
|
315
|
+
client_id = request.headers['Authorization'][/client=(.*?) /,1]
|
316
|
+
token = request.headers['Authorization'][/token=(.*?) /,1]
|
317
|
+
|
318
|
+
@user.valid_token?(token, client_id)
|
319
|
+
~~~
|
320
|
+
|
321
|
+
* **`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.
|
322
|
+
|
323
|
+
**Example**:
|
324
|
+
~~~ruby
|
325
|
+
# extract client_id from auth header
|
326
|
+
client_id = request.headers['Authorization'][/client=(.*?) /,1]
|
327
|
+
|
328
|
+
# update token, generate updated auth headers for response
|
329
|
+
new_auth_header = @user.create_new_auth_token(client_id)
|
330
|
+
|
331
|
+
# update response with the header that will be required by the next request
|
332
|
+
response.headers["Authorization"] = new_auth_header
|
333
|
+
~~~
|
334
|
+
|
335
|
+
* **`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.
|
336
|
+
|
337
|
+
**Example**:
|
338
|
+
~~~ruby
|
339
|
+
# create client id and token
|
340
|
+
client_id = SecureRandom.urlsafe_base64(nil, false)
|
341
|
+
token = SecureRandom.urlsafe_base64(nil, false)
|
342
|
+
|
343
|
+
# store client + token in user's token hash
|
344
|
+
@user.tokens[client_id] = {
|
345
|
+
token: BCrypt::Password.create(token),
|
346
|
+
expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
347
|
+
}
|
348
|
+
|
349
|
+
# update token, generate updated auth headers for response
|
350
|
+
new_auth_header = @user.create_new_auth_token(token, client_id)
|
351
|
+
|
352
|
+
# update response with the header that will be required by the next request
|
353
|
+
response.headers["Authorization"] = new_auth_header
|
354
|
+
~~~
|
355
|
+
|
356
|
+
## Using multiple models
|
357
|
+
|
358
|
+
This gem supports the use of multiple user models. One possible use case is to authorize visitors using a model called `User`, and to authorize administrators with a model called `Admin` from the same app. Take the following steps to add another authentication model to your app:
|
359
|
+
|
360
|
+
1. Run the install generator for the new model.
|
361
|
+
~~~
|
362
|
+
rails g devise_token_auth:install Admin admin_auth
|
363
|
+
~~~
|
364
|
+
|
365
|
+
This will create the `Admin` model and define the model's authentication routes with the base path `/admin_auth`.
|
366
|
+
|
367
|
+
1. Define the routes to be used by the `Admin` user within a [`devise_scope`](https://github.com/plataformatec/devise#configuring-routes).
|
368
|
+
|
369
|
+
**Example**:
|
370
|
+
~~~ruby
|
371
|
+
Rails.application.routes.draw do
|
372
|
+
# when using multiple models, controllers will default to the first available
|
373
|
+
# devise mapping. routes for subsequent devise mappings will need to defined
|
374
|
+
# within a `devise_scope` block
|
375
|
+
|
376
|
+
# define :users as the first devise mapping:
|
377
|
+
mount_devise_token_auth_for 'User', at: '/auth'
|
378
|
+
|
379
|
+
# define :admins as the second devise mapping. routes using this class will
|
380
|
+
# need to be defined within a devise_scope as shown below
|
381
|
+
mount_devise_token_auth_for "Admin", at: '/admin_auth'
|
382
|
+
|
383
|
+
# this route will authorize visitors using the User class
|
384
|
+
get 'demo/members_only', to: 'demo#members_only'
|
385
|
+
|
386
|
+
# routes within this block will authorize visitors using the Admin class
|
387
|
+
devise_scope :admin do
|
388
|
+
get 'demo/admins_only', to: 'demo#admins_only'
|
389
|
+
end
|
390
|
+
end
|
391
|
+
~~~
|
392
|
+
|
393
|
+
# Conceptual
|
394
|
+
|
395
|
+
None of the following information is required to use this gem, but read on if you're curious.
|
396
|
+
|
397
|
+
## About token management
|
398
|
+
|
399
|
+
Tokens should be invalidated after each request to the API. The following diagram illustrates this concept:
|
400
|
+
|
401
|
+
![password reset flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/token-update-detail.jpg)
|
402
|
+
|
403
|
+
During each request, a new token is generated. The `Authorization` header that should be used in the next request is returned in the `Authorization` header of the response to the previous request. The last request in the diagram fails because it tries to use a token that was invalidated by the previous request.
|
404
|
+
|
405
|
+
The only case where an expired token is allowed is during [batch requests](#about-batch-requests).
|
406
|
+
|
407
|
+
These measures are taken by default when using this gem.
|
408
|
+
|
409
|
+
## About batch requests
|
410
|
+
|
411
|
+
By default, the API should update the auth token for each request ([read more](#about-token-management)). But sometimes it's neccessary to make several concurrent requests to the API, for example:
|
412
|
+
|
413
|
+
#####Batch request example
|
414
|
+
~~~javascript
|
415
|
+
$scope.getResourceData = function() {
|
416
|
+
|
417
|
+
$http.get('/api/restricted_resource_1').success(function(resp) {
|
418
|
+
// handle response
|
419
|
+
$scope.resource1 = resp.data;
|
420
|
+
});
|
421
|
+
|
422
|
+
$http.get('/api/restricted_resource_2').success(function(resp) {
|
423
|
+
// handle response
|
424
|
+
$scope.resource2 = resp.data;
|
425
|
+
});
|
426
|
+
};
|
427
|
+
~~~
|
428
|
+
|
429
|
+
In this case, it's impossible to update the `Authorization` header for the second request with the `Authorization` header of the first response because the second request will begin before the first one is complete. The server must allow these batches of concurrent requests to share the same auth token. This diagram illustrates how batch requests are identified by the server:
|
430
|
+
|
431
|
+
![batch request overview](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/batch-request-overview.jpg)
|
432
|
+
|
433
|
+
The "5 second" buffer in the diagram is the default used this gem.
|
434
|
+
|
435
|
+
The following diagram details the relationship between the client, server, and access tokens used over time when dealing with batch requests:
|
436
|
+
|
437
|
+
![batch request detail](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/batch-request-detail.jpg)
|
438
|
+
|
439
|
+
Note that when the server identifies that a request is part of a batch request, the user's auth token is not updated. The auth token will be updated for the first request in the batch, and then that same token will be returned in the responses for each subsequent request in the batch (as shown in the diagram).
|
440
|
+
|
441
|
+
This gem automatically manages batch requests. You can change the time buffer for what is considered a batch request using the `batch_request_buffer_throttle` parameter in `config/initializers/devise_token_auth.rb`.
|
442
|
+
|
443
|
+
|
242
444
|
# Security
|
243
445
|
|
244
446
|
This gem takes the following steps to ensure security.
|
245
447
|
|
246
448
|
This gem uses auth tokens that are:
|
247
|
-
* changed after every request,
|
449
|
+
* [changed after every request](#about-token-management),
|
248
450
|
* [of cryptographic strength](http://ruby-doc.org/stdlib-2.1.0/libdoc/securerandom/rdoc/SecureRandom.html),
|
249
451
|
* hashed using [BCrypt](https://github.com/codahale/bcrypt-ruby) (not stored in plain-text),
|
250
452
|
* securely compared (to protect against timing attacks),
|
@@ -256,11 +458,6 @@ This gem further mitigates timing attacks by using [this technique](https://gist
|
|
256
458
|
|
257
459
|
But the most important step is to use HTTPS. You are on the hook for that.
|
258
460
|
|
259
|
-
# TODO
|
260
|
-
|
261
|
-
* Write tests
|
262
|
-
* `User` model is currently baked into this gem. Allow for dynamic definition using concerns (or other means).
|
263
|
-
* Find a way to expose devise + omniauth configs, maybe using generators.
|
264
461
|
|
265
462
|
# Contributing
|
266
463
|
Just send a pull request. I will grant you commit access if you send quality pull requests.
|