grape_oauth2 0.1.1 → 0.2.0
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/.gitignore +11 -11
- data/Gemfile +23 -23
- data/Rakefile +11 -11
- data/grape_oauth2.gemspec +26 -27
- data/lib/grape_oauth2.rb +129 -129
- data/lib/grape_oauth2/configuration.rb +143 -143
- data/lib/grape_oauth2/configuration/class_accessors.rb +36 -36
- data/lib/grape_oauth2/configuration/validation.rb +71 -71
- data/lib/grape_oauth2/endpoints/authorize.rb +34 -34
- data/lib/grape_oauth2/endpoints/token.rb +72 -72
- data/lib/grape_oauth2/gem_version.rb +24 -24
- data/lib/grape_oauth2/generators/authorization.rb +44 -44
- data/lib/grape_oauth2/generators/base.rb +26 -26
- data/lib/grape_oauth2/generators/token.rb +62 -62
- data/lib/grape_oauth2/helpers/access_token_helpers.rb +52 -54
- data/lib/grape_oauth2/helpers/oauth_params.rb +41 -41
- data/lib/grape_oauth2/mixins/active_record/access_grant.rb +47 -47
- data/lib/grape_oauth2/mixins/active_record/access_token.rb +75 -75
- data/lib/grape_oauth2/mixins/active_record/client.rb +36 -35
- data/lib/grape_oauth2/mixins/mongoid/access_grant.rb +58 -58
- data/lib/grape_oauth2/mixins/mongoid/access_token.rb +88 -88
- data/lib/grape_oauth2/mixins/mongoid/client.rb +44 -41
- data/lib/grape_oauth2/mixins/sequel/access_grant.rb +68 -68
- data/lib/grape_oauth2/mixins/sequel/access_token.rb +86 -86
- data/lib/grape_oauth2/mixins/sequel/client.rb +54 -46
- data/lib/grape_oauth2/responses/authorization.rb +11 -10
- data/lib/grape_oauth2/responses/base.rb +56 -56
- data/lib/grape_oauth2/responses/token.rb +10 -10
- data/lib/grape_oauth2/scopes.rb +74 -74
- data/lib/grape_oauth2/strategies/authorization_code.rb +38 -38
- data/lib/grape_oauth2/strategies/base.rb +47 -47
- data/lib/grape_oauth2/strategies/client_credentials.rb +20 -20
- data/lib/grape_oauth2/strategies/password.rb +22 -22
- data/lib/grape_oauth2/strategies/refresh_token.rb +47 -47
- data/lib/grape_oauth2/unique_token.rb +20 -20
- data/lib/grape_oauth2/version.rb +14 -14
- data/spec/configuration/config_spec.rb +231 -231
- data/spec/configuration/version_spec.rb +12 -12
- data/spec/dummy/endpoints/custom_authorization.rb +25 -25
- data/spec/dummy/endpoints/custom_token.rb +35 -35
- data/spec/dummy/endpoints/status.rb +25 -25
- data/spec/dummy/grape_oauth2_config.rb +11 -11
- data/spec/dummy/orm/active_record/app/config/db.rb +7 -7
- data/spec/dummy/orm/active_record/app/models/access_code.rb +3 -3
- data/spec/dummy/orm/active_record/app/models/access_token.rb +3 -3
- data/spec/dummy/orm/active_record/app/models/application.rb +3 -3
- data/spec/dummy/orm/active_record/app/models/application_record.rb +3 -3
- data/spec/dummy/orm/active_record/app/models/user.rb +10 -10
- data/spec/dummy/orm/active_record/app/twitter.rb +36 -36
- data/spec/dummy/orm/active_record/config.ru +7 -7
- data/spec/dummy/orm/active_record/db/schema.rb +53 -53
- data/spec/dummy/orm/mongoid/app/config/db.rb +6 -6
- data/spec/dummy/orm/mongoid/app/config/mongoid.yml +21 -21
- data/spec/dummy/orm/mongoid/app/models/access_code.rb +3 -3
- data/spec/dummy/orm/mongoid/app/models/access_token.rb +3 -3
- data/spec/dummy/orm/mongoid/app/models/application.rb +3 -3
- data/spec/dummy/orm/mongoid/app/models/user.rb +11 -11
- data/spec/dummy/orm/mongoid/app/twitter.rb +34 -34
- data/spec/dummy/orm/mongoid/config.ru +5 -5
- data/spec/dummy/orm/sequel/app/config/db.rb +1 -1
- data/spec/dummy/orm/sequel/app/models/access_code.rb +4 -4
- data/spec/dummy/orm/sequel/app/models/access_token.rb +4 -4
- data/spec/dummy/orm/sequel/app/models/application.rb +4 -4
- data/spec/dummy/orm/sequel/app/models/application_record.rb +2 -2
- data/spec/dummy/orm/sequel/app/models/user.rb +11 -11
- data/spec/dummy/orm/sequel/app/twitter.rb +47 -47
- data/spec/dummy/orm/sequel/config.ru +5 -5
- data/spec/dummy/orm/sequel/db/schema.rb +50 -50
- data/spec/lib/scopes_spec.rb +50 -50
- data/spec/mixins/active_record/access_token_spec.rb +185 -185
- data/spec/mixins/active_record/client_spec.rb +104 -95
- data/spec/mixins/mongoid/access_token_spec.rb +185 -185
- data/spec/mixins/mongoid/client_spec.rb +104 -95
- data/spec/mixins/sequel/access_token_spec.rb +185 -185
- data/spec/mixins/sequel/client_spec.rb +105 -96
- data/spec/requests/flows/authorization_code_spec.rb +67 -67
- data/spec/requests/flows/client_credentials_spec.rb +101 -101
- data/spec/requests/flows/password_spec.rb +210 -210
- data/spec/requests/flows/refresh_token_spec.rb +222 -222
- data/spec/requests/flows/revoke_token_spec.rb +103 -103
- data/spec/requests/protected_resources_spec.rb +64 -64
- data/spec/spec_helper.rb +60 -60
- data/spec/support/api_helper.rb +11 -11
- metadata +50 -52
- data/.rspec +0 -2
- data/.rubocop.yml +0 -18
- data/.travis.yml +0 -42
- data/README.md +0 -820
- data/gemfiles/active_record.rb +0 -25
- data/gemfiles/mongoid.rb +0 -14
- data/gemfiles/sequel.rb +0 -24
- data/grape_oauth2.png +0 -0
data/spec/support/api_helper.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
module ApiHelper
|
2
|
-
include Rack::Test::Methods
|
3
|
-
|
4
|
-
def app
|
5
|
-
TWITTER_APP
|
6
|
-
end
|
7
|
-
|
8
|
-
def json_body
|
9
|
-
JSON.parse(last_response.body, symbolize_names: true) rescue fail StandardError, 'API request returned invalid json'
|
10
|
-
end
|
11
|
-
end
|
1
|
+
module ApiHelper
|
2
|
+
include Rack::Test::Methods
|
3
|
+
|
4
|
+
def app
|
5
|
+
TWITTER_APP
|
6
|
+
end
|
7
|
+
|
8
|
+
def json_body
|
9
|
+
JSON.parse(last_response.body, symbolize_names: true) rescue fail StandardError, 'API request returned invalid json'
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape_oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nikita Bulai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grape
|
@@ -16,54 +16,60 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '1.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.0'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
29
|
+
version: '1.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rack-oauth2
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.
|
39
|
+
version: 1.6.0
|
34
40
|
- - ">="
|
35
41
|
- !ruby/object:Gem::Version
|
36
|
-
version: 1.
|
42
|
+
version: 1.6.0
|
37
43
|
type: :runtime
|
38
44
|
prerelease: false
|
39
45
|
version_requirements: !ruby/object:Gem::Requirement
|
40
46
|
requirements:
|
41
47
|
- - "~>"
|
42
48
|
- !ruby/object:Gem::Version
|
43
|
-
version: 1.
|
49
|
+
version: 1.6.0
|
44
50
|
- - ">="
|
45
51
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.
|
52
|
+
version: 1.6.0
|
47
53
|
- !ruby/object:Gem::Dependency
|
48
54
|
name: rspec-rails
|
49
55
|
requirement: !ruby/object:Gem::Requirement
|
50
56
|
requirements:
|
51
57
|
- - "~>"
|
52
58
|
- !ruby/object:Gem::Version
|
53
|
-
version: 3.
|
59
|
+
version: 3.6.0
|
54
60
|
- - ">="
|
55
61
|
- !ruby/object:Gem::Version
|
56
|
-
version: 3.
|
62
|
+
version: 3.6.0
|
57
63
|
type: :development
|
58
64
|
prerelease: false
|
59
65
|
version_requirements: !ruby/object:Gem::Requirement
|
60
66
|
requirements:
|
61
67
|
- - "~>"
|
62
68
|
- !ruby/object:Gem::Version
|
63
|
-
version: 3.
|
69
|
+
version: 3.6.0
|
64
70
|
- - ">="
|
65
71
|
- !ruby/object:Gem::Version
|
66
|
-
version: 3.
|
72
|
+
version: 3.6.0
|
67
73
|
- !ruby/object:Gem::Dependency
|
68
74
|
name: database_cleaner
|
69
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,7 +90,7 @@ dependencies:
|
|
84
90
|
- - ">="
|
85
91
|
- !ruby/object:Gem::Version
|
86
92
|
version: 1.5.0
|
87
|
-
description:
|
93
|
+
description: Flexible, ORM-agnostic, fully customizable and simple OAuth2 provider
|
88
94
|
support for Grape APIs
|
89
95
|
email:
|
90
96
|
- bulajnikita@gmail.com
|
@@ -93,17 +99,9 @@ extensions: []
|
|
93
99
|
extra_rdoc_files: []
|
94
100
|
files:
|
95
101
|
- ".gitignore"
|
96
|
-
- ".rspec"
|
97
|
-
- ".rubocop.yml"
|
98
|
-
- ".travis.yml"
|
99
102
|
- Gemfile
|
100
|
-
- README.md
|
101
103
|
- Rakefile
|
102
|
-
- gemfiles/active_record.rb
|
103
|
-
- gemfiles/mongoid.rb
|
104
|
-
- gemfiles/sequel.rb
|
105
104
|
- grape_oauth2.gemspec
|
106
|
-
- grape_oauth2.png
|
107
105
|
- lib/grape_oauth2.rb
|
108
106
|
- lib/grape_oauth2/configuration.rb
|
109
107
|
- lib/grape_oauth2/configuration/class_accessors.rb
|
@@ -203,55 +201,55 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
201
|
version: '0'
|
204
202
|
requirements: []
|
205
203
|
rubyforge_project:
|
206
|
-
rubygems_version: 2.
|
204
|
+
rubygems_version: 2.5.1
|
207
205
|
signing_key:
|
208
206
|
specification_version: 4
|
209
207
|
summary: Grape OAuth2 provider
|
210
208
|
test_files:
|
211
|
-
- spec/support/api_helper.rb
|
212
|
-
- spec/configuration/version_spec.rb
|
213
209
|
- spec/configuration/config_spec.rb
|
210
|
+
- spec/configuration/version_spec.rb
|
211
|
+
- spec/dummy/endpoints/custom_authorization.rb
|
212
|
+
- spec/dummy/endpoints/custom_token.rb
|
213
|
+
- spec/dummy/endpoints/status.rb
|
214
214
|
- spec/dummy/grape_oauth2_config.rb
|
215
|
-
- spec/dummy/orm/
|
216
|
-
- spec/dummy/orm/sequel/app/models/user.rb
|
217
|
-
- spec/dummy/orm/sequel/app/models/application.rb
|
218
|
-
- spec/dummy/orm/sequel/app/models/access_code.rb
|
219
|
-
- spec/dummy/orm/sequel/app/models/access_token.rb
|
220
|
-
- spec/dummy/orm/sequel/app/models/application_record.rb
|
221
|
-
- spec/dummy/orm/sequel/app/config/db.rb
|
222
|
-
- spec/dummy/orm/sequel/config.ru
|
223
|
-
- spec/dummy/orm/sequel/db/schema.rb
|
224
|
-
- spec/dummy/orm/active_record/app/twitter.rb
|
225
|
-
- spec/dummy/orm/active_record/app/models/user.rb
|
226
|
-
- spec/dummy/orm/active_record/app/models/application.rb
|
215
|
+
- spec/dummy/orm/active_record/app/config/db.rb
|
227
216
|
- spec/dummy/orm/active_record/app/models/access_code.rb
|
228
217
|
- spec/dummy/orm/active_record/app/models/access_token.rb
|
218
|
+
- spec/dummy/orm/active_record/app/models/application.rb
|
229
219
|
- spec/dummy/orm/active_record/app/models/application_record.rb
|
230
|
-
- spec/dummy/orm/active_record/app/
|
220
|
+
- spec/dummy/orm/active_record/app/models/user.rb
|
221
|
+
- spec/dummy/orm/active_record/app/twitter.rb
|
231
222
|
- spec/dummy/orm/active_record/config.ru
|
232
223
|
- spec/dummy/orm/active_record/db/schema.rb
|
233
|
-
- spec/dummy/orm/mongoid/app/
|
234
|
-
- spec/dummy/orm/mongoid/app/
|
235
|
-
- spec/dummy/orm/mongoid/app/models/application.rb
|
224
|
+
- spec/dummy/orm/mongoid/app/config/db.rb
|
225
|
+
- spec/dummy/orm/mongoid/app/config/mongoid.yml
|
236
226
|
- spec/dummy/orm/mongoid/app/models/access_code.rb
|
237
227
|
- spec/dummy/orm/mongoid/app/models/access_token.rb
|
238
|
-
- spec/dummy/orm/mongoid/app/
|
239
|
-
- spec/dummy/orm/mongoid/app/
|
228
|
+
- spec/dummy/orm/mongoid/app/models/application.rb
|
229
|
+
- spec/dummy/orm/mongoid/app/models/user.rb
|
230
|
+
- spec/dummy/orm/mongoid/app/twitter.rb
|
240
231
|
- spec/dummy/orm/mongoid/config.ru
|
241
|
-
- spec/dummy/
|
242
|
-
- spec/dummy/
|
243
|
-
- spec/dummy/
|
244
|
-
- spec/
|
245
|
-
- spec/
|
246
|
-
- spec/
|
232
|
+
- spec/dummy/orm/sequel/app/config/db.rb
|
233
|
+
- spec/dummy/orm/sequel/app/models/access_code.rb
|
234
|
+
- spec/dummy/orm/sequel/app/models/access_token.rb
|
235
|
+
- spec/dummy/orm/sequel/app/models/application.rb
|
236
|
+
- spec/dummy/orm/sequel/app/models/application_record.rb
|
237
|
+
- spec/dummy/orm/sequel/app/models/user.rb
|
238
|
+
- spec/dummy/orm/sequel/app/twitter.rb
|
239
|
+
- spec/dummy/orm/sequel/config.ru
|
240
|
+
- spec/dummy/orm/sequel/db/schema.rb
|
241
|
+
- spec/lib/scopes_spec.rb
|
247
242
|
- spec/mixins/active_record/access_token_spec.rb
|
248
|
-
- spec/mixins/
|
243
|
+
- spec/mixins/active_record/client_spec.rb
|
249
244
|
- spec/mixins/mongoid/access_token_spec.rb
|
250
|
-
- spec/
|
245
|
+
- spec/mixins/mongoid/client_spec.rb
|
246
|
+
- spec/mixins/sequel/access_token_spec.rb
|
247
|
+
- spec/mixins/sequel/client_spec.rb
|
251
248
|
- spec/requests/flows/authorization_code_spec.rb
|
249
|
+
- spec/requests/flows/client_credentials_spec.rb
|
252
250
|
- spec/requests/flows/password_spec.rb
|
253
|
-
- spec/requests/flows/revoke_token_spec.rb
|
254
251
|
- spec/requests/flows/refresh_token_spec.rb
|
252
|
+
- spec/requests/flows/revoke_token_spec.rb
|
255
253
|
- spec/requests/protected_resources_spec.rb
|
256
|
-
- spec/lib/scopes_spec.rb
|
257
254
|
- spec/spec_helper.rb
|
255
|
+
- spec/support/api_helper.rb
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
LineLength:
|
2
|
-
Max: 120
|
3
|
-
AllCops:
|
4
|
-
Exclude:
|
5
|
-
- 'spec/**/*'
|
6
|
-
DisplayCopNames: true
|
7
|
-
Rails:
|
8
|
-
Enabled: true
|
9
|
-
Documentation:
|
10
|
-
Enabled: false
|
11
|
-
Style/EndOfLine:
|
12
|
-
Enabled: false
|
13
|
-
Rails/TimeZone:
|
14
|
-
Enabled: false
|
15
|
-
Metrics/BlockLength:
|
16
|
-
Exclude:
|
17
|
-
- lib/grape_oauth2/mixins/**/*
|
18
|
-
- lib/grape_oauth2/endpoints/*
|
data/.travis.yml
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
cache: bundler
|
4
|
-
bundler_args: --without yard guard benchmarks
|
5
|
-
|
6
|
-
services:
|
7
|
-
- mongodb
|
8
|
-
|
9
|
-
before_install:
|
10
|
-
- gem install bundler -v '~> 1.10'
|
11
|
-
|
12
|
-
matrix:
|
13
|
-
allow_failures:
|
14
|
-
- rvm: ruby-head
|
15
|
-
include:
|
16
|
-
- rvm: 2.2.4
|
17
|
-
gemfile: gemfiles/active_record.rb
|
18
|
-
env: ORM=active_record
|
19
|
-
- rvm: 2.2.4
|
20
|
-
gemfile: gemfiles/sequel.rb
|
21
|
-
env: ORM=sequel
|
22
|
-
- rvm: 2.2.4
|
23
|
-
gemfile: gemfiles/mongoid.rb
|
24
|
-
env: ORM=mongoid
|
25
|
-
- rvm: 2.3.1
|
26
|
-
gemfile: gemfiles/active_record.rb
|
27
|
-
env: ORM=active_record
|
28
|
-
- rvm: 2.3.1
|
29
|
-
gemfile: gemfiles/sequel.rb
|
30
|
-
env: ORM=sequel
|
31
|
-
- rvm: 2.3.1
|
32
|
-
gemfile: gemfiles/mongoid.rb
|
33
|
-
env: ORM=mongoid
|
34
|
-
- rvm: ruby-head
|
35
|
-
gemfile: gemfiles/active_record.rb
|
36
|
-
env: ORM=active_record
|
37
|
-
- rvm: ruby-head
|
38
|
-
gemfile: gemfiles/sequel.rb
|
39
|
-
env: ORM=sequel
|
40
|
-
- rvm: ruby-head
|
41
|
-
gemfile: gemfiles/mongoid.rb
|
42
|
-
env: ORM=mongoid
|
data/README.md
DELETED
@@ -1,820 +0,0 @@
|
|
1
|
-
<p align="center">
|
2
|
-
<img alt="Grape::OAuth2 - OAuth2 provider for Grape" src="https://raw.githubusercontent.com/nbulaj/grape_oauth2/master/grape_oauth2.png">
|
3
|
-
</p>
|
4
|
-
|
5
|
-
# Grape::OAuth2
|
6
|
-
[](https://travis-ci.org/nbulaj/grape_oauth2)
|
7
|
-
[](https://gemnasium.com/nbulaj/grape_oauth2)
|
8
|
-
[](https://coveralls.io/github/nbulaj/grape_oauth2)
|
9
|
-
[](https://codeclimate.com/github/nbulaj/grape_oauth2)
|
10
|
-
[](http://inch-ci.org/github/nbulaj/grape_oauth2)
|
11
|
-
[](#license)
|
12
|
-
|
13
|
-
This gem adds a flexible OAuth2 ([RFC 6749](http://www.rfc-editor.org/rfc/rfc6749.txt)) server authorization and
|
14
|
-
endpoints protection to your [Grape](https://github.com/ruby-grape/grape) API project with any ORM / ODM / PORO.
|
15
|
-
|
16
|
-
**Currently under development**.
|
17
|
-
|
18
|
-
Implemented features (flows):
|
19
|
-
|
20
|
-
- Resource Owner Password Credentials
|
21
|
-
- Client Credentials
|
22
|
-
- Refresh token
|
23
|
-
- Token revocation
|
24
|
-
- Access Token Scopes
|
25
|
-
|
26
|
-
Supported token types:
|
27
|
-
|
28
|
-
* Bearer
|
29
|
-
|
30
|
-
_In progress_:
|
31
|
-
|
32
|
-
- Authorization Code Flow (partially implemented)
|
33
|
-
- Access Grants
|
34
|
-
- Implicit Grant
|
35
|
-
|
36
|
-
## Documentation valid for `master` branch
|
37
|
-
|
38
|
-
Please check the documentation for the version of `Grape::OAuth2` you are using in:
|
39
|
-
https://github.com/nbulaj/grape_oauth2/releases
|
40
|
-
|
41
|
-
- See the [Wiki](https://github.com/nbulaj/grape_oauth2/wiki)
|
42
|
-
|
43
|
-
## Table of Contents
|
44
|
-
|
45
|
-
- [Installation](#installation)
|
46
|
-
- [Configuration](#configuration)
|
47
|
-
- [ActiveRecord](#activerecord)
|
48
|
-
- [Sequel](#sequel)
|
49
|
-
- [Mongoid](#mongoid)
|
50
|
-
- [Other ORMs](#other-orms)
|
51
|
-
- [Client](#client)
|
52
|
-
- [AccessToken](#accesstoken)
|
53
|
-
- [ResourceOwner](#resourceowner)
|
54
|
-
- [Usage examples](#usage-examples)
|
55
|
-
- [I'm lazy, give me all out of the box!](#im-lazy-give-me-all-out-of-the-box)
|
56
|
-
- [Hey, I wanna control all the authentication process!](#hey-i-wanna-control-all-the-authentication-process)
|
57
|
-
- [Override default mixins](#override-default-mixins)
|
58
|
-
- [Custom authentication endpoints](#custom-authentication-endpoints)
|
59
|
-
- [Custom Access Token authenticator](#custom-access-token-authenticator)
|
60
|
-
- [Custom scopes validation](#custom-scopes-validation)
|
61
|
-
- [Custom token generator](#custom-token-generator)
|
62
|
-
- [Process token on Refresh (protect against Replay Attacks)](#process-token-on-refresh-protect-against-replay-attacks)
|
63
|
-
- [Errors (exceptions) handling](#errors-exceptions-handling)
|
64
|
-
- [Example App](#example-app)
|
65
|
-
- [Contributing](#contributing)
|
66
|
-
- [License](#license)
|
67
|
-
|
68
|
-
## Installation
|
69
|
-
|
70
|
-
**Grape::OAuth2** gem requires only `Grape` and `Rack::OAuth2` gems as the dependency.
|
71
|
-
Yes, no Rails, ActiveRecord or any other libs or huge frameworks :+1:
|
72
|
-
|
73
|
-
If you are using bundler, first add `'grape_oauth2'` to your Gemfile:
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
gem 'grape_oauth2', git: 'https://github.com/nbulaj/grape_oauth2.git'
|
77
|
-
```
|
78
|
-
|
79
|
-
And run:
|
80
|
-
|
81
|
-
```sh
|
82
|
-
bundle install
|
83
|
-
```
|
84
|
-
|
85
|
-
If you running your Grape API with `rackup` and using the [gem from git source](http://bundler.io/git.html), then
|
86
|
-
you need to explicitly require bundler in the `config.ru`:
|
87
|
-
|
88
|
-
```ruby
|
89
|
-
require 'bundler/setup'
|
90
|
-
Bundler.setup
|
91
|
-
```
|
92
|
-
|
93
|
-
or run your app with bundle exec command:
|
94
|
-
|
95
|
-
```
|
96
|
-
> bundle exec rackup config.ru
|
97
|
-
[2016-11-19 02:35:33] INFO WEBrick 1.3.1
|
98
|
-
[2016-11-19 02:35:33] INFO ruby 2.3.1 (2016-04-26) [i386-mingw32]
|
99
|
-
[2016-11-19 02:35:33] INFO WEBrick::HTTPServer#start: pid=5472 port=9292
|
100
|
-
```
|
101
|
-
|
102
|
-
## Configuration
|
103
|
-
|
104
|
-
Main `Grape::OAuth2` configuration must be placed in `config/initializers/` (in case you are using [Rails](https://github.com/rails/rails))
|
105
|
-
or in some place, that will be processed at the application startup:
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
Grape::OAuth2.configure do |config|
|
109
|
-
# Access Tokens lifetime (expires in)
|
110
|
-
config.access_token_lifetime = 7200 # in seconds (2.hours for Rails), `nil` if never expires
|
111
|
-
|
112
|
-
# Authorization Code lifetime
|
113
|
-
# config.authorization_code_lifetime = 7200 # in seconds (2.hours for Rails)
|
114
|
-
|
115
|
-
# Allowed OAuth2 Authorization Grants (default is %w(password client_credentials)
|
116
|
-
config.allowed_grant_types = %w(password client_credentials refresh_token)
|
117
|
-
|
118
|
-
# Issue access tokens with refresh token (default is false)
|
119
|
-
config.issue_refresh_token = true
|
120
|
-
|
121
|
-
# Process Access Token that was used for the Refresh Token Flow (default is :nothing).
|
122
|
-
# Could be a symbol (Access Token instance must respond to it)
|
123
|
-
# or block with refresh token as an argument.
|
124
|
-
# config.on_refresh = :nothing
|
125
|
-
|
126
|
-
# WWW-Authenticate Realm (default is "OAuth 2.0")
|
127
|
-
# config.realm = 'My API'
|
128
|
-
|
129
|
-
# Access Token authenticator block.
|
130
|
-
# config.token_authenticator do |request|
|
131
|
-
# AccessToken.authenticate(request.access_token) || request.invalid_token!
|
132
|
-
# end
|
133
|
-
|
134
|
-
# Scopes validator class (default is Grape::OAuth2::Scopes).
|
135
|
-
# config.scopes_validator_class_name = 'MyCustomValidator'
|
136
|
-
|
137
|
-
# Token generator class (default is Grape::OAuth2::UniqueToken).
|
138
|
-
# Must respond to `self.generate(payload = {}, options = {})`.
|
139
|
-
# config.token_generator_class_name = 'JWTGenerator'
|
140
|
-
|
141
|
-
# Classes for OAuth2 Roles
|
142
|
-
config.client_class_name = 'Application'
|
143
|
-
config.access_token_class_name = 'AccessToken'
|
144
|
-
config.resource_owner_class_name = 'User'
|
145
|
-
end
|
146
|
-
```
|
147
|
-
|
148
|
-
Currently implemented (partly on completely) grant types: _password, client_credentials, refresh_token_.
|
149
|
-
|
150
|
-
As you know, OAuth2 workflow implies the existence of the next three roles: **Access Token**, **Client** and **Resource Owner**.
|
151
|
-
So your project must include 3 classes (models) - _AccessToken_, _Application_ and _User_ for example. The gem needs to know
|
152
|
-
what classes it work, so you need to create them and configure `Grape::OAuth2`.
|
153
|
-
|
154
|
-
`resource_owner_class` must have a `self.oauth_authenticate(client, username, password)` method, that returns an instance of the
|
155
|
-
class if authentication successful (`username` and `password` matches for example) and `false` or `nil` in other cases.
|
156
|
-
|
157
|
-
```ruby
|
158
|
-
# app/models/user.rb
|
159
|
-
class User < ApplicationRecord
|
160
|
-
has_secure_password
|
161
|
-
|
162
|
-
def self.oauth_authenticate(_client, username, password)
|
163
|
-
# find the user by it username
|
164
|
-
user = find_by(username: username)
|
165
|
-
return if user.nil?
|
166
|
-
|
167
|
-
# check the password
|
168
|
-
user.authenticate(password)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
```
|
172
|
-
|
173
|
-
`client_class`, `access_token_class` and `resource_owner_class` objects must contain a specific set of API (methods), that are
|
174
|
-
called by the gem. `Grape::OAuth2` includes predefined mixins for the projects that use the `ActiveRecord` or `Sequel` ORMs,
|
175
|
-
and you can just include them into your models.
|
176
|
-
|
177
|
-
### ActiveRecord
|
178
|
-
|
179
|
-
```ruby
|
180
|
-
# app/models/access_token.rb
|
181
|
-
class AccessToken < ApplicationRecord
|
182
|
-
include Grape::OAuth2::ActiveRecord::AccessToken
|
183
|
-
end
|
184
|
-
|
185
|
-
# app/models/application.rb
|
186
|
-
class Application < ApplicationRecord
|
187
|
-
include Grape::OAuth2::ActiveRecord::Client
|
188
|
-
end
|
189
|
-
```
|
190
|
-
|
191
|
-
Migration for the simplest use case of the gem looks as follows:
|
192
|
-
|
193
|
-
```ruby
|
194
|
-
ActiveRecord::Schema.define(version: 3) do
|
195
|
-
# All the columns are custom
|
196
|
-
create_table :users do |t|
|
197
|
-
t.string :name
|
198
|
-
t.string :username
|
199
|
-
t.string :password_digest
|
200
|
-
end
|
201
|
-
|
202
|
-
# Required columns: :key & :secret
|
203
|
-
create_table :applications do |t|
|
204
|
-
t.string :name
|
205
|
-
t.string :key
|
206
|
-
t.string :secret
|
207
|
-
|
208
|
-
t.timestamps null: false
|
209
|
-
end
|
210
|
-
|
211
|
-
add_index :applications, :key, unique: true
|
212
|
-
|
213
|
-
# Required columns: :client_id, :resource_owner_id, :token, :expires_at, :revoked_at, :refresh_token
|
214
|
-
create_table :access_tokens do |t|
|
215
|
-
t.integer :resource_owner_id
|
216
|
-
t.integer :client_id
|
217
|
-
|
218
|
-
t.string :token, null: false
|
219
|
-
t.string :refresh_token
|
220
|
-
t.string :scopes, default: ''
|
221
|
-
|
222
|
-
t.datetime :expires_at
|
223
|
-
t.datetime :revoked_at
|
224
|
-
t.datetime :created_at, null: false
|
225
|
-
end
|
226
|
-
|
227
|
-
add_index :access_tokens, :token, unique: true
|
228
|
-
add_index :access_tokens, :resource_owner_id
|
229
|
-
add_index :access_tokens, :client_id
|
230
|
-
add_index :access_tokens, :refresh_token, unique: true
|
231
|
-
end
|
232
|
-
```
|
233
|
-
|
234
|
-
### Sequel
|
235
|
-
|
236
|
-
```ruby
|
237
|
-
# app/models/access_token.rb
|
238
|
-
class AccessToken < Sequel::Model
|
239
|
-
include Grape::OAuth2::Sequel::AccessToken
|
240
|
-
end
|
241
|
-
|
242
|
-
# app/models/application.rb
|
243
|
-
class Application < Sequel::Model
|
244
|
-
include Grape::OAuth2::Sequel::Client
|
245
|
-
end
|
246
|
-
```
|
247
|
-
|
248
|
-
Migration for the simplest use case of the gem looks as follows:
|
249
|
-
|
250
|
-
```ruby
|
251
|
-
DB.create_table :applications do
|
252
|
-
primary_key :id
|
253
|
-
|
254
|
-
column :name, String, size: 255, null: false
|
255
|
-
column :key, String, size: 255, null: false, index: { unique: true }
|
256
|
-
column :secret, String, size: 255, null: false
|
257
|
-
|
258
|
-
|
259
|
-
column :redirect_uri, String
|
260
|
-
|
261
|
-
column :created_at, DateTime
|
262
|
-
column :updated_at, DateTime
|
263
|
-
end
|
264
|
-
|
265
|
-
DB.create_table :access_tokens do
|
266
|
-
primary_key :id
|
267
|
-
column :client_id, Integer
|
268
|
-
column :resource_owner_id, Integer, index: true
|
269
|
-
|
270
|
-
column :token, String, size: 255, null: false, index: { unique: true }
|
271
|
-
|
272
|
-
column :refresh_token, String, size: 255, index: { unique: true }
|
273
|
-
|
274
|
-
column :expires_at, DateTime
|
275
|
-
column :revoked_at, DateTime
|
276
|
-
column :created_at, DateTime, null: false
|
277
|
-
column :scopes, String, size: 255
|
278
|
-
end
|
279
|
-
|
280
|
-
DB.create_table :users do
|
281
|
-
primary_key :id
|
282
|
-
column :name, String, size: 255
|
283
|
-
column :username, String, size: 255
|
284
|
-
column :created_at, DateTime
|
285
|
-
column :updated_at, DateTime
|
286
|
-
column :password_digest, String, size: 255
|
287
|
-
end
|
288
|
-
```
|
289
|
-
|
290
|
-
### Mongoid
|
291
|
-
|
292
|
-
```ruby
|
293
|
-
# app/models/access_token.rb
|
294
|
-
class AccessToken
|
295
|
-
include Grape::OAuth2::Mongoid::AccessToken
|
296
|
-
end
|
297
|
-
|
298
|
-
# app/models/application.rb
|
299
|
-
class Application
|
300
|
-
include Grape::OAuth2::Mongoid::Client
|
301
|
-
end
|
302
|
-
|
303
|
-
# app/models/user.rb
|
304
|
-
class User
|
305
|
-
include Mongoid::Document
|
306
|
-
include Mongoid::Timestamps
|
307
|
-
|
308
|
-
field :username, type: String
|
309
|
-
field :password, type: String
|
310
|
-
|
311
|
-
def self.oauth_authenticate(_client, username, password)
|
312
|
-
find_by(username: username, password: password)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
```
|
316
|
-
|
317
|
-
### Other ORMs
|
318
|
-
|
319
|
-
If you want to use `Grape::OAuth2` gem, but your project doesn't use `ActiveRecord`, `Sequel` or `Mongoid`, then you can
|
320
|
-
create at least 3 classes (models) to cover OAuth2 roles and define a specific set ot API for them as described below.
|
321
|
-
|
322
|
-
#### Client
|
323
|
-
|
324
|
-
Class that represents an OAuth2 Client should contain the following API:
|
325
|
-
|
326
|
-
```ruby
|
327
|
-
class Client
|
328
|
-
# ...
|
329
|
-
|
330
|
-
def self.authenticate(key, secret = nil)
|
331
|
-
# Should return a Client instance matching the
|
332
|
-
# key & secret provided (`secret` is optional).
|
333
|
-
end
|
334
|
-
end
|
335
|
-
```
|
336
|
-
|
337
|
-
#### AccessToken
|
338
|
-
|
339
|
-
For the class that represents an OAuth2 Access Token you must define the next API:
|
340
|
-
|
341
|
-
```ruby
|
342
|
-
class AccessToken
|
343
|
-
# ...
|
344
|
-
|
345
|
-
def self.create_for(client, resource_owner, scopes = nil)
|
346
|
-
# Creates the record in the database for the provided client and
|
347
|
-
# resource owner with specific scopes (if present).
|
348
|
-
# Returns an instance of that record.
|
349
|
-
end
|
350
|
-
|
351
|
-
def self.authenticate(token, type: :access_token)
|
352
|
-
# Returns an Access Token instance matching the token provided.
|
353
|
-
# Access Token can be searched by token or refresh token value. In the
|
354
|
-
# first case :type option must be set to :access_token (default), in
|
355
|
-
# the second case - to the :refresh_token.
|
356
|
-
# Note that you MAY include expired access tokens in the result
|
357
|
-
# of this method so long as you implement an instance `#expired?`
|
358
|
-
# method.
|
359
|
-
end
|
360
|
-
|
361
|
-
def client
|
362
|
-
# Returns associated Client instance. Always must be present!
|
363
|
-
# For ORM objects it can be an association (`belongs_to :client` for ActiveRecord).
|
364
|
-
end
|
365
|
-
|
366
|
-
def resource_owner
|
367
|
-
# Returns associated Resource Owner instance.
|
368
|
-
# Can return `nil` (for Client Credentials flow as an example).
|
369
|
-
# For ORM objects it can be an association (`belongs_to :resource_owner` for ActiveRecord).
|
370
|
-
end
|
371
|
-
|
372
|
-
def scopes
|
373
|
-
# Returns Access Token authorised set of scopes. Can be a space-separated String,
|
374
|
-
# Array or any object, that responds to `to_a`.
|
375
|
-
end
|
376
|
-
|
377
|
-
def expired?
|
378
|
-
# true if the Access Token has reached its expiration.
|
379
|
-
end
|
380
|
-
|
381
|
-
def revoked?
|
382
|
-
# true if the Access Token was revoked.
|
383
|
-
end
|
384
|
-
|
385
|
-
def revoke!(revoked_at = Time.now)
|
386
|
-
# Revokes an Access Token (by setting its :revoked_at attribute to the
|
387
|
-
# specific time for example).
|
388
|
-
end
|
389
|
-
|
390
|
-
def to_bearer_token
|
391
|
-
# Returns a Hash of Bearer token attributes like the following:
|
392
|
-
# access_token: '', # - required
|
393
|
-
# refresh_token: '', # - optional
|
394
|
-
# token_type: 'bearer', # - required
|
395
|
-
# expires_in: '', # - required
|
396
|
-
# scope: '' # - optional
|
397
|
-
end
|
398
|
-
end
|
399
|
-
```
|
400
|
-
|
401
|
-
You can take a look at the [Grape::OAuth2 mixins](https://github.com/nbulaj/grape_oauth2/tree/master/lib/grape_oauth2/mixins)
|
402
|
-
to understand what they are doing and what they are returning.
|
403
|
-
|
404
|
-
#### ResourceOwner
|
405
|
-
|
406
|
-
As was said before, Resource Owner class (`User` model for example) must contain only one class method
|
407
|
-
(**only for** Password Authorization Grant): `self.oauth_authenticate(client, username, password)`.
|
408
|
-
|
409
|
-
```ruby
|
410
|
-
class User
|
411
|
-
# ...
|
412
|
-
|
413
|
-
def self.oauth_authenticate(client, username, password)
|
414
|
-
# Returns an instance of the User class with matching username
|
415
|
-
# and password. If there is no such User or password doesn't match
|
416
|
-
# then returns nil.
|
417
|
-
end
|
418
|
-
end
|
419
|
-
```
|
420
|
-
|
421
|
-
## Usage examples
|
422
|
-
### I'm lazy, give me all out of the box!
|
423
|
-
|
424
|
-
If you need a common OAuth2 authentication then you can use default gem endpoints for it. First of all you
|
425
|
-
will need to configure Grape::OAuth2 as described above (create models, migrations, configure the gem).
|
426
|
-
For `ActiveRecord` it would be as follows:
|
427
|
-
|
428
|
-
```ruby
|
429
|
-
# app/models/access_token.rb
|
430
|
-
class AccessToken < ApplicationRecord
|
431
|
-
include Grape::OAuth2::ActiveRecord::AccessToken
|
432
|
-
end
|
433
|
-
|
434
|
-
# app/models/application.rb
|
435
|
-
class Application < ApplicationRecord
|
436
|
-
include Grape::OAuth2::ActiveRecord::Client
|
437
|
-
end
|
438
|
-
|
439
|
-
# app/models/user.rb
|
440
|
-
class User < ApplicationRecord
|
441
|
-
has_secure_password
|
442
|
-
|
443
|
-
# Don't forget to setup this method for your Resource Owner model!
|
444
|
-
def self.oauth_authenticate(_client, username, password)
|
445
|
-
user = find_by(username: username)
|
446
|
-
return if user.nil?
|
447
|
-
|
448
|
-
user.authenticate(password)
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
|
-
# config/oauth2.rb
|
453
|
-
Grape::OAuth2.configure do |config|
|
454
|
-
# Classes for OAuth2 Roles
|
455
|
-
config.client_class_name = 'Application'
|
456
|
-
config.access_token_class_name = 'AccessToken'
|
457
|
-
config.resource_owner_class_name = 'User'
|
458
|
-
end
|
459
|
-
```
|
460
|
-
|
461
|
-
And just inject `Grape::OAuth2` into your main API class:
|
462
|
-
|
463
|
-
```ruby
|
464
|
-
# app/twitter.rb
|
465
|
-
module Twitter
|
466
|
-
class API < Grape::API
|
467
|
-
version 'v1', using: :path
|
468
|
-
format :json
|
469
|
-
prefix :api
|
470
|
-
|
471
|
-
# Mount all endpoints by default.
|
472
|
-
# You can define a custom one you want to use by providing them
|
473
|
-
# as an argument:
|
474
|
-
# include Grape::OAuth2.api :token, :authorize
|
475
|
-
#
|
476
|
-
include Grape::OAuth2.api
|
477
|
-
|
478
|
-
# mount any other endpoints
|
479
|
-
# ...
|
480
|
-
end
|
481
|
-
end
|
482
|
-
```
|
483
|
-
|
484
|
-
The `include Grape::OAuth2.api` could be replaced with the next (as it does the same):
|
485
|
-
|
486
|
-
|
487
|
-
```ruby
|
488
|
-
# app/twitter.rb
|
489
|
-
module Twitter
|
490
|
-
class API < Grape::API
|
491
|
-
version 'v1', using: :path
|
492
|
-
format :json
|
493
|
-
prefix :api
|
494
|
-
|
495
|
-
# Add OAuth2 helpers
|
496
|
-
helpers Grape::OAuth2::Helpers::AccessTokenHelpers
|
497
|
-
|
498
|
-
# Inject token authentication middleware
|
499
|
-
use *Grape::OAuth2.middleware
|
500
|
-
|
501
|
-
# Mount default Grape::OAuth2 Token endpoint
|
502
|
-
mount Grape::OAuth2::Endpoints::Token
|
503
|
-
# Mount default Grape::OAuth2 Authorization endpoint
|
504
|
-
mount Grape::OAuth2::Endpoints::Authorize
|
505
|
-
|
506
|
-
# mount any other endpoints
|
507
|
-
# ...
|
508
|
-
end
|
509
|
-
end
|
510
|
-
```
|
511
|
-
|
512
|
-
And that is all! Use the next available routes to get the Access Token:
|
513
|
-
|
514
|
-
```
|
515
|
-
POST /oauth/token
|
516
|
-
POST /oauth/revoke
|
517
|
-
```
|
518
|
-
|
519
|
-
Now you can protect your endpoints with `access_token_required!` method:
|
520
|
-
|
521
|
-
```ruby
|
522
|
-
module Twitter
|
523
|
-
module Endpoints
|
524
|
-
class Status < Grape::API
|
525
|
-
resources :status do
|
526
|
-
get do
|
527
|
-
# public resource, no scopes required
|
528
|
-
access_token_required!
|
529
|
-
|
530
|
-
present(:status, current_user.status)
|
531
|
-
end
|
532
|
-
|
533
|
-
post do
|
534
|
-
# requires 'write' scope to exist in Access Token
|
535
|
-
access_token_required! :write
|
536
|
-
|
537
|
-
status = current_user.statuses.create!(body: 'Hi man!')
|
538
|
-
present(:status, status, with: V1::Entities::Status)
|
539
|
-
end
|
540
|
-
end
|
541
|
-
end
|
542
|
-
end
|
543
|
-
end
|
544
|
-
```
|
545
|
-
|
546
|
-
If you need to protect all the routes in the endpoint, but it's requires different scopes, than you can
|
547
|
-
add `access_token_required!` helper to the `before` filter and setup required scopes directly for the endpoints:
|
548
|
-
|
549
|
-
```ruby
|
550
|
-
module Twitter
|
551
|
-
module Endpoints
|
552
|
-
class Status < Grape::API
|
553
|
-
before do
|
554
|
-
access_token_required!
|
555
|
-
end
|
556
|
-
|
557
|
-
resources :status do
|
558
|
-
# public endpoint - no scopes required
|
559
|
-
get do
|
560
|
-
present(:status, current_user.status)
|
561
|
-
end
|
562
|
-
|
563
|
-
# private endpoint - requires :write scope
|
564
|
-
put ':id', scopes: [:write] do
|
565
|
-
status = current_user.statuses.create!(body: 'Hi man!')
|
566
|
-
present(:status, status, with: V1::Entities::Status)
|
567
|
-
end
|
568
|
-
end
|
569
|
-
end
|
570
|
-
end
|
571
|
-
end
|
572
|
-
```
|
573
|
-
|
574
|
-
### Hey, I wanna control all the authentication process!
|
575
|
-
#### Override default mixins
|
576
|
-
|
577
|
-
If you need to do some special things (check if `key` starts with _'MyAPI'_ word for example) and don't want to
|
578
|
-
write your own authentication endpoints, then you can just override default authentication methods in models
|
579
|
-
(only if you are using gem mixins, in other cases you **MUST** write them by yourself):
|
580
|
-
|
581
|
-
```ruby
|
582
|
-
# app/models/application.rb
|
583
|
-
class Application < ApplicationRecord
|
584
|
-
include Grape::OAuth2::ActiveRecord::Client
|
585
|
-
|
586
|
-
class << self
|
587
|
-
def self.authenticate(key, secret = nil)
|
588
|
-
# My custom condition for successful authentication
|
589
|
-
return nil unless key.start_with?('MyAPI')
|
590
|
-
|
591
|
-
if secret.present?
|
592
|
-
find_by(key: key, secret: secret)
|
593
|
-
else
|
594
|
-
find_by(key: key)
|
595
|
-
end
|
596
|
-
end
|
597
|
-
end
|
598
|
-
end
|
599
|
-
```
|
600
|
-
|
601
|
-
#### Custom authentication endpoints
|
602
|
-
|
603
|
-
Besides, you can create your own API endpoints for OAuth2 authentication and use `grape_oauth2` gem functionality.
|
604
|
-
In that case you will get a full control over the authentication proccess and can do anything in it. Just create
|
605
|
-
a common Grape API class, set optional OAuth2 params and process the request with the `Grape::OAuth2::Generators::Token`
|
606
|
-
generator for example (for issuing an access token):
|
607
|
-
|
608
|
-
```ruby
|
609
|
-
# api/oauth2.rb
|
610
|
-
module MyAPI
|
611
|
-
class OAuth2 < Grape::API
|
612
|
-
helpers Grape::OAuth2::Helpers::OAuthParams
|
613
|
-
|
614
|
-
namespace :oauth do
|
615
|
-
params do
|
616
|
-
use :oauth_token_params
|
617
|
-
end
|
618
|
-
|
619
|
-
post :token do
|
620
|
-
token_response = Grape::OAuth2::Generators::Token.generate_for(env) do |request, response|
|
621
|
-
# You can use default authentication if you don't need to change this part:
|
622
|
-
# client = Grape::OAuth2::Strategies::Base.authenticate_client(request)
|
623
|
-
|
624
|
-
# Or write your custom client authentication:
|
625
|
-
client = Application.find_by(key: request.client_id, active: true)
|
626
|
-
request.invalid_client! unless client
|
627
|
-
|
628
|
-
# You can use default Resource Owner authentication if you don't need to change this part:
|
629
|
-
# resource_owner = Grape::OAuth2::Strategies::Base.authenticate_resource_owner(client, request)
|
630
|
-
|
631
|
-
# Or define your custom resource owner authentication:
|
632
|
-
resource_owner = User.find_by(username: request.username)
|
633
|
-
request.invalid_grant! if resource_owner.nil? || resource_owner.inactive?
|
634
|
-
|
635
|
-
# You can create an Access Token as you want:
|
636
|
-
token = MyAwesomeAccessToken.create(client: client,
|
637
|
-
resource_owner: resource_owner,
|
638
|
-
scope: request.scope)
|
639
|
-
|
640
|
-
response.access_token = Grape::OAuth2::Strategies::Base.expose_to_bearer_token(token)
|
641
|
-
end
|
642
|
-
|
643
|
-
# If request is successful, then return it
|
644
|
-
status token_response.status
|
645
|
-
|
646
|
-
token_response.headers.each do |key, value|
|
647
|
-
header key, value
|
648
|
-
end
|
649
|
-
|
650
|
-
body token_response.access_token
|
651
|
-
end
|
652
|
-
|
653
|
-
desc 'OAuth 2.0 Token Revocation'
|
654
|
-
|
655
|
-
params do
|
656
|
-
use :oauth_token_revocation_params
|
657
|
-
end
|
658
|
-
|
659
|
-
post :revoke do
|
660
|
-
# ...
|
661
|
-
end
|
662
|
-
end
|
663
|
-
end
|
664
|
-
end
|
665
|
-
```
|
666
|
-
|
667
|
-
## Custom Access Token authenticator
|
668
|
-
|
669
|
-
If you don't want to use default `Grape::OAuth2` Access Token authenticator then you can define your own in the
|
670
|
-
configuration (it must be a `proc` or `lambda`):
|
671
|
-
|
672
|
-
```ruby
|
673
|
-
Grape::OAuth2.configure do |config|
|
674
|
-
config.token_authenticator do |request|
|
675
|
-
AccessToken.find_by(token: request.access_token) || request.invalid_token!
|
676
|
-
end
|
677
|
-
|
678
|
-
# or config.token_authenticator = lambda { |request| ... }
|
679
|
-
end
|
680
|
-
```
|
681
|
-
|
682
|
-
Don't forget to add the middleware to your root API class (`use *Grape::OAuth2.middleware`, see below).
|
683
|
-
|
684
|
-
## Custom scopes validation
|
685
|
-
|
686
|
-
If you want to control the process of scopes validation (for protected endpoints for example) then you must implement
|
687
|
-
your own class that will implement the following API:
|
688
|
-
|
689
|
-
```ruby
|
690
|
-
class CustomScopesValidator
|
691
|
-
# `scopes' is the set of required scopes that must be
|
692
|
-
# present in the Access Token instance.
|
693
|
-
def initialize(scopes)
|
694
|
-
@scopes = scopes || []
|
695
|
-
# ...some custom processing of scopes if required ...
|
696
|
-
end
|
697
|
-
|
698
|
-
def valid_for?(access_token)
|
699
|
-
# custom scopes validation implementation...
|
700
|
-
end
|
701
|
-
end
|
702
|
-
```
|
703
|
-
|
704
|
-
And set that class as scopes validator in the Grape::OAuth2 config:
|
705
|
-
|
706
|
-
```ruby
|
707
|
-
Grape::OAuth2.configure do |config|
|
708
|
-
# ...
|
709
|
-
|
710
|
-
config.scopes_validator_class_name = 'CustomScopesValidator'
|
711
|
-
end
|
712
|
-
```
|
713
|
-
|
714
|
-
## Custom token generator
|
715
|
-
|
716
|
-
If you want to generate your own tokens for Access Tokens and Authorization Codes then you need to write your own generator:
|
717
|
-
|
718
|
-
```ruby
|
719
|
-
class SomeTokenGenerator
|
720
|
-
# @param payload [Hash]
|
721
|
-
# Access Token payload (attributes before creation for example)
|
722
|
-
#
|
723
|
-
# @param options [Hash]
|
724
|
-
# Options for Generator
|
725
|
-
#
|
726
|
-
def self.generate(payload = {}, options = {})
|
727
|
-
# Returns a generated token string.
|
728
|
-
end
|
729
|
-
end
|
730
|
-
```
|
731
|
-
|
732
|
-
And set it as a token generator class in the Grape::OAuth2 config:
|
733
|
-
|
734
|
-
```ruby
|
735
|
-
Grape::OAuth2.configure do |config|
|
736
|
-
# ...
|
737
|
-
|
738
|
-
config.token_generator_class_name = 'SomeTokenGenerator'
|
739
|
-
end
|
740
|
-
```
|
741
|
-
|
742
|
-
## Process token on Refresh (protect against Replay Attacks)
|
743
|
-
|
744
|
-
If you want to do something with the original Access Token that was used with the Refresh Token Flow, then you need to
|
745
|
-
setup `on_refresh` configuration option. By default `Grape::OAuth2` gem does nothing on token refresh and that
|
746
|
-
option is set to `:nothing`. You can set it to the symbol (in that case `Access Token` instance must respond to it)
|
747
|
-
or block. Look at the examples:
|
748
|
-
|
749
|
-
```ruby
|
750
|
-
Grape::OAuth2.configure do |config|
|
751
|
-
# ...
|
752
|
-
|
753
|
-
config.on_refresh = :destroy # will call :destroy method (`refresh_token.destroy`)
|
754
|
-
end
|
755
|
-
```
|
756
|
-
|
757
|
-
```ruby
|
758
|
-
Grape::OAuth2.configure do |config|
|
759
|
-
# ...
|
760
|
-
|
761
|
-
config.on_refresh do |refresh_token|
|
762
|
-
refresh_token.destroy
|
763
|
-
|
764
|
-
MyAwesomeLogger.info("Token ##{refresh_token.id} was destroyed on refresh!")
|
765
|
-
end
|
766
|
-
end
|
767
|
-
```
|
768
|
-
|
769
|
-
## Errors (exceptions) handling
|
770
|
-
|
771
|
-
You can add any exception class from the [`rack-oauth2`](https://github.com/nov/rack-oauth2) gem (like `Rack::OAuth2::Server::Resource::Bearer::Unauthorized`)
|
772
|
-
to the `rescue_from` if you need to return some special response.
|
773
|
-
|
774
|
-
Example:
|
775
|
-
|
776
|
-
```ruby
|
777
|
-
module Twitter
|
778
|
-
class API < Grape::API
|
779
|
-
include Grape::OAuth2.api
|
780
|
-
|
781
|
-
# ...
|
782
|
-
|
783
|
-
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
|
784
|
-
error!({ status: e.status, description: e.description, error: e.error}, 400)
|
785
|
-
end
|
786
|
-
end
|
787
|
-
end
|
788
|
-
```
|
789
|
-
|
790
|
-
Do not forget to meet the OAuth 2.0 specification.
|
791
|
-
|
792
|
-
## Example App
|
793
|
-
|
794
|
-
If you want to see the gem in action then you can look at [sample app](https://github.com/grape-oauth2/grape-oauth2-sample) (deployable to Heroku).
|
795
|
-
|
796
|
-
Or you can take a look at the [sample applications](https://github.com/nbulaj/grape_oauth2/tree/master/spec/dummy) in the "_spec/dummy_" project directory.
|
797
|
-
|
798
|
-
## Contributing
|
799
|
-
|
800
|
-
You are very welcome to help improve `grape_oauth2` if you have suggestions for features that other people can use.
|
801
|
-
|
802
|
-
To contribute:
|
803
|
-
|
804
|
-
1. Fork the project.
|
805
|
-
1. Create your feature branch (`git checkout -b my-new-feature`).
|
806
|
-
1. Implement your feature or bug fix.
|
807
|
-
1. Add documentation for your feature or bug fix.
|
808
|
-
1. Add tests for your feature or bug fix.
|
809
|
-
1. Run `rake` to make sure all tests pass.
|
810
|
-
1. Commit your changes (`git commit -am 'Add new feature'`).
|
811
|
-
1. Push to the branch (`git push origin my-new-feature`).
|
812
|
-
1. Create new pull request.
|
813
|
-
|
814
|
-
Thanks.
|
815
|
-
|
816
|
-
## License
|
817
|
-
|
818
|
-
`Grape::OAuth2` gem is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
819
|
-
|
820
|
-
Copyright (c) 2014-2016 Nikita Bulai (bulajnikita@gmail.com).
|