oa2c 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.md +22 -0
- data/Rakefile +29 -0
- data/app/controllers/oa2c/authorizations_controller.rb +87 -0
- data/app/models/oa2c/access_token.rb +29 -0
- data/app/models/oa2c/authorization_code.rb +10 -0
- data/app/models/oa2c/client.rb +32 -0
- data/app/models/oa2c/oauth2_token.rb +39 -0
- data/app/models/oa2c/refresh_token.rb +10 -0
- data/app/views/oa2c/_form.html.erb +7 -0
- data/app/views/oa2c/error.html.erb +3 -0
- data/app/views/oa2c/new.html.erb +5 -0
- data/config/routes.rb +5 -0
- data/lib/oa2c.rb +6 -0
- data/lib/oa2c/authentication.rb +29 -0
- data/lib/oa2c/engine.rb +6 -0
- data/lib/oa2c/version.rb +3 -0
- data/lib/tasks/oa2c_tasks.rake +4 -0
- metadata +149 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Anton Dieterle
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Oa2c - OAuth2 Container
|
2
|
+
|
3
|
+
Rails OAuth2 provider engine to support embedded iframe applications from different sites. Just like games on Facebook.
|
4
|
+
|
5
|
+
Heavily depends on [rack-oauth2](https://github.com/nov/rack-oauth2) gem.
|
6
|
+
|
7
|
+
It's mostly extract from [rack-oauth2-sample](https://github.com/nov/rack-oauth2-sample).
|
8
|
+
|
9
|
+
Right now it supports only Mongoid 3 as ORM.
|
10
|
+
|
11
|
+
# TODO
|
12
|
+
|
13
|
+
* Write specs
|
14
|
+
* Support for ActiveRecord
|
15
|
+
|
16
|
+
# License
|
17
|
+
|
18
|
+
See MIT-LICENSE
|
19
|
+
|
20
|
+
# Contribute
|
21
|
+
|
22
|
+
Pull requests are more than welcome!
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Oauth2Container'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
Bundler::GemHelper.install_tasks
|
25
|
+
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new
|
28
|
+
|
29
|
+
task :default => :spec
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Oa2c
|
2
|
+
class AuthorizationsController < ActionController::Base
|
3
|
+
before_filter :authorize, except: :token
|
4
|
+
|
5
|
+
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
|
6
|
+
@error = e
|
7
|
+
render :error, status: e.status
|
8
|
+
end
|
9
|
+
|
10
|
+
def new
|
11
|
+
respond *authorize_endpoint.call(request.env)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
respond *authorize_endpoint(true).call(request.env)
|
16
|
+
end
|
17
|
+
|
18
|
+
def token
|
19
|
+
token_endpoint.call(request.env)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def respond(status, header, response)
|
25
|
+
["WWW-Authenticate"].each do |key|
|
26
|
+
headers[key] = header[key] if header[key].present?
|
27
|
+
end
|
28
|
+
if response.redirect?
|
29
|
+
redirect_to header['Location']
|
30
|
+
else
|
31
|
+
render :new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorize_endpoint(allow_approval = false)
|
36
|
+
Rack::OAuth2::Server::Authorize.new do |req, res|
|
37
|
+
@client = Auth::Client.where(identifier: req.client_id).first || req.bad_request!
|
38
|
+
res.redirect_uri = @redirect_uri = req.verify_redirect_uri!(@client.redirect_uri)
|
39
|
+
if allow_approval
|
40
|
+
if params[:approve]
|
41
|
+
case req.response_type
|
42
|
+
when :code
|
43
|
+
authorization_code = current_user.authorization_codes.create(client_id: @client.id, redirect_uri: res.redirect_uri)
|
44
|
+
res.code = authorization_code.token
|
45
|
+
when :token
|
46
|
+
res.access_token = current_user.access_tokens.create(client_id: @client.id).to_bearer_token
|
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
|
+
|
58
|
+
def token_endpoint
|
59
|
+
Rack::OAuth2::Server::Token.new do |req, res|
|
60
|
+
client = Auth::Client.where(identifier: req.client_id).first || req.invalid_client!
|
61
|
+
client.secret == req.client_secret || req.invalid_client!
|
62
|
+
case req.grant_type
|
63
|
+
when :authorization_code
|
64
|
+
code = Auth::AuthorizationCode.valid.where(token: req.code).first
|
65
|
+
req.invalid_grant! if code.blank? || code.redirect_uri != req.redirect_uri
|
66
|
+
res.access_token = code.access_token.to_bearer_token(:with_refresh_token)
|
67
|
+
when :password
|
68
|
+
# NOTE: password is not hashed in this sample app. Don't do the same on your app.
|
69
|
+
# FIXME
|
70
|
+
account = Account.find_by_username_and_password(req.username, req.password) || req.invalid_grant!
|
71
|
+
res.access_token = account.access_tokens.create(:client => client).to_bearer_token(:with_refresh_token)
|
72
|
+
when :client_credentials
|
73
|
+
# NOTE: client is already authenticated here.
|
74
|
+
res.access_token = client.access_tokens.create.to_bearer_token
|
75
|
+
when :refresh_token
|
76
|
+
refresh_token = client.refresh_tokens.valid.wehre(token: req.refresh_token).first
|
77
|
+
req.invalid_grant! unless refresh_token
|
78
|
+
res.access_token = refresh_token.access_tokens.create.to_bearer_token
|
79
|
+
else
|
80
|
+
# NOTE: extended assertion grant_types are not supported yet.
|
81
|
+
req.unsupported_grant_type!
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'oa2c/oauth2_token'
|
2
|
+
module Oa2c
|
3
|
+
class AccessToken
|
4
|
+
include Mongoid::Document
|
5
|
+
include OAuth2Token
|
6
|
+
|
7
|
+
belongs_to :refresh_token
|
8
|
+
|
9
|
+
def to_bearer_token(with_refresh_token = false)
|
10
|
+
Rack::OAuth2::AccessToken::Bearer.new(access_token: token, expires_in: expires_in).tap do |bearer_token|
|
11
|
+
if with_refresh_token
|
12
|
+
bearer_token.refresh_token = create_refresh_token(user: user, client: client).token
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def setup
|
20
|
+
super
|
21
|
+
if refresh_token
|
22
|
+
self.user = refresh_token.user
|
23
|
+
self.client = refresh_token.client
|
24
|
+
self.expires_at = [expires_at, refresh_token.expires_at].min
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Oa2c
|
2
|
+
class Client
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
field :identifier
|
6
|
+
field :secret
|
7
|
+
field :redirect_uri
|
8
|
+
|
9
|
+
has_many :access_tokens
|
10
|
+
has_many :refresh_tokens
|
11
|
+
|
12
|
+
before_validation :setup, on: :create
|
13
|
+
validates :secret, presence: true
|
14
|
+
validates :identifier, presence: true, uniqueness: true
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def setup
|
19
|
+
self.identifier = SecureRandom.hex(16)
|
20
|
+
generate_secret
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_secret
|
24
|
+
self.secret = SecureRandom.hex(32)
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_secret!
|
28
|
+
generate_secret
|
29
|
+
save!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Oa2c
|
2
|
+
module OAuth2Token
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
cattr_accessor :default_lifetime
|
7
|
+
self.default_lifetime = 15.minutes
|
8
|
+
|
9
|
+
field :token
|
10
|
+
field :expires_at, type: Time
|
11
|
+
|
12
|
+
belongs_to :user
|
13
|
+
belongs_to :client
|
14
|
+
|
15
|
+
before_validation :setup, on: :create
|
16
|
+
validates :client, :expires_at, presence: true
|
17
|
+
validates :token, presence: true, uniqueness: true
|
18
|
+
|
19
|
+
scope :valid, proc { where(:expires_at.gte => Time.now) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def expires_in
|
23
|
+
(expires_at - Time.now).to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def expire!
|
27
|
+
update_attributes! expires_at: Time.now
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def setup
|
33
|
+
self.token = SecureRandom.hex(32)
|
34
|
+
self.expires_at ||= default_lifetime.from_now
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
@@ -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
|
+
<%= hiden_field_tag action, true %>
|
7
|
+
<% end %>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<h2><%= @client.name %> is requiring your resource access</h2>
|
2
|
+
|
3
|
+
<%= render 'form', client: @client, response_type: @response_type, redirect_uri: @redirect_uri, action: :deny %>
|
4
|
+
<%= render 'form', client: @client, response_type: @response_type, redirect_uri: @redirect_uri, action: :approve %>
|
5
|
+
|
data/config/routes.rb
ADDED
data/lib/oa2c.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Oa2c
|
2
|
+
module Authentication
|
3
|
+
def current_token
|
4
|
+
@current_token
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_client
|
8
|
+
@current_client
|
9
|
+
end
|
10
|
+
|
11
|
+
def require_oauth_token
|
12
|
+
@current_token = Auth::AccessToken.where(token: request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN].token).first # FIXME Right now an AccessToken object is store here, need to store only token
|
13
|
+
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized unless @current_token
|
14
|
+
end
|
15
|
+
|
16
|
+
def require_oauth_user_token
|
17
|
+
require_oauth_token
|
18
|
+
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:invalid_token, 'User token is required') unless @current_token.user
|
19
|
+
authenticate @current_token.user
|
20
|
+
end
|
21
|
+
|
22
|
+
def require_oauth_client_token
|
23
|
+
require_oauth_token
|
24
|
+
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:invalid_token, 'Client token is required') if @current_token.user
|
25
|
+
@current_client = @current_token.client
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/oa2c/engine.rb
ADDED
data/lib/oa2c/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oa2c
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anton Dieterle
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rack-oauth2
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.14.9
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.14.9
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mongoid
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec-rails
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: sqlite3
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Rails engine to provide OAuth2 authorization for embedded iframe applications.
|
95
|
+
email:
|
96
|
+
- antondie@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- app/models/oa2c/oauth2_token.rb
|
102
|
+
- app/models/oa2c/client.rb
|
103
|
+
- app/models/oa2c/refresh_token.rb
|
104
|
+
- app/models/oa2c/authorization_code.rb
|
105
|
+
- app/models/oa2c/access_token.rb
|
106
|
+
- app/views/oa2c/_form.html.erb
|
107
|
+
- app/views/oa2c/error.html.erb
|
108
|
+
- app/views/oa2c/new.html.erb
|
109
|
+
- app/controllers/oa2c/authorizations_controller.rb
|
110
|
+
- config/routes.rb
|
111
|
+
- lib/tasks/oa2c_tasks.rake
|
112
|
+
- lib/oa2c.rb
|
113
|
+
- lib/oa2c/version.rb
|
114
|
+
- lib/oa2c/authentication.rb
|
115
|
+
- lib/oa2c/engine.rb
|
116
|
+
- MIT-LICENSE
|
117
|
+
- Rakefile
|
118
|
+
- README.md
|
119
|
+
homepage: https://github.com/adie/oa2c
|
120
|
+
licenses: []
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
hash: -181462029
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ! '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
hash: -181462029
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 1.8.24
|
146
|
+
signing_key:
|
147
|
+
specification_version: 3
|
148
|
+
summary: OAuth2 authorization for embedded iframe applications.
|
149
|
+
test_files: []
|