grape_oauth2 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -11
  3. data/Gemfile +23 -23
  4. data/Rakefile +11 -11
  5. data/grape_oauth2.gemspec +26 -27
  6. data/lib/grape_oauth2.rb +129 -129
  7. data/lib/grape_oauth2/configuration.rb +143 -143
  8. data/lib/grape_oauth2/configuration/class_accessors.rb +36 -36
  9. data/lib/grape_oauth2/configuration/validation.rb +71 -71
  10. data/lib/grape_oauth2/endpoints/authorize.rb +34 -34
  11. data/lib/grape_oauth2/endpoints/token.rb +72 -72
  12. data/lib/grape_oauth2/gem_version.rb +24 -24
  13. data/lib/grape_oauth2/generators/authorization.rb +44 -44
  14. data/lib/grape_oauth2/generators/base.rb +26 -26
  15. data/lib/grape_oauth2/generators/token.rb +62 -62
  16. data/lib/grape_oauth2/helpers/access_token_helpers.rb +52 -54
  17. data/lib/grape_oauth2/helpers/oauth_params.rb +41 -41
  18. data/lib/grape_oauth2/mixins/active_record/access_grant.rb +47 -47
  19. data/lib/grape_oauth2/mixins/active_record/access_token.rb +75 -75
  20. data/lib/grape_oauth2/mixins/active_record/client.rb +36 -35
  21. data/lib/grape_oauth2/mixins/mongoid/access_grant.rb +58 -58
  22. data/lib/grape_oauth2/mixins/mongoid/access_token.rb +88 -88
  23. data/lib/grape_oauth2/mixins/mongoid/client.rb +44 -41
  24. data/lib/grape_oauth2/mixins/sequel/access_grant.rb +68 -68
  25. data/lib/grape_oauth2/mixins/sequel/access_token.rb +86 -86
  26. data/lib/grape_oauth2/mixins/sequel/client.rb +54 -46
  27. data/lib/grape_oauth2/responses/authorization.rb +11 -10
  28. data/lib/grape_oauth2/responses/base.rb +56 -56
  29. data/lib/grape_oauth2/responses/token.rb +10 -10
  30. data/lib/grape_oauth2/scopes.rb +74 -74
  31. data/lib/grape_oauth2/strategies/authorization_code.rb +38 -38
  32. data/lib/grape_oauth2/strategies/base.rb +47 -47
  33. data/lib/grape_oauth2/strategies/client_credentials.rb +20 -20
  34. data/lib/grape_oauth2/strategies/password.rb +22 -22
  35. data/lib/grape_oauth2/strategies/refresh_token.rb +47 -47
  36. data/lib/grape_oauth2/unique_token.rb +20 -20
  37. data/lib/grape_oauth2/version.rb +14 -14
  38. data/spec/configuration/config_spec.rb +231 -231
  39. data/spec/configuration/version_spec.rb +12 -12
  40. data/spec/dummy/endpoints/custom_authorization.rb +25 -25
  41. data/spec/dummy/endpoints/custom_token.rb +35 -35
  42. data/spec/dummy/endpoints/status.rb +25 -25
  43. data/spec/dummy/grape_oauth2_config.rb +11 -11
  44. data/spec/dummy/orm/active_record/app/config/db.rb +7 -7
  45. data/spec/dummy/orm/active_record/app/models/access_code.rb +3 -3
  46. data/spec/dummy/orm/active_record/app/models/access_token.rb +3 -3
  47. data/spec/dummy/orm/active_record/app/models/application.rb +3 -3
  48. data/spec/dummy/orm/active_record/app/models/application_record.rb +3 -3
  49. data/spec/dummy/orm/active_record/app/models/user.rb +10 -10
  50. data/spec/dummy/orm/active_record/app/twitter.rb +36 -36
  51. data/spec/dummy/orm/active_record/config.ru +7 -7
  52. data/spec/dummy/orm/active_record/db/schema.rb +53 -53
  53. data/spec/dummy/orm/mongoid/app/config/db.rb +6 -6
  54. data/spec/dummy/orm/mongoid/app/config/mongoid.yml +21 -21
  55. data/spec/dummy/orm/mongoid/app/models/access_code.rb +3 -3
  56. data/spec/dummy/orm/mongoid/app/models/access_token.rb +3 -3
  57. data/spec/dummy/orm/mongoid/app/models/application.rb +3 -3
  58. data/spec/dummy/orm/mongoid/app/models/user.rb +11 -11
  59. data/spec/dummy/orm/mongoid/app/twitter.rb +34 -34
  60. data/spec/dummy/orm/mongoid/config.ru +5 -5
  61. data/spec/dummy/orm/sequel/app/config/db.rb +1 -1
  62. data/spec/dummy/orm/sequel/app/models/access_code.rb +4 -4
  63. data/spec/dummy/orm/sequel/app/models/access_token.rb +4 -4
  64. data/spec/dummy/orm/sequel/app/models/application.rb +4 -4
  65. data/spec/dummy/orm/sequel/app/models/application_record.rb +2 -2
  66. data/spec/dummy/orm/sequel/app/models/user.rb +11 -11
  67. data/spec/dummy/orm/sequel/app/twitter.rb +47 -47
  68. data/spec/dummy/orm/sequel/config.ru +5 -5
  69. data/spec/dummy/orm/sequel/db/schema.rb +50 -50
  70. data/spec/lib/scopes_spec.rb +50 -50
  71. data/spec/mixins/active_record/access_token_spec.rb +185 -185
  72. data/spec/mixins/active_record/client_spec.rb +104 -95
  73. data/spec/mixins/mongoid/access_token_spec.rb +185 -185
  74. data/spec/mixins/mongoid/client_spec.rb +104 -95
  75. data/spec/mixins/sequel/access_token_spec.rb +185 -185
  76. data/spec/mixins/sequel/client_spec.rb +105 -96
  77. data/spec/requests/flows/authorization_code_spec.rb +67 -67
  78. data/spec/requests/flows/client_credentials_spec.rb +101 -101
  79. data/spec/requests/flows/password_spec.rb +210 -210
  80. data/spec/requests/flows/refresh_token_spec.rb +222 -222
  81. data/spec/requests/flows/revoke_token_spec.rb +103 -103
  82. data/spec/requests/protected_resources_spec.rb +64 -64
  83. data/spec/spec_helper.rb +60 -60
  84. data/spec/support/api_helper.rb +11 -11
  85. metadata +50 -52
  86. data/.rspec +0 -2
  87. data/.rubocop.yml +0 -18
  88. data/.travis.yml +0 -42
  89. data/README.md +0 -820
  90. data/gemfiles/active_record.rb +0 -25
  91. data/gemfiles/mongoid.rb +0 -14
  92. data/gemfiles/sequel.rb +0 -24
  93. data/grape_oauth2.png +0 -0
@@ -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.1.1
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: 2016-05-31 00:00:00.000000000 Z
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.16'
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.16'
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.3.0
39
+ version: 1.6.0
34
40
  - - ">="
35
41
  - !ruby/object:Gem::Version
36
- version: 1.3.0
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.3.0
49
+ version: 1.6.0
44
50
  - - ">="
45
51
  - !ruby/object:Gem::Version
46
- version: 1.3.0
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.4.0
59
+ version: 3.6.0
54
60
  - - ">="
55
61
  - !ruby/object:Gem::Version
56
- version: 3.4.0
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.4.0
69
+ version: 3.6.0
64
70
  - - ">="
65
71
  - !ruby/object:Gem::Version
66
- version: 3.4.0
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: Provides flexible, ORM-agnostic, fully customizable and simple OAuth2
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.6.3
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/sequel/app/twitter.rb
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/config/db.rb
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/twitter.rb
234
- - spec/dummy/orm/mongoid/app/models/user.rb
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/config/mongoid.yml
239
- - spec/dummy/orm/mongoid/app/config/db.rb
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/endpoints/custom_token.rb
242
- - spec/dummy/endpoints/status.rb
243
- - spec/dummy/endpoints/custom_authorization.rb
244
- - spec/mixins/sequel/client_spec.rb
245
- - spec/mixins/sequel/access_token_spec.rb
246
- - spec/mixins/active_record/client_spec.rb
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/mongoid/client_spec.rb
243
+ - spec/mixins/active_record/client_spec.rb
249
244
  - spec/mixins/mongoid/access_token_spec.rb
250
- - spec/requests/flows/client_credentials_spec.rb
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
@@ -1,2 +0,0 @@
1
- --color
2
- --format=documentation
@@ -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/*
@@ -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
- [![Build Status](https://travis-ci.org/nbulaj/grape_oauth2.svg?branch=master)](https://travis-ci.org/nbulaj/grape_oauth2)
7
- [![Dependency Status](https://gemnasium.com/nbulaj/grape_oauth2.svg)](https://gemnasium.com/nbulaj/grape_oauth2)
8
- [![Coverage Status](https://coveralls.io/repos/github/nbulaj/grape_oauth2/badge.svg)](https://coveralls.io/github/nbulaj/grape_oauth2)
9
- [![Code Climate](https://codeclimate.com/github/nbulaj/grape_oauth2/badges/gpa.svg)](https://codeclimate.com/github/nbulaj/grape_oauth2)
10
- [![Inline docs](http://inch-ci.org/github/nbulaj/grape_oauth2.svg?branch=master)](http://inch-ci.org/github/nbulaj/grape_oauth2)
11
- [![License](http://img.shields.io/badge/license-MIT-brightgreen.svg)](#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).