grape_devise_token_auth 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -6
- data/lib/grape_devise_token_auth/auth_headers.rb +49 -0
- data/lib/grape_devise_token_auth/auth_helpers.rb +12 -1
- data/lib/grape_devise_token_auth/authorizer_data.rb +27 -0
- data/lib/grape_devise_token_auth/configuration.rb +19 -0
- data/lib/grape_devise_token_auth/devise_interface.rb +31 -0
- data/lib/grape_devise_token_auth/middleware.rb +27 -105
- data/lib/grape_devise_token_auth/token_authorizer.rb +46 -0
- data/lib/grape_devise_token_auth/unauthorized.rb +4 -0
- data/lib/grape_devise_token_auth/version.rb +1 -1
- data/lib/grape_devise_token_auth.rb +29 -12
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6c0879cd01d03c9f719c86c7ee0fe21ddba87d2
|
4
|
+
data.tar.gz: bea840a724adba030833ceec4d7418a1eedbd19f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed1bd798383fee3650292ffe6553fa40ad3eaaba13416d4f24358bbaea61151e471624c5d515d03d72661381351cebab4d4883693af29bc523f3f292769a1349
|
7
|
+
data.tar.gz: 4be80289b09ad4ad60e96484290ae900faa32352a36eef00f4f6f38bc5d6770728873369cca078f663e6c6ebf1d40087bdda9bb0cca3bd53acfe3307f417321c
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ Place this line in an initializer in your rails app or at least somewhere before
|
|
32
32
|
the grape API will get loaded:
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
GrapeDeviseTokenAuth.setup!
|
35
|
+
GrapeDeviseTokenAuth.setup!
|
36
36
|
```
|
37
37
|
|
38
38
|
Within the Grape API:
|
@@ -47,13 +47,30 @@ class Posts < Grape::API
|
|
47
47
|
end
|
48
48
|
```
|
49
49
|
|
50
|
-
including the helpers line allows you to use methods the `current_user` and
|
51
|
-
`authenticated?` within the API.
|
52
|
-
|
53
50
|
The resource class option allows you to specific the scope that will be
|
54
51
|
authenticated, this corresponds to your devise mapping.
|
55
52
|
|
56
|
-
|
53
|
+
Individual endpoints can now be authenticated by calling `authenticate_YOUR_MAPPING_HERE!` (e.g. `authenticate_user!`)
|
54
|
+
within them.
|
55
|
+
|
56
|
+
For Example:
|
57
|
+
|
58
|
+
```
|
59
|
+
get '/' do
|
60
|
+
authenticate_user!
|
61
|
+
present Post.all
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
alternatively to portect all routes place the call in a before block:
|
66
|
+
|
67
|
+
```
|
68
|
+
before do
|
69
|
+
authenticate_user!
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
[A full example setup can be found here][6]
|
57
74
|
|
58
75
|
## Testing and Example
|
59
76
|
|
@@ -81,4 +98,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
81
98
|
[3]: https://github.com/plataformatec/devise
|
82
99
|
[4]: https://github.com/lynndylanhurley
|
83
100
|
[5]: https://github.com/mcordell/rails_grape_auth
|
84
|
-
|
101
|
+
[6]: https://github.com/mcordell/rails_grape_auth/blob/7ca6b2f3d989fc23824aaf40fc353fc3e8de40ec/app/api/grape_api/posts.rb
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GrapeDeviseTokenAuth
|
2
|
+
class AuthHeaders
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(warden, mapping, request_start, data)
|
6
|
+
@resource = warden.session_serializer.fetch(mapping)
|
7
|
+
@request_start = request_start
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def headers
|
12
|
+
return {} unless resource && resource.valid? && client_id
|
13
|
+
auth_headers_from_resource
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def_delegators :@data, :token, :client_id
|
19
|
+
attr_reader :request_start, :resource
|
20
|
+
|
21
|
+
def batch_request?
|
22
|
+
@batch_request ||= resource.tokens[client_id] &&
|
23
|
+
resource.tokens[client_id]['updated_at'] &&
|
24
|
+
within_batch_request_window?
|
25
|
+
end
|
26
|
+
|
27
|
+
def within_batch_request_window?
|
28
|
+
end_of_window = Time.parse(resource.tokens[client_id]['updated_at']) +
|
29
|
+
GrapeDeviseTokenAuth.batch_request_buffer_throttle
|
30
|
+
|
31
|
+
request_start < end_of_window
|
32
|
+
end
|
33
|
+
|
34
|
+
def auth_headers_from_resource
|
35
|
+
auth_headers = {}
|
36
|
+
resource.with_lock do
|
37
|
+
if !GrapeDeviseTokenAuth.change_headers_on_each_request
|
38
|
+
auth_headers = resource.extend_batch_buffer(token, client_id)
|
39
|
+
elsif batch_request?
|
40
|
+
resource.extend_batch_buffer(token, client_id)
|
41
|
+
# don't set any headers in a batch request
|
42
|
+
else
|
43
|
+
auth_headers = resource.create_new_auth_token(client_id)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
auth_headers
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -3,7 +3,18 @@ module GrapeDeviseTokenAuth
|
|
3
3
|
def self.included(_base)
|
4
4
|
Devise.mappings.keys.each do |mapping|
|
5
5
|
define_method("current_#{mapping}") do
|
6
|
-
warden.session_serializer.fetch(
|
6
|
+
warden.session_serializer.fetch(mapping)
|
7
|
+
end
|
8
|
+
|
9
|
+
define_method("authenticate_#{mapping}!") do
|
10
|
+
authorizer_data = AuthorizerData.from_env(env)
|
11
|
+
devise_interface = DeviseInterface.new(authorizer_data)
|
12
|
+
token_authorizer = TokenAuthorizer.new(authorizer_data,
|
13
|
+
devise_interface)
|
14
|
+
user = token_authorizer.authenticate_from_token(mapping)
|
15
|
+
fail Unauthorized unless user
|
16
|
+
devise_interface.set_user_in_warden(mapping, user)
|
17
|
+
user
|
7
18
|
end
|
8
19
|
end
|
9
20
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module GrapeDeviseTokenAuth
|
2
|
+
class AuthorizerData
|
3
|
+
attr_reader :uid, :client_id, :token, :expiry, :warden
|
4
|
+
|
5
|
+
def initialize(uid, client_id, token, expiry, warden)
|
6
|
+
@uid = uid
|
7
|
+
@client_id = client_id
|
8
|
+
@token = token
|
9
|
+
@expiry = expiry
|
10
|
+
@warden = warden
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.from_env(env)
|
14
|
+
new(
|
15
|
+
env[Configuration::UID_KEY],
|
16
|
+
env[Configuration::CLIENT_KEY] || 'default',
|
17
|
+
env[Configuration::ACCESS_TOKEN_KEY],
|
18
|
+
env[Configuration::EXPIRY_KEY],
|
19
|
+
env['warden']
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def token_prerequisites_present?
|
24
|
+
token && uid
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module GrapeDeviseTokenAuth
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :batch_request_buffer_throttle, :change_headers_on_each_request, :authenticate_all
|
4
|
+
ACCESS_TOKEN_KEY = 'HTTP_ACCESS_TOKEN'
|
5
|
+
EXPIRY_KEY = 'HTTP_EXPIRY'
|
6
|
+
UID_KEY = 'HTTP_UID'
|
7
|
+
CLIENT_KEY = 'HTTP_CLIENT'
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@batch_request_buffer_throttle ||= DeviseTokenAuth.batch_request_buffer_throttle
|
11
|
+
@change_headers_on_each_request ||= DeviseTokenAuth.change_headers_on_each_request
|
12
|
+
@authenticate_all = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def auth_all?
|
16
|
+
@authenticate_all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GrapeDeviseTokenAuth
|
2
|
+
class DeviseInterface
|
3
|
+
def initialize(data)
|
4
|
+
@warden = data.warden
|
5
|
+
@client_id = data.client_id
|
6
|
+
end
|
7
|
+
|
8
|
+
# extracted and simplified from Devise
|
9
|
+
def set_user_in_warden(scope, resource)
|
10
|
+
scope = Devise::Mapping.find_scope!(scope)
|
11
|
+
warden.session_serializer.store(resource, scope)
|
12
|
+
end
|
13
|
+
|
14
|
+
def mapping_to_class(m)
|
15
|
+
mapping = m ? Devise.mappings[m] : Devise.mappings.values.first
|
16
|
+
@resource_class = mapping.to
|
17
|
+
end
|
18
|
+
|
19
|
+
def exisiting_warden_user(resource_class)
|
20
|
+
warden_user = warden.user(resource_class.to_s.underscore.to_sym)
|
21
|
+
return unless warden_user && warden_user.tokens[@client_id].nil?
|
22
|
+
resource = warden_user
|
23
|
+
resource.create_new_auth_token
|
24
|
+
resource
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :warden
|
30
|
+
end
|
31
|
+
end
|
@@ -1,125 +1,59 @@
|
|
1
1
|
module GrapeDeviseTokenAuth
|
2
2
|
class Middleware
|
3
|
-
|
4
|
-
EXPIRY_KEY = 'HTTP_EXPIRY'
|
5
|
-
UID_KEY = 'HTTP_UID'
|
6
|
-
CLIENT_KEY = 'HTTP_CLIENT'
|
3
|
+
extend Forwardable
|
7
4
|
|
8
5
|
def initialize(app, resource_name)
|
9
6
|
@app = app
|
10
|
-
|
7
|
+
@resource_name = resource_name
|
11
8
|
end
|
12
9
|
|
13
10
|
def call(env)
|
14
11
|
setup(env)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
begin
|
13
|
+
auth_all
|
14
|
+
responses_with_auth_headers(*@app.call(env))
|
15
|
+
rescue Unauthorized => _e
|
16
|
+
return unauthorized
|
17
|
+
end
|
19
18
|
end
|
20
19
|
|
21
20
|
private
|
22
21
|
|
23
|
-
attr_reader :
|
24
|
-
|
25
|
-
def setup(env)
|
26
|
-
@request_start = Time.now
|
27
|
-
@uid = env[UID_KEY]
|
28
|
-
@client_id = env[CLIENT_KEY] || 'default'
|
29
|
-
@token = env[ACCESS_TOKEN_KEY]
|
30
|
-
@expiry = env[EXPIRY_KEY]
|
31
|
-
@warden = env['warden']
|
32
|
-
end
|
33
|
-
|
34
|
-
def sign_in_user(user)
|
35
|
-
# user already logged in from devise:
|
36
|
-
return resource if resource
|
37
|
-
@resource = user
|
38
|
-
set_user_in_warden(:user, user)
|
39
|
-
end
|
40
|
-
|
41
|
-
#extracted and simplified from Devise
|
42
|
-
def set_user_in_warden(scope, resource)
|
43
|
-
scope = Devise::Mapping.find_scope!(scope)
|
44
|
-
warden.session_serializer.store(resource, scope)
|
45
|
-
end
|
46
|
-
|
47
|
-
def resource_from_existing_devise_user
|
48
|
-
warden_user = warden.user(resource_class.to_s.underscore.to_sym)
|
49
|
-
return unless warden_user && warden_user.tokens[client_id].nil?
|
50
|
-
@resource = warden_user
|
51
|
-
@resource.create_new_auth_token
|
52
|
-
end
|
53
|
-
|
54
|
-
def authenticate_from_token(mapping = nil)
|
55
|
-
resource_class_from_mapping(mapping)
|
56
|
-
return nil unless resource_class
|
57
|
-
|
58
|
-
resource_from_existing_devise_user
|
59
|
-
return resource if correct_resource_type_logged_in?
|
22
|
+
attr_reader :authorizer_data, :token_authorizer, :resource, :request_start
|
23
|
+
def_delegators :@authorizer_data, :warden, :token, :client_id
|
60
24
|
|
61
|
-
|
62
|
-
|
63
|
-
user =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
user
|
68
|
-
end
|
69
|
-
|
70
|
-
def token_request_valid?
|
71
|
-
token && uid
|
72
|
-
end
|
73
|
-
|
74
|
-
def correct_resource_type_logged_in?
|
75
|
-
resource && resource.class == resource_class
|
76
|
-
end
|
77
|
-
|
78
|
-
def resource_class_from_mapping(m)
|
79
|
-
mapping = m ? Devise.mappings[m] : Devise.mappings.values.first
|
80
|
-
@resource_class = mapping.to
|
25
|
+
def auth_all
|
26
|
+
return if skip_auth_all?
|
27
|
+
user = token_authorizer.authenticate_from_token(@resource_name)
|
28
|
+
fail Unauthorized unless user
|
29
|
+
sign_in_user(user)
|
81
30
|
end
|
82
31
|
|
83
|
-
def
|
84
|
-
|
32
|
+
def skip_auth_all?
|
33
|
+
!GrapeDeviseTokenAuth.configuration.auth_all?
|
85
34
|
end
|
86
35
|
|
87
|
-
def
|
88
|
-
|
36
|
+
def setup(env)
|
37
|
+
@request_start = Time.now
|
38
|
+
@authorizer_data = AuthorizerData.from_env(env)
|
39
|
+
@devise_interface = DeviseInterface.new(@authorizer_data)
|
40
|
+
@token_authorizer = TokenAuthorizer.new(@authorizer_data,
|
41
|
+
@devise_interface)
|
89
42
|
end
|
90
43
|
|
91
|
-
def
|
92
|
-
|
44
|
+
def sign_in_user(user)
|
45
|
+
@devise_interface.set_user_in_warden(@resource_name, user)
|
93
46
|
end
|
94
47
|
|
95
48
|
def responses_with_auth_headers(status, headers, response)
|
49
|
+
auth_headers = AuthHeaders.new(warden, @resource_name, request_start, authorizer_data)
|
96
50
|
[
|
97
51
|
status,
|
98
|
-
headers.merge(auth_headers),
|
52
|
+
headers.merge(auth_headers.headers),
|
99
53
|
response
|
100
54
|
]
|
101
55
|
end
|
102
56
|
|
103
|
-
def auth_headers
|
104
|
-
return {} unless resource && resource.valid? && client_id
|
105
|
-
auth_headers_from_resource
|
106
|
-
end
|
107
|
-
|
108
|
-
def auth_headers_from_resource
|
109
|
-
auth_headers = {}
|
110
|
-
resource.with_lock do
|
111
|
-
if !DeviseTokenAuth.change_headers_on_each_request
|
112
|
-
auth_headers = resource.extend_batch_buffer(token, client_id)
|
113
|
-
elsif batch_request?
|
114
|
-
resource.extend_batch_buffer(token, client_id)
|
115
|
-
# don't set any headers in a batch request
|
116
|
-
else
|
117
|
-
auth_headers = resource.create_new_auth_token(client_id)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
auth_headers
|
121
|
-
end
|
122
|
-
|
123
57
|
def unauthorized
|
124
58
|
[401,
|
125
59
|
{ 'Content-Type' => 'application/json'
|
@@ -127,17 +61,5 @@ module GrapeDeviseTokenAuth
|
|
127
61
|
[]
|
128
62
|
]
|
129
63
|
end
|
130
|
-
|
131
|
-
def batch_request?
|
132
|
-
@batch_request ||= resource.tokens[client_id] &&
|
133
|
-
resource.tokens[client_id]['updated_at'] &&
|
134
|
-
within_batch_request_window?
|
135
|
-
end
|
136
|
-
|
137
|
-
def within_batch_request_window?
|
138
|
-
end_of_window = Time.parse(resource.tokens[client_id]['updated_at']) +
|
139
|
-
DeviseTokenAuth.batch_request_buffer_throttle
|
140
|
-
request_start < end_of_window
|
141
|
-
end
|
142
64
|
end
|
143
65
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GrapeDeviseTokenAuth
|
2
|
+
class TokenAuthorizer
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(data, devise_interface)
|
6
|
+
@data = data
|
7
|
+
@devise_interface = devise_interface
|
8
|
+
end
|
9
|
+
|
10
|
+
def authenticate_from_token(mapping = nil)
|
11
|
+
@resource_class = devise_interface.mapping_to_class(mapping)
|
12
|
+
return nil unless resource_class
|
13
|
+
|
14
|
+
resource_from_existing_devise_user
|
15
|
+
return resource if correct_resource_type_logged_in?
|
16
|
+
|
17
|
+
return nil unless data.token_prerequisites_present?
|
18
|
+
load_user_from_uid
|
19
|
+
return nil unless user_authenticated?
|
20
|
+
|
21
|
+
user
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_accessor :resource_class
|
27
|
+
attr_reader :data, :resource, :user, :devise_interface
|
28
|
+
def_delegators :@data, :warden, :uid, :token, :client_id
|
29
|
+
|
30
|
+
def user_authenticated?
|
31
|
+
user && user.valid_token?(token, client_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_user_from_uid
|
35
|
+
@user = resource_class.find_by_uid(uid)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resource_from_existing_devise_user
|
39
|
+
@resource = @devise_interface.exisiting_warden_user(resource_class)
|
40
|
+
end
|
41
|
+
|
42
|
+
def correct_resource_type_logged_in?
|
43
|
+
resource && resource.class == resource_class
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,18 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
%w(version middleware auth_helpers authorizer_data unauthorized
|
2
|
+
token_authorizer configuration auth_headers devise_interface).each do |file|
|
3
|
+
require "grape_devise_token_auth/#{file}"
|
4
|
+
end
|
5
|
+
|
4
6
|
require 'grape'
|
5
7
|
|
6
8
|
module GrapeDeviseTokenAuth
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
class << self
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegators :configuration, :batch_request_buffer_throttle, :change_headers_on_each_request
|
13
|
+
|
14
|
+
def configuration
|
15
|
+
@configuration ||= Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
yield(configuration)
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup!(middleware = false)
|
23
|
+
yield(configuration) if block_given?
|
24
|
+
add_auth_strategy
|
25
|
+
end
|
10
26
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
27
|
+
def add_auth_strategy
|
28
|
+
Grape::Middleware::Auth::Strategies.add(
|
29
|
+
:grape_devise_token_auth,
|
30
|
+
GrapeDeviseTokenAuth::Middleware,
|
31
|
+
->(options) { [options[:resource_class]] }
|
32
|
+
)
|
33
|
+
end
|
17
34
|
end
|
18
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape_devise_token_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Cordell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -98,8 +98,14 @@ files:
|
|
98
98
|
- bin/setup
|
99
99
|
- grape_devise_token_auth.gemspec
|
100
100
|
- lib/grape_devise_token_auth.rb
|
101
|
+
- lib/grape_devise_token_auth/auth_headers.rb
|
101
102
|
- lib/grape_devise_token_auth/auth_helpers.rb
|
103
|
+
- lib/grape_devise_token_auth/authorizer_data.rb
|
104
|
+
- lib/grape_devise_token_auth/configuration.rb
|
105
|
+
- lib/grape_devise_token_auth/devise_interface.rb
|
102
106
|
- lib/grape_devise_token_auth/middleware.rb
|
107
|
+
- lib/grape_devise_token_auth/token_authorizer.rb
|
108
|
+
- lib/grape_devise_token_auth/unauthorized.rb
|
103
109
|
- lib/grape_devise_token_auth/version.rb
|
104
110
|
homepage: https://github.com/mcordell/grape_devise_token_auth
|
105
111
|
licenses:
|