mustwin_devise_oauth2_providable 1.1.3
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.
- data/.gitignore +35 -0
- data/.rvmrc +1 -0
- data/CONTRIBUTORS.txt +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +158 -0
- data/Rakefile +8 -0
- data/app/controllers/devise/oauth2_providable/authorizations_controller.rb +59 -0
- data/app/controllers/devise/oauth2_providable/tokens_controller.rb +17 -0
- data/app/models/devise/oauth2_providable/access_token.rb +24 -0
- data/app/models/devise/oauth2_providable/authorization_code.rb +3 -0
- data/app/models/devise/oauth2_providable/client.rb +24 -0
- data/app/models/devise/oauth2_providable/refresh_token.rb +8 -0
- data/app/views/devise/oauth2_providable/authorizations/_form.html.erb +7 -0
- data/app/views/devise/oauth2_providable/authorizations/error.html.erb +4 -0
- data/app/views/devise/oauth2_providable/authorizations/new.html.erb +4 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20111014160714_create_devise_oauth2_providable_schema.rb +54 -0
- data/devise_oauth2_providable.gemspec +32 -0
- data/lib/devise/oauth2_providable/engine.rb +16 -0
- data/lib/devise/oauth2_providable/expirable_token.rb +58 -0
- data/lib/devise/oauth2_providable/models/oauth2_authorization_code_grantable.rb +6 -0
- data/lib/devise/oauth2_providable/models/oauth2_password_grantable.rb +6 -0
- data/lib/devise/oauth2_providable/models/oauth2_providable.rb +13 -0
- data/lib/devise/oauth2_providable/models/oauth2_refresh_token_grantable.rb +6 -0
- data/lib/devise/oauth2_providable/strategies/oauth2_authorization_code_grant_type_strategy.rb +21 -0
- data/lib/devise/oauth2_providable/strategies/oauth2_grant_type_strategy.rb +39 -0
- data/lib/devise/oauth2_providable/strategies/oauth2_password_grant_type_strategy.rb +22 -0
- data/lib/devise/oauth2_providable/strategies/oauth2_providable_strategy.rb +25 -0
- data/lib/devise/oauth2_providable/strategies/oauth2_refresh_token_grant_type_strategy.rb +22 -0
- data/lib/devise/oauth2_providable/version.rb +5 -0
- data/lib/devise_oauth2_providable.rb +41 -0
- data/script/rails +6 -0
- data/spec/controllers/authorizations_controller_spec.rb +32 -0
- data/spec/controllers/protected_controller_spec.rb +42 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +7 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/protected_controller.rb +6 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +51 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/devise.rb +210 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/devise.en.yml +58 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20111014142838_create_users.rb +9 -0
- data/spec/dummy/db/migrate/20111014161437_create_devise_oauth2_providable_schema.rb +55 -0
- data/spec/dummy/db/schema.rb +78 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/client_factory.rb +5 -0
- data/spec/factories/user_factory.rb +4 -0
- data/spec/integration/oauth2_authorization_token_grant_type_strategy_spec.rb +136 -0
- data/spec/integration/oauth2_password_grant_type_strategy_spec.rb +174 -0
- data/spec/integration/oauth2_refresh_token_grant_type_strategy_spec.rb +138 -0
- data/spec/lib/devise_oauth2_providable_spec.rb +7 -0
- data/spec/models/access_token_spec.rb +53 -0
- data/spec/models/authorization_code_spec.rb +23 -0
- data/spec/models/client_spec.rb +22 -0
- data/spec/models/refresh_token_spec.rb +26 -0
- data/spec/models/user_spec.rb +6 -0
- data/spec/routing/authorizations_routing_spec.rb +16 -0
- data/spec/routing/tokens_routing_spec.rb +9 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/inject_engine_routes_into_application.rb +74 -0
- data/spec/support/match_json.rb +6 -0
- metadata +357 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# rcov generated
|
|
2
|
+
coverage
|
|
3
|
+
|
|
4
|
+
# rdoc generated
|
|
5
|
+
rdoc
|
|
6
|
+
|
|
7
|
+
# yard generated
|
|
8
|
+
doc
|
|
9
|
+
.yardoc
|
|
10
|
+
|
|
11
|
+
# bundler
|
|
12
|
+
.bundle
|
|
13
|
+
Gemfile.lock
|
|
14
|
+
|
|
15
|
+
# jeweler generated
|
|
16
|
+
pkg
|
|
17
|
+
|
|
18
|
+
# test files
|
|
19
|
+
test/*.log
|
|
20
|
+
test/*.sqlite3
|
|
21
|
+
|
|
22
|
+
# For vim:
|
|
23
|
+
*.swp
|
|
24
|
+
|
|
25
|
+
# For MacOS:
|
|
26
|
+
.DS_Store
|
|
27
|
+
|
|
28
|
+
# git files
|
|
29
|
+
*.orig
|
|
30
|
+
|
|
31
|
+
# rails files
|
|
32
|
+
tmp
|
|
33
|
+
log
|
|
34
|
+
*.log
|
|
35
|
+
*.sqlite3
|
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use @devise_oauth2_providable --create
|
data/CONTRIBUTORS.txt
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2011 Socialcast, Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# devise_oauth2_providable
|
|
2
|
+
|
|
3
|
+
Rails3 engine that brings OAuth2 Provider support to your application.
|
|
4
|
+
|
|
5
|
+
Current OAuth2 Specification Draft:
|
|
6
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-22
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
* integrate OAuth2 authentication with Devise authenthentication stack
|
|
11
|
+
* one-stop-shop includes all Models, Controllers and Views to get up and
|
|
12
|
+
running quickly
|
|
13
|
+
* All server requests support authentication via bearer token included in
|
|
14
|
+
the request. http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-04
|
|
15
|
+
* customizable mount point for oauth2 routes (ex: /oauth2 vs /oauth)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
* Devise authentication library
|
|
21
|
+
* Rails 3.1 or higher
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
#### Install gem
|
|
26
|
+
```ruby
|
|
27
|
+
# Gemfile
|
|
28
|
+
gem 'devise_oauth2_providable'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Migrate database for Oauth2 models
|
|
32
|
+
```
|
|
33
|
+
$ rake devise_oauth2_providable:install:migrations
|
|
34
|
+
$ rake db:migrate
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Add Oauth2 Routes
|
|
38
|
+
```ruby
|
|
39
|
+
# config/routes.rb
|
|
40
|
+
Rails.application.routes.draw do
|
|
41
|
+
# oauth routes can be mounted to any path (ex: /oauth2 or /oauth)
|
|
42
|
+
mount Devise::Oauth2Providable::Engine => '/oauth2'
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Configure User for supported Oauth2 flows
|
|
47
|
+
```ruby
|
|
48
|
+
class User
|
|
49
|
+
# NOTE: include :database_authenticatable configuration
|
|
50
|
+
# if supporting Resource Owner Password Credentials Grant Type
|
|
51
|
+
devise :oauth2_providable,
|
|
52
|
+
:oauth2_password_grantable,
|
|
53
|
+
:oauth2_refresh_token_grantable,
|
|
54
|
+
:oauth2_authorization_code_grantable
|
|
55
|
+
end
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### (optional) Configure token expiration settings
|
|
59
|
+
```ruby
|
|
60
|
+
# config/application.rb
|
|
61
|
+
config.devise_oauth2_providable.access_token_expires_in = 1.second # 15.minute default
|
|
62
|
+
config.devise_oauth2_providable.refresh_token_expires_in = 1.minute # 1.month default
|
|
63
|
+
config.devise_oauth2_providable.authorization_token_expires_in = 5.seconds # 1.minute default
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Models
|
|
67
|
+
|
|
68
|
+
### Client
|
|
69
|
+
registered OAuth2 client for storing the unique client_id and
|
|
70
|
+
client_secret.
|
|
71
|
+
|
|
72
|
+
### AccessToken
|
|
73
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-1.3
|
|
74
|
+
|
|
75
|
+
Short lived token used by clients to perform subsequent requests (see
|
|
76
|
+
bearer token spec)
|
|
77
|
+
|
|
78
|
+
expires after 15min by default. to customize the duration of the access token:
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
Devise::Oauth2Providable::AccessToken.default_lifetime = 1.minute
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### RefreshToken
|
|
85
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-1.5
|
|
86
|
+
|
|
87
|
+
Long lived token used by clients to request new access tokens without
|
|
88
|
+
requiring user intervention to re-authorize.
|
|
89
|
+
|
|
90
|
+
expires after 1 month by default. to customize the duration of refresh token:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Devise::Oauth2Providable::RefreshToken.default_lifetime = 1.year
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### AuthorizationCode
|
|
97
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-1.4.1
|
|
98
|
+
|
|
99
|
+
*Very* short lived token created to allow a client to request an access
|
|
100
|
+
token after a user has gone through the authorization flow.
|
|
101
|
+
|
|
102
|
+
expires after 1min by default. to customize the duration of the
|
|
103
|
+
authorization code:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
Devise::Oauth2Providable::AuthorizationCode.default_lifetime = 5.minutes
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Routes
|
|
110
|
+
|
|
111
|
+
### /oauth2/authorize
|
|
112
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-2.1
|
|
113
|
+
|
|
114
|
+
Endpoint to start client authorization flow. Models, controllers and
|
|
115
|
+
views are included for out of the box deployment.
|
|
116
|
+
|
|
117
|
+
Supports the Authorization Code and Implicit grant types.
|
|
118
|
+
|
|
119
|
+
### /oauth2/token
|
|
120
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-2.2
|
|
121
|
+
|
|
122
|
+
Endpoint to request access token. See grant type documentation for
|
|
123
|
+
supported flows.
|
|
124
|
+
|
|
125
|
+
## Grant Types
|
|
126
|
+
|
|
127
|
+
### Resource Owner Password Credentials Grant Type
|
|
128
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
|
|
129
|
+
|
|
130
|
+
in order to use the Resource Owner Password Credentials Grant Type, your
|
|
131
|
+
Devise model *must* be configured with the :database_authenticatable option
|
|
132
|
+
|
|
133
|
+
### Client Credentials Grant Type
|
|
134
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
|
|
135
|
+
|
|
136
|
+
### Authorization Code Grant Type
|
|
137
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
|
|
138
|
+
|
|
139
|
+
### Implicit Grant Type
|
|
140
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.2
|
|
141
|
+
|
|
142
|
+
### Refresh Token Grant Type
|
|
143
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6
|
|
144
|
+
|
|
145
|
+
## Contributing
|
|
146
|
+
|
|
147
|
+
* Fork the project
|
|
148
|
+
* Fix the issue
|
|
149
|
+
* Add unit tests
|
|
150
|
+
* Submit pull request on github
|
|
151
|
+
|
|
152
|
+
See CONTRIBUTORS.txt for list of project contributors
|
|
153
|
+
|
|
154
|
+
## Copyright
|
|
155
|
+
|
|
156
|
+
Copyright (c) 2011 Socialcast, Inc.
|
|
157
|
+
See LICENSE.txt for further details.
|
|
158
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
module Oauth2Providable
|
|
3
|
+
class AuthorizationsController < ApplicationController
|
|
4
|
+
before_filter :authenticate_user!
|
|
5
|
+
|
|
6
|
+
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
|
|
7
|
+
@error = e
|
|
8
|
+
render :error, :status => e.status
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def new
|
|
12
|
+
respond *authorize_endpoint.call(request.env)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create
|
|
16
|
+
respond *authorize_endpoint(:allow_approval).call(request.env)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def respond(status, header, response)
|
|
22
|
+
["WWW-Authenticate"].each do |key|
|
|
23
|
+
headers[key] = header[key] if header[key].present?
|
|
24
|
+
end
|
|
25
|
+
if response.redirect?
|
|
26
|
+
redirect_to header['Location']
|
|
27
|
+
else
|
|
28
|
+
render :new
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def authorize_endpoint(allow_approval = false)
|
|
33
|
+
Rack::OAuth2::Server::Authorize.new do |req, res|
|
|
34
|
+
@client = Client.find_by_identifier(req.client_id) || req.bad_request!
|
|
35
|
+
res.redirect_uri = @redirect_uri = req.verify_redirect_uri!(@client.redirect_uri)
|
|
36
|
+
if allow_approval
|
|
37
|
+
if params[:approve].present?
|
|
38
|
+
case req.response_type
|
|
39
|
+
when :code
|
|
40
|
+
authorization_code = current_user.authorization_codes.create!(:client => @client)
|
|
41
|
+
res.code = authorization_code.token
|
|
42
|
+
when :token
|
|
43
|
+
access_token = current_user.access_tokens.create!(:client => @client).token
|
|
44
|
+
bearer_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => access_token)
|
|
45
|
+
res.access_token = bearer_token
|
|
46
|
+
res.uid = current_user.id
|
|
47
|
+
end
|
|
48
|
+
res.approve!
|
|
49
|
+
else
|
|
50
|
+
req.access_denied!
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
@response_type = req.response_type
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Devise::Oauth2Providable::TokensController < ApplicationController
|
|
2
|
+
before_filter :authenticate_user!
|
|
3
|
+
skip_before_filter :verify_authenticity_token, :only => :create
|
|
4
|
+
|
|
5
|
+
def create
|
|
6
|
+
@refresh_token = oauth2_current_refresh_token || oauth2_current_client.refresh_tokens.create!(:user => current_user)
|
|
7
|
+
@access_token = @refresh_token.access_tokens.create!(:client => oauth2_current_client, :user => current_user)
|
|
8
|
+
render :json => @access_token.token_response
|
|
9
|
+
end
|
|
10
|
+
private
|
|
11
|
+
def oauth2_current_client
|
|
12
|
+
env[Devise::Oauth2Providable::CLIENT_ENV_REF]
|
|
13
|
+
end
|
|
14
|
+
def oauth2_current_refresh_token
|
|
15
|
+
env[Devise::Oauth2Providable::REFRESH_TOKEN_ENV_REF]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class Devise::Oauth2Providable::AccessToken < ActiveRecord::Base
|
|
2
|
+
expires_according_to :access_token_expires_in
|
|
3
|
+
|
|
4
|
+
before_validation :restrict_expires_at, :on => :create, :if => :refresh_token
|
|
5
|
+
belongs_to :refresh_token
|
|
6
|
+
|
|
7
|
+
attr_accessible :refresh_token
|
|
8
|
+
|
|
9
|
+
def token_response
|
|
10
|
+
response = {
|
|
11
|
+
:access_token => token,
|
|
12
|
+
:token_type => 'bearer',
|
|
13
|
+
:expires_in => expires_in
|
|
14
|
+
}
|
|
15
|
+
response[:refresh_token] = refresh_token.token if refresh_token
|
|
16
|
+
response
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def restrict_expires_at
|
|
22
|
+
self.expires_at = [self.expires_at, refresh_token.expires_at].compact.min
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class Devise::Oauth2Providable::Client < ActiveRecord::Base
|
|
2
|
+
has_many :access_tokens
|
|
3
|
+
has_many :refresh_tokens
|
|
4
|
+
has_many :authorization_codes
|
|
5
|
+
|
|
6
|
+
before_validation :init_identifier, :on => :create, :unless => :identifier?
|
|
7
|
+
before_validation :init_secret, :on => :create, :unless => :secret?
|
|
8
|
+
validates :website, :secret, :presence => true
|
|
9
|
+
validates :name, :presence => true, :uniqueness => true
|
|
10
|
+
validates :identifier, :presence => true, :uniqueness => true
|
|
11
|
+
|
|
12
|
+
attr_accessible :name, :website, :redirect_uri
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def init_identifier
|
|
17
|
+
self.identifier = Devise::Oauth2Providable.random_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def init_secret
|
|
21
|
+
self.secret = Devise::Oauth2Providable.random_id
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<%= form_tag authorizations_path, :class => action do %>
|
|
2
|
+
<%= hidden_field_tag :client_id, client.identifier %>
|
|
3
|
+
<%= hidden_field_tag :response_type, response_type %>
|
|
4
|
+
<%= hidden_field_tag :redirect_uri, redirect_uri %>
|
|
5
|
+
<%= submit_tag action.to_s.capitalize %>
|
|
6
|
+
<%= hidden_field_tag action, true %>
|
|
7
|
+
<% end %>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<h2><%= link_to @client.name, @client.website %> is requesting permission to access your resources.</h2>
|
|
2
|
+
|
|
3
|
+
<%= render 'devise/oauth2_providable/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :approve %>
|
|
4
|
+
<%= render 'devise/oauth2_providable/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :deny %>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class CreateDeviseOauth2ProvidableSchema < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :oauth2_clients do |t|
|
|
4
|
+
t.string :name
|
|
5
|
+
t.string :redirect_uri
|
|
6
|
+
t.string :website
|
|
7
|
+
t.string :identifier
|
|
8
|
+
t.string :secret
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
change_table :oauth2_clients do |t|
|
|
12
|
+
t.index :identifier, :unique => true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
create_table :oauth2_access_tokens do |t|
|
|
16
|
+
t.belongs_to :user, :client, :refresh_token
|
|
17
|
+
t.string :token
|
|
18
|
+
t.datetime :expires_at
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
change_table :oauth2_access_tokens do |t|
|
|
22
|
+
t.index :token, :unique => true
|
|
23
|
+
t.index :expires_at
|
|
24
|
+
t.index :user_id
|
|
25
|
+
t.index :client_id
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
create_table :oauth2_refresh_tokens do |t|
|
|
29
|
+
t.belongs_to :user, :client
|
|
30
|
+
t.string :token
|
|
31
|
+
t.datetime :expires_at
|
|
32
|
+
t.timestamps
|
|
33
|
+
end
|
|
34
|
+
change_table :oauth2_refresh_tokens do |t|
|
|
35
|
+
t.index :token, :unique => true
|
|
36
|
+
t.index :expires_at
|
|
37
|
+
t.index :user_id
|
|
38
|
+
t.index :client_id
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
create_table :oauth2_authorization_codes do |t|
|
|
42
|
+
t.belongs_to :user, :client
|
|
43
|
+
t.string :token
|
|
44
|
+
t.datetime :expires_at
|
|
45
|
+
t.timestamps
|
|
46
|
+
end
|
|
47
|
+
change_table :oauth2_authorization_codes do |t|
|
|
48
|
+
t.index :token, :unique => true
|
|
49
|
+
t.index :expires_at
|
|
50
|
+
t.index :user_id
|
|
51
|
+
t.index :client_id
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "devise/oauth2_providable/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "mustwin_devise_oauth2_providable"
|
|
7
|
+
s.version = Devise::Oauth2Providable::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Ryan Sonnek"]
|
|
10
|
+
s.email = ["ryan@socialcast.com"]
|
|
11
|
+
s.homepage = ""
|
|
12
|
+
s.summary = %q{OAuth2 Provider for Rails3 applications}
|
|
13
|
+
s.description = %q{Rails3 engine that adds OAuth2 Provider support to any application built with Devise authentication}
|
|
14
|
+
|
|
15
|
+
s.rubyforge_project = "devise_oauth2_providable"
|
|
16
|
+
|
|
17
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.1.0"])
|
|
18
|
+
s.add_runtime_dependency(%q<devise>, [">= 1.4.3"])
|
|
19
|
+
s.add_runtime_dependency(%q<rack-oauth2>, ["~> 0.11.0"])
|
|
20
|
+
s.add_development_dependency(%q<rspec-rails>, ['2.6.1'])
|
|
21
|
+
s.add_development_dependency(%q<sqlite3>, ['1.3.5'])
|
|
22
|
+
s.add_development_dependency(%q<shoulda-matchers>, ['1.0.0.beta3'])
|
|
23
|
+
s.add_development_dependency(%q<pry>, ['0.9.6.2'])
|
|
24
|
+
s.add_development_dependency(%q<factory_girl>, ['2.2.0'])
|
|
25
|
+
s.add_development_dependency(%q<factory_girl_rspec>, ['0.0.1'])
|
|
26
|
+
s.add_development_dependency(%q<rake>, ['0.9.2.2'])
|
|
27
|
+
|
|
28
|
+
s.files = `git ls-files`.split("\n")
|
|
29
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
30
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
31
|
+
s.require_paths = ["lib"]
|
|
32
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
module Oauth2Providable
|
|
3
|
+
class Engine < Rails::Engine
|
|
4
|
+
config.devise_oauth2_providable = ActiveSupport::OrderedOptions.new
|
|
5
|
+
config.devise_oauth2_providable.access_token_expires_in = 15.minutes
|
|
6
|
+
config.devise_oauth2_providable.refresh_token_expires_in = 1.month
|
|
7
|
+
config.devise_oauth2_providable.authorization_code_expires_in = 1.minute
|
|
8
|
+
|
|
9
|
+
engine_name 'oauth2'
|
|
10
|
+
isolate_namespace Devise::Oauth2Providable
|
|
11
|
+
initializer "devise_oauth2_providable.initialize_application", :before=> :load_config_initializers do |app|
|
|
12
|
+
app.config.filter_parameters << :client_secret
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
|
|
4
|
+
module Devise
|
|
5
|
+
module Oauth2Providable
|
|
6
|
+
module ExpirableToken
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def expires_according_to(config_name)
|
|
11
|
+
cattr_accessor :default_lifetime
|
|
12
|
+
self.default_lifetime = Rails.application.config.devise_oauth2_providable[config_name]
|
|
13
|
+
|
|
14
|
+
belongs_to :user
|
|
15
|
+
belongs_to :client
|
|
16
|
+
|
|
17
|
+
attr_accessible :user, :client
|
|
18
|
+
|
|
19
|
+
after_initialize :init_token, :on => :create, :unless => :token?
|
|
20
|
+
after_initialize :init_expires_at, :on => :create, :unless => :expires_at?
|
|
21
|
+
validates :expires_at, :presence => true
|
|
22
|
+
validates :client, :presence => true
|
|
23
|
+
validates :token, :presence => true, :uniqueness => true
|
|
24
|
+
|
|
25
|
+
default_scope lambda {
|
|
26
|
+
where(self.arel_table[:expires_at].gteq(Time.now.utc))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
include LocalInstanceMethods
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module LocalInstanceMethods
|
|
34
|
+
# number of seconds until the token expires
|
|
35
|
+
def expires_in
|
|
36
|
+
(expires_at - Time.now.utc).to_i
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# forcefully expire the token
|
|
40
|
+
def expired!
|
|
41
|
+
self.expires_at = Time.now.utc
|
|
42
|
+
self.save!
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def init_token
|
|
48
|
+
self.token = Devise::Oauth2Providable.random_id
|
|
49
|
+
end
|
|
50
|
+
def init_expires_at
|
|
51
|
+
self.expires_at = self.default_lifetime.from_now
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
ActiveRecord::Base.send :include, Devise::Oauth2Providable::ExpirableToken
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'devise/models'
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Models
|
|
5
|
+
module Oauth2Providable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
included do
|
|
8
|
+
has_many :access_tokens, :class_name => 'Devise::Oauth2Providable::AccessToken'
|
|
9
|
+
has_many :authorization_codes, :class_name => 'Devise::Oauth2Providable::AuthorizationCode'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'devise/oauth2_providable/strategies/oauth2_grant_type_strategy'
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Strategies
|
|
5
|
+
class Oauth2AuthorizationCodeGrantTypeStrategy < Oauth2GrantTypeStrategy
|
|
6
|
+
def grant_type
|
|
7
|
+
'authorization_code'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def authenticate_grant_type(client)
|
|
11
|
+
if code = client.authorization_codes.find_by_token(params[:code])
|
|
12
|
+
success! code.user
|
|
13
|
+
else
|
|
14
|
+
oauth_error! :invalid_grant, 'invalid authorization code request'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Warden::Strategies.add(:oauth2_authorization_code_grantable, Devise::Strategies::Oauth2AuthorizationCodeGrantTypeStrategy)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'devise/strategies/base'
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Strategies
|
|
5
|
+
class Oauth2GrantTypeStrategy < Authenticatable
|
|
6
|
+
def valid?
|
|
7
|
+
params[:controller] == 'devise/oauth2_providable/tokens' && request.post? && params[:grant_type] == grant_type
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# defined by subclass
|
|
11
|
+
def grant_type
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# defined by subclass
|
|
15
|
+
def authenticate_grant_type(client)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def authenticate!
|
|
19
|
+
client_id, client_secret = request.authorization ? decode_credentials : [params[:client_id], params[:client_secret]]
|
|
20
|
+
client = Devise::Oauth2Providable::Client.find_by_identifier client_id
|
|
21
|
+
if client && client.secret == client_secret
|
|
22
|
+
env[Devise::Oauth2Providable::CLIENT_ENV_REF] = client
|
|
23
|
+
authenticate_grant_type(client)
|
|
24
|
+
else
|
|
25
|
+
oauth_error! :invalid_client, 'invalid client credentials'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# return custom error response in accordance with the oauth spec
|
|
30
|
+
# see http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-4.3
|
|
31
|
+
def oauth_error!(error_code = :invalid_request, description = nil)
|
|
32
|
+
body = {:error => error_code}
|
|
33
|
+
body[:error_description] = description if description
|
|
34
|
+
custom! [400, {'Content-Type' => 'application/json'}, [body.to_json]]
|
|
35
|
+
throw :warden
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|