devise_oauth2_providable 0.1.6 → 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.
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