devise_oauth2_token_bearer_authenticatable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format documentation
3
+
@@ -0,0 +1,6 @@
1
+ Ryan Sonnek - Original Author
2
+
3
+
4
+ Complete list of contributors:
5
+ https://github.com/socialcast/devise_oauth2_token_bearer_authenticatable/contributors
6
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in devise_oauth2_token_bearer_authenticatable.gemspec
4
+ gemspec
@@ -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
+
@@ -0,0 +1,20 @@
1
+ # devise_oauth2_token_bearer_authenticatable
2
+
3
+ Support OAuth2 authentication for your API.
4
+
5
+ http://tools.ietf.org/html/draft-ietf-oauth-v2-15
6
+
7
+ ## Contributing
8
+
9
+ * Fork the project
10
+ * Fix the issue
11
+ * Add unit tests
12
+ * Submit pull request on github
13
+
14
+ See CONTRIBUTORS.txt for list of project contributors
15
+
16
+ ## Copyright
17
+
18
+ Copyright (c) 2011 Socialcast, Inc.
19
+ See LICENSE.txt for further details.
20
+
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+ task :default => :spec
7
+
@@ -0,0 +1,52 @@
1
+ class Oauth2::AuthorizationsController < ApplicationController
2
+ before_filter :authenticate_user!
3
+
4
+ rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
5
+ @error = e
6
+ render :error, :status => e.status
7
+ end
8
+
9
+ def new
10
+ respond *authorize_endpoint.call(request.env)
11
+ end
12
+
13
+ def create
14
+ respond *authorize_endpoint(:allow_approval).call(request.env)
15
+ end
16
+
17
+ private
18
+
19
+ def respond(status, header, response)
20
+ ["WWW-Authenticate"].each do |key|
21
+ headers[key] = header[key] if header[key].present?
22
+ end
23
+ if response.redirect?
24
+ redirect_to header['Location']
25
+ else
26
+ render :new
27
+ end
28
+ end
29
+
30
+ def authorize_endpoint(allow_approval = false)
31
+ Rack::OAuth2::Server::Authorize.new do |req, res|
32
+ @client = Client.find_by_identifier(req.client_id) || req.bad_request!
33
+ res.redirect_uri = @redirect_uri = req.verify_redirect_uri!(@client.redirect_uri)
34
+ if allow_approval
35
+ if params[:approve].present?
36
+ case req.response_type
37
+ when :code
38
+ authorization_code = current_user.authorization_codes.create(:client_id => @client, :redirect_uri => @redirect_uri)
39
+ res.code = authorization_code.token
40
+ when :token
41
+ res.access_token = current_user.access_tokens.create(:client_id => @client).to_bearer_token
42
+ end
43
+ res.approve!
44
+ else
45
+ req.access_denied!
46
+ end
47
+ else
48
+ @response_type = req.response_type
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ require 'oauth2_token'
2
+
3
+ class AccessToken < ActiveRecord::Base
4
+ include Oauth2Token
5
+ self.default_lifetime = 15.minutes
6
+ belongs_to :refresh_token
7
+
8
+ def to_bearer_token(with_refresh_token = false)
9
+ bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
10
+ :access_token => self.token,
11
+ :expires_in => self.expires_in
12
+ )
13
+ if with_refresh_token
14
+ bearer_token.refresh_token = self.create_refresh_token(
15
+ :user => self.user,
16
+ :client => self.client
17
+ ).token
18
+ end
19
+ p bearer_token.token_response
20
+ bearer_token
21
+ end
22
+
23
+ private
24
+
25
+ def setup
26
+ super
27
+ if refresh_token
28
+ self.user = refresh_token.user
29
+ self.client = refresh_token.client
30
+ self.expires_at = [self.expires_at, refresh_token.expires_at].min
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ require 'oauth2_token'
2
+
3
+ class AuthorizationCode < ActiveRecord::Base
4
+ include Oauth2Token
5
+
6
+ def access_token
7
+ @access_token ||= expired! && user.access_tokens.create(:client => client)
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class Client < ActiveRecord::Base
2
+ has_many :access_tokens
3
+ has_many :refresh_tokens
4
+
5
+ before_validation :setup, :on => :create
6
+ validates :name, :website, :redirect_uri, :secret, :presence => true
7
+ validates :identifier, :presence => true, :uniqueness => true
8
+
9
+ private
10
+
11
+ def setup
12
+ self.identifier = ActiveSupport::SecureRandom.base64(16)
13
+ self.secret = ActiveSupport::SecureRandom.base64
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ require 'oauth2_token'
2
+
3
+ class RefreshToken < ActiveRecord::Base
4
+ include Oauth2Token
5
+ self.default_lifetime = 1.month
6
+ has_many :access_tokens
7
+ end
@@ -0,0 +1,7 @@
1
+ <%= form_tag oauth2_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>Invalid Authorization Request</h2>
2
+ <h3><%= @error.error %></h3>
3
+ <p><%= @error.description %></p>
4
+
@@ -0,0 +1,5 @@
1
+ <h2><%= link_to @client.name, @client.website %> is permission to access your resources.</h2>
2
+
3
+ <%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :approve %>
4
+ <%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :deny %>
5
+
@@ -0,0 +1,8 @@
1
+ require 'token_endpoint'
2
+ Rails.application.routes.draw do |map|
3
+ namespace 'oauth2' do
4
+ resources :authorizations, :only => :create
5
+ end
6
+ match 'oauth2/authorize', :to => 'oauth2/authorizations#new'
7
+ post 'oauth2/token', :to => proc { |env| TokenEndpoint.new.call(env) }
8
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "devise_oauth2_token_bearer_authenticatable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "devise_oauth2_token_bearer_authenticatable"
7
+ s.version = DeviseOauth2TokenBearerAuthenticatable::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{add OAuth2 authentication to rails3 application}
14
+
15
+ s.rubyforge_project = "devise_oauth2_token_bearer_authenticatable"
16
+
17
+ s.add_runtime_dependency(%q<rails>, ["~> 3.0.7"])
18
+ s.add_runtime_dependency(%q<devise>, ["~> 1.3.3"])
19
+ s.add_runtime_dependency(%q<rack-oauth2>, ["~> 0.6.3"])
20
+ s.add_development_dependency(%q<rspec>, ['>= 2.5.0'])
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,18 @@
1
+ require 'devise'
2
+ require 'rack/oauth2'
3
+ require 'devise_oauth2_token_bearer_authenticatable/strategy'
4
+ require 'devise_oauth2_token_bearer_authenticatable/model'
5
+ require 'devise_oauth2_token_bearer_authenticatable/schema'
6
+ require 'devise_oauth2_token_bearer_authenticatable/engine'
7
+
8
+ module Devise
9
+ module Oauth2TokenBearerAuthenticatable
10
+ # Your code goes here...
11
+
12
+ end
13
+ end
14
+
15
+ Devise.add_module(:oauth2_token_bearer_authenticatable,
16
+ :strategy => true,
17
+ :model => 'devise_oauth2_token_bearer_authenticatable/model')
18
+
@@ -0,0 +1,12 @@
1
+ module Devise
2
+ module Oauth2TokenBearerAuthenticatable
3
+ class Engine < Rails::Engine
4
+ initializer "devise_oauth2_token_bearer_authenticatable.initialize_application" do |app|
5
+ app.middleware.use Rack::OAuth2::Server::Resource::Bearer, 'OAuth2 Bearer Token Resources' do |req|
6
+ AccessToken.valid.find_by_token(req.access_token) || req.invalid_token!
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,13 @@
1
+ require 'devise/models'
2
+
3
+ module Devise
4
+ module Models
5
+ module Oauth2TokenBearerAuthenticatable
6
+ extend ActiveSupport::Concern
7
+ included do
8
+ has_many :access_tokens
9
+ has_many :authorization_codes
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ require 'devise/schema'
2
+
3
+ module Devise
4
+ module Oauth2TokenBearerAuthenticatable
5
+ module Schema
6
+ def self.up(migration)
7
+ migration.create_table :clients do |t|
8
+ t.string :name
9
+ t.string :redirect_uri
10
+ t.string :website
11
+ t.string :identifier
12
+ t.string :secret
13
+ t.timestamps
14
+ end
15
+ migration.add_index :clients, :identifier
16
+
17
+ migration.create_table :access_tokens do |t|
18
+ t.belongs_to :user, :client
19
+ t.string :token
20
+ t.datetime :expires_at
21
+ t.timestamps
22
+ end
23
+ migration.add_index :access_tokens, :token
24
+ migration.add_index :access_tokens, :expires_at
25
+
26
+ migration.create_table :refresh_tokens do |t|
27
+ t.belongs_to :user, :client
28
+ t.string :token
29
+ t.datetime :expires_at
30
+ t.timestamps
31
+ end
32
+ migration.add_index :refresh_tokens, :token
33
+ migration.add_index :refresh_tokens, :expires_at
34
+
35
+ migration.create_table :authorization_codes do |t|
36
+ t.belongs_to :user, :client
37
+ t.string :token
38
+ t.datetime :expires_at
39
+ t.string :redirect_uri
40
+ t.timestamps
41
+ end
42
+ migration.add_index :authorization_codes, :token
43
+ migration.add_index :authorization_codes, :expires_at
44
+ end
45
+
46
+ def self.down(migration)
47
+ migration.drop_table :refresh_tokens
48
+ migration.drop_table :access_tokens
49
+ migration.drop_table :clients
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,19 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2TokenBearerAuthenticatable < Base
6
+ def valid?
7
+ env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN].present?
8
+ end
9
+ def authenticate!
10
+ token = AccessToken.valid.find_by_token env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN]
11
+ raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized unless token
12
+ raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:invalid_token, 'User token is required') unless token.user
13
+ success! token.user
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ Warden::Strategies.add(:oauth2_token_bearer_authenticatable, Devise::Strategies::Oauth2TokenBearerAuthenticatable)
@@ -0,0 +1,3 @@
1
+ module DeviseOauth2TokenBearerAuthenticatable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,37 @@
1
+ module Oauth2Token
2
+ def self.included(klass)
3
+ klass.class_eval do
4
+ cattr_accessor :default_lifetime
5
+ self.default_lifetime = 1.minute
6
+
7
+ belongs_to :user
8
+ belongs_to :client
9
+
10
+ before_validation :setup, :on => :create
11
+ validates :client, :expires_at, :presence => true
12
+ validates :token, :presence => true, :uniqueness => true
13
+
14
+ # TODO: this should be a default scope once rails default_scope supports lambda's
15
+ scope :valid, lambda {
16
+ where(self.arel_table[:expires_at].gteq(Time.now.utc))
17
+ }
18
+ end
19
+ end
20
+
21
+ def expires_in
22
+ (expires_at - Time.now.utc).to_i
23
+ end
24
+
25
+ def expired!
26
+ self.expires_at = Time.now.utc
27
+ self.save!
28
+ end
29
+
30
+ private
31
+
32
+ def setup
33
+ self.token = ActiveSupport::SecureRandom.base64(16)
34
+ self.expires_at ||= self.default_lifetime.from_now
35
+ end
36
+ end
37
+
@@ -0,0 +1,37 @@
1
+ class TokenEndpoint
2
+
3
+ def call(env)
4
+ authenticator.call(env)
5
+ end
6
+
7
+ private
8
+
9
+ def authenticator
10
+ Rack::OAuth2::Server::Token.new do |req, res|
11
+ client = Client.find_by_identifier(req.client_id) || req.invalid_client!
12
+ client.secret == req.client_secret || req.invalid_client!
13
+ case req.grant_type
14
+ when :authorization_code
15
+ code = AuthorizationCode.valid.find_by_token(req.code)
16
+ req.invalid_grant! if code.blank? || code.redirect_uri != req.redirect_uri
17
+ res.access_token = code.access_token.to_bearer_token(:with_refresh_token)
18
+ when :password
19
+ user = User.find_by_email(req.username) || req.invalid_grant!
20
+ req.invalid_grant! unless user.valid_password?(req.password)
21
+ res.access_token = user.access_tokens.create(:client => client).to_bearer_token(:with_refresh_token)
22
+ when :client_credentials
23
+ # NOTE: client is already authenticated here.
24
+ res.access_token = client.access_tokens.create.to_bearer_token
25
+ when :refresh_token
26
+ refresh_token = client.refresh_tokens.valid.find_by_token(req.refresh_token)
27
+ req.invalid_grant! unless refresh_token
28
+ res.access_token = refresh_token.access_tokens.create.to_bearer_token
29
+ else
30
+ # NOTE: extended assertion grant_types are not supported yet.
31
+ req.unsupported_grant_type!
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Devise::Oauth2TokenBearerAuthenticatable do
4
+ it 'should be defined' do
5
+ # success
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'devise_oauth2_token_bearer_authenticatable'
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc,
7
+ # in spec/support/ and its subdirectories.
8
+ # Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
9
+
10
+ # include Devise::TestHelpers
11
+
12
+ RSpec.configure do |config|
13
+ # == Mock Framework
14
+ #
15
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
16
+ #
17
+ # config.mock_with :mocha
18
+ # config.mock_with :flexmock
19
+ # config.mock_with :rr
20
+ config.mock_with :rspec
21
+
22
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
23
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
24
+
25
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
26
+ # examples within a transaction, remove the following line or assign false
27
+ # instead of true.
28
+ # config.use_transactional_fixtures = true
29
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_oauth2_token_bearer_authenticatable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Sonnek
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-25 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rails
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 9
29
+ segments:
30
+ - 3
31
+ - 0
32
+ - 7
33
+ version: 3.0.7
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: devise
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 29
45
+ segments:
46
+ - 1
47
+ - 3
48
+ - 3
49
+ version: 1.3.3
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rack-oauth2
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 1
61
+ segments:
62
+ - 0
63
+ - 6
64
+ - 3
65
+ version: 0.6.3
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: rspec
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 27
77
+ segments:
78
+ - 2
79
+ - 5
80
+ - 0
81
+ version: 2.5.0
82
+ type: :development
83
+ version_requirements: *id004
84
+ description: add OAuth2 authentication to rails3 application
85
+ email:
86
+ - ryan@socialcast.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - .gitignore
95
+ - .rspec
96
+ - CONTRIBUTORS.txt
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - app/controllers/oauth2/authorizations_controller.rb
102
+ - app/models/access_token.rb
103
+ - app/models/authorization_code.rb
104
+ - app/models/client.rb
105
+ - app/models/refresh_token.rb
106
+ - app/views/oauth2/authorizations/_form.html.erb
107
+ - app/views/oauth2/authorizations/error.html.erb
108
+ - app/views/oauth2/authorizations/new.html.erb
109
+ - config/routes.rb
110
+ - devise_oauth2_token_bearer_authenticatable.gemspec
111
+ - lib/devise_oauth2_token_bearer_authenticatable.rb
112
+ - lib/devise_oauth2_token_bearer_authenticatable/engine.rb
113
+ - lib/devise_oauth2_token_bearer_authenticatable/model.rb
114
+ - lib/devise_oauth2_token_bearer_authenticatable/schema.rb
115
+ - lib/devise_oauth2_token_bearer_authenticatable/strategy.rb
116
+ - lib/devise_oauth2_token_bearer_authenticatable/version.rb
117
+ - lib/oauth2_token.rb
118
+ - lib/token_endpoint.rb
119
+ - spec/devise_oauth2_token_bearer_authenticatable_spec.rb
120
+ - spec/spec_helper.rb
121
+ homepage: ""
122
+ licenses: []
123
+
124
+ post_install_message:
125
+ rdoc_options: []
126
+
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ hash: 3
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ requirements: []
148
+
149
+ rubyforge_project: devise_oauth2_token_bearer_authenticatable
150
+ rubygems_version: 1.7.2
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: OAuth2 Provider for Rails3 applications
154
+ test_files:
155
+ - spec/devise_oauth2_token_bearer_authenticatable_spec.rb
156
+ - spec/spec_helper.rb