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.
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).