devise_oauth2_providable 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -62,7 +62,7 @@ requiring user intervention to re-authorize.
62
62
 
63
63
  expires after 1 month by default.
64
64
 
65
- ### AthorizationCode
65
+ ### AuthorizationCode
66
66
  http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-1.4.1
67
67
 
68
68
  *Very* short lived token created to allow a client to request an access
@@ -92,8 +92,10 @@ supported flows.
92
92
  http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
93
93
 
94
94
  in order to use the Resource Owner Password Credentials Grant Type, your
95
- Devise model *must* be configured to support the
96
- :database_authenticatable option
95
+ Devise model *must* be configured with the :database_authenticatable option
96
+
97
+ ### Client Credentials Grant Type
98
+ http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
97
99
 
98
100
  ### Authorization Code Grant Type
99
101
  http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
@@ -3,15 +3,13 @@ require 'expirable_token'
3
3
  class AccessToken < ActiveRecord::Base
4
4
  include ExpirableToken
5
5
  self.default_lifetime = 15.minutes
6
- attr_accessor :refresh_token
7
6
 
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
- refresh_token = client.refresh_tokens.create! :user => self.user
7
+ before_validation :restrict_expires_at, :if => :refresh_token
8
+ belongs_to :refresh_token
9
+
10
+ def to_bearer_token
11
+ bearer_token = Rack::OAuth2::AccessToken::Bearer.new :access_token => self.token, :expires_in => self.expires_in
12
+ if refresh_token
15
13
  bearer_token.refresh_token = refresh_token.token
16
14
  end
17
15
  bearer_token
@@ -19,12 +17,7 @@ class AccessToken < ActiveRecord::Base
19
17
 
20
18
  private
21
19
 
22
- def setup
23
- super
24
- if refresh_token
25
- self.user = refresh_token.user
26
- self.client = refresh_token.client
27
- self.expires_at = [self.expires_at, refresh_token.expires_at].min
28
- end
20
+ def restrict_expires_at
21
+ self.expires_at = [self.expires_at, refresh_token.expires_at].min
29
22
  end
30
23
  end
data/app/models/client.rb CHANGED
@@ -2,14 +2,17 @@ class Client < ActiveRecord::Base
2
2
  has_many :access_tokens
3
3
  has_many :refresh_tokens
4
4
 
5
- before_validation :setup, :on => :create
5
+ before_validation :init_identifier, :on => :create, :unless => :identifier?
6
+ before_validation :init_secret, :on => :create, :unless => :secret?
6
7
  validates :name, :website, :redirect_uri, :secret, :presence => true
7
8
  validates :identifier, :presence => true, :uniqueness => true
8
9
 
9
10
  private
10
11
 
11
- def setup
12
- self.identifier = ActiveSupport::SecureRandom.base64(16)
13
- self.secret = ActiveSupport::SecureRandom.base64
12
+ def init_identifier
13
+ self.identifier = Devise::Oauth2Providable.random_id
14
+ end
15
+ def init_secret
16
+ self.secret = Devise::Oauth2Providable.random_id
14
17
  end
15
18
  end
@@ -1,4 +1,4 @@
1
- <h2><%= link_to @client.name, @client.website %> is permission to access your resources.</h2>
1
+ <h2><%= link_to @client.name, @client.website %> is requesting permission to access your resources.</h2>
2
2
 
3
3
  <%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :approve %>
4
4
  <%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :deny %>
@@ -15,7 +15,7 @@ module Devise
15
15
  migration.add_index :clients, :identifier
16
16
 
17
17
  migration.create_table :access_tokens do |t|
18
- t.belongs_to :user, :client
18
+ t.belongs_to :user, :client, :refresh_token
19
19
  t.string :token
20
20
  t.datetime :expires_at
21
21
  t.timestamps
@@ -1,5 +1,5 @@
1
1
  module Devise
2
2
  module Oauth2Providable
3
- VERSION = "0.1.6"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -7,8 +7,11 @@ require 'devise_oauth2_providable/engine'
7
7
 
8
8
  module Devise
9
9
  module Oauth2Providable
10
- # Your code goes here...
11
-
10
+ class << self
11
+ def random_id
12
+ ActiveSupport::SecureRandom.hex
13
+ end
14
+ end
12
15
  end
13
16
  end
14
17
 
@@ -7,8 +7,10 @@ module ExpirableToken
7
7
  belongs_to :user
8
8
  belongs_to :client
9
9
 
10
- before_validation :setup, :on => :create
11
- validates :client, :expires_at, :presence => true
10
+ before_validation :init_token, :on => :create, :unless => :token?
11
+ before_validation :init_expires_at, :on => :create, :unless => :expires_at?
12
+ validates :expires_at, :presence => true
13
+ validates :client, :presence => true
12
14
  validates :token, :presence => true, :uniqueness => true
13
15
 
14
16
  # TODO: this should be a default scope once rails default_scope supports lambda's
@@ -29,9 +31,11 @@ module ExpirableToken
29
31
 
30
32
  private
31
33
 
32
- def setup
33
- self.token = ActiveSupport::SecureRandom.base64(16)
34
- self.expires_at ||= self.default_lifetime.from_now
34
+ def init_token
35
+ self.token = Devise::Oauth2Providable.random_id
36
+ end
37
+ def init_expires_at
38
+ self.expires_at = self.default_lifetime.from_now
35
39
  end
36
40
  end
37
41
 
@@ -1,5 +1,5 @@
1
1
  class TokenEndpoint
2
-
2
+ class InvalidGrantType < StandardError; end
3
3
  def call(env)
4
4
  authenticator.call(env)
5
5
  end
@@ -8,41 +8,43 @@ class TokenEndpoint
8
8
 
9
9
  def authenticator
10
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
-
14
- token = access_token(req, client)
15
- if token && token.save
16
- include_bearer_token = [:authorization_code, :password].include?(req.grant_type) ? :with_refresh_token : false
17
- res.access_token = token.to_bearer_token include_bearer_token
18
- else
11
+ client = Client.find_by_identifier(req.client_id)
12
+ req.invalid_client! unless client && client.secret == req.client_secret
13
+ begin
14
+ res.access_token = access_token(req, client).to_bearer_token
15
+ rescue => e
16
+ puts e.inspect
19
17
  req.invalid_grant!
20
18
  end
21
19
  end
22
20
  end
23
21
 
24
- # NOTE: extended assertion grant_types are not supported yet.
25
22
  def access_token(req, client)
23
+ refresh_token = find_refresh_token(req, client)
24
+ refresh_token.access_tokens.create!(:client => client, :user => refresh_token.user)
25
+ end
26
+
27
+ # NOTE: extended assertion grant_types are not supported yet.
28
+ # NOTE: client_credentials grant_types are not yet supported
29
+ def find_refresh_token(req, client)
26
30
  case req.grant_type
27
31
  when :authorization_code
28
32
  code = AuthorizationCode.valid.find_by_token(req.code)
29
- return nil unless code.valid_request?(req)
30
- code.access_token.build
33
+ raise InvalidGrantType.new('invalid authorization code') unless code && code.valid_request?(req)
34
+ client.refresh_tokens.create! :user => code.user
31
35
  when :password
32
36
  resource = mapping.to.find_for_authentication(mapping.to.authentication_keys.first => req.username)
33
- return nil unless resource && resource.respond_to?(:valid_password?)
37
+ raise InvalidGrantType.new('user not found') unless resource
38
+ raise InvalidGrantType.new('user does not support password authentication') unless resource.respond_to?(:valid_password?)
34
39
  valid = resource.valid_for_authentication? { resource.valid_password?(req.password) }
35
- return nil unless valid.is_a?(TrueClass)
36
- resource.access_tokens.build(:client => client)
37
- when :client_credentials
38
- # NOTE: client is already authenticated here.
39
- client.access_tokens.build
40
+ raise InvalidGrantType.new("authentication failed: #{valid}") unless valid.is_a?(TrueClass)
41
+ client.refresh_tokens.create! :user => resource
40
42
  when :refresh_token
41
43
  refresh_token = client.refresh_tokens.valid.find_by_token(req.refresh_token)
42
- return nil unless refresh_token.present?
43
- refresh_token.access_tokens.build(:client => client, :user => refresh_token.user)
44
+ raise InvalidGrantType.new('refresh token not found') unless refresh_token
45
+ refresh_token
44
46
  else
45
- nil
47
+ raise InvalidGrantType.new('invalid grant type')
46
48
  end
47
49
  end
48
50
  def mapping
@@ -15,6 +15,7 @@ ActiveRecord::Schema.define(:version => 20110511210926) do
15
15
  create_table "access_tokens", :force => true do |t|
16
16
  t.integer "user_id"
17
17
  t.integer "client_id"
18
+ t.integer "refresh_token_id"
18
19
  t.string "token"
19
20
  t.datetime "expires_at"
20
21
  t.datetime "created_at"
@@ -1,6 +1,35 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe TokenEndpoint do
4
+ describe 'refresh_token grant type' do
5
+ context 'with valid params' do
6
+ before do
7
+ @user = User.create! :email => 'ryan@socialcast.com', :name => 'ryan sonnek', :password => 'test'
8
+ @client = Client.create! :name => 'example', :redirect_uri => 'http://localhost', :website => 'http://localhost'
9
+ @refresh_token = @client.refresh_tokens.create! :user => @user
10
+ params = {
11
+ :grant_type => 'refresh_token',
12
+ :client_id => @client.identifier,
13
+ :client_secret => @client.secret,
14
+ :refresh_token => @refresh_token.token
15
+ }
16
+
17
+ post '/oauth2/token', params
18
+ end
19
+ it { response.code.to_i.should == 200 }
20
+ it 'returns json' do
21
+ token = AccessToken.last
22
+ refresh_token = @refresh_token
23
+ expected = {
24
+ :token_type => 'bearer',
25
+ :expires_in => 899,
26
+ :refresh_token => refresh_token.token,
27
+ :access_token => token.token
28
+ }
29
+ response.body.should == expected.to_json
30
+ end
31
+ end
32
+ end
4
33
  describe 'password grant type' do
5
34
  context 'with valid params' do
6
35
  before do
@@ -30,7 +59,6 @@ describe TokenEndpoint do
30
59
  response.body.should == expected.to_json
31
60
  end
32
61
  end
33
-
34
62
  context 'with invalid params' do
35
63
  before do
36
64
  @user = User.create! :email => 'ryan@socialcast.com', :name => 'ryan sonnek', :password => 'test'
@@ -12,6 +12,25 @@ describe AccessToken do
12
12
  it { should belong_to :client }
13
13
  it { should validate_presence_of :client }
14
14
  it { should validate_presence_of :expires_at }
15
+ it { should belong_to :refresh_token }
16
+ it { should allow_mass_assignment_of :refresh_token }
17
+ it { should have_db_index :client_id }
18
+ it { should have_db_index :user_id }
19
+ it { should have_db_index :token }
20
+ it { should have_db_index :expires_at }
21
+ end
22
+
23
+ describe 'refresh token expires before access token expires_at' do
24
+ before do
25
+ @soon = 1.minute.from_now
26
+ client = Client.create! :name => 'test', :redirect_uri => 'http://localhost:3000', :website => 'http://localhost'
27
+ @refresh_token = client.refresh_tokens.create!
28
+ @refresh_token.expires_at = @soon
29
+ @access_token = AccessToken.create! :client => client, :refresh_token => @refresh_token
30
+ end
31
+ it 'should set the access token expires_at to equal refresh token' do
32
+ @access_token.expires_at.should eq @soon
33
+ end
15
34
  end
16
35
  end
17
36
 
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe RefreshToken do
4
+ describe 'basic refresh token instance' do
5
+ subject do
6
+ client = Client.create! :name => 'test', :redirect_uri => 'http://localhost:3000', :website => 'http://localhost'
7
+ RefreshToken.create! :client => client
8
+ end
9
+ it { should validate_presence_of :token }
10
+ it { should validate_uniqueness_of :token }
11
+ it { should belong_to :user }
12
+ it { should belong_to :client }
13
+ it { should validate_presence_of :client }
14
+ it { should validate_presence_of :expires_at }
15
+ it { should have_many :access_tokens }
16
+ it { should have_db_index :client_id }
17
+ it { should have_db_index :user_id }
18
+ it { should have_db_index :token }
19
+ it { should have_db_index :expires_at }
20
+ end
21
+ end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 6
10
- version: 0.1.6
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Sonnek
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-12 00:00:00 Z
18
+ date: 2011-05-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rails
@@ -163,6 +163,7 @@ files:
163
163
  - spec/rails_app/spec/integration/token_endpoint_spec.rb
164
164
  - spec/rails_app/spec/models/access_token_spec.rb
165
165
  - spec/rails_app/spec/models/client_spec.rb
166
+ - spec/rails_app/spec/models/refresh_token_spec.rb
166
167
  - spec/rails_app/spec/models/user_spec.rb
167
168
  - spec/rails_app/spec/spec_helper.rb
168
169
  - spec/rails_app/vendor/plugins/.gitkeep
@@ -249,6 +250,7 @@ test_files:
249
250
  - spec/rails_app/spec/integration/token_endpoint_spec.rb
250
251
  - spec/rails_app/spec/models/access_token_spec.rb
251
252
  - spec/rails_app/spec/models/client_spec.rb
253
+ - spec/rails_app/spec/models/refresh_token_spec.rb
252
254
  - spec/rails_app/spec/models/user_spec.rb
253
255
  - spec/rails_app/spec/spec_helper.rb
254
256
  - spec/rails_app/vendor/plugins/.gitkeep