grape_devise_token_auth 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|