Skip to content

Commit

Permalink
Merge pull request #1 from tsov/assertion
Browse files Browse the repository at this point in the history
Added Assertion Flow
  • Loading branch information
tsov committed Jun 25, 2013
2 parents be38a35 + b3f3993 commit 6865c34
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 6 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ Side note: when using devise you have access to current_user as devise extends e

If you are not using devise, you may want to check other ways of authentication [here](https://github.com/applicake/doorkeeper/wiki/Authenticating-using-Clearance-DIY).

### Authenticating with Assertion

You may want to configure Doorkeeper to handle authentication via assertion. This will let you define your own way of authenticating
resource owners via 3rd Party applications (e.g. Facebook).

```ruby
Doorkeeper.configure do
resource_owner_from_assertion do
facebook = URI.parse('https://graph.facebook.com/me?access_token=' + params[:assertion])
response = Net::HTTP.get_response(facebook)
user_data = JSON.parse(response.body)
User.find_by_facebook_id(user_data['id'])
end
end
```

## Protecting resources with OAuth (a.k.a your API endpoint)

To protect your API with OAuth, doorkeeper only requires you to call `doorkeeper_for` helper, specifying the actions you want to protect.
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ en:

#configuration error messages
credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
assertion_flow_not_configured: 'Resource Owner Assertion flow failed due to Doorkeeper.configure.resource_owner_from_assertion being unconfigured.'
resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'

# Access grant errors
Expand Down
5 changes: 5 additions & 0 deletions lib/doorkeeper/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ def extended(base)
warn(I18n.translate('doorkeeper.errors.messages.credential_flow_not_configured'))
nil
}
option :resource_owner_from_assertion,
:default => lambda{|routes|
warn(I18n.translate('doorkeeper.errors.messages.assertion_flow_not_configured'))
nil
}
option :skip_authorization, :default => lambda{|routes|}
option :access_token_expires_in, :default => 7200
option :authorization_code_expires_in,:default => 600
Expand Down
5 changes: 5 additions & 0 deletions lib/doorkeeper/helpers/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def self.included(base)
:authenticate_admin!,
:current_resource_owner,
:resource_owner_from_credentials,
:resource_owner_from_assertion,
:skip_authorization?
end

Expand All @@ -22,6 +23,10 @@ def resource_owner_from_credentials
instance_eval &Doorkeeper.configuration.resource_owner_from_credentials
end

def resource_owner_from_assertion
instance_eval &Doorkeeper.configuration.resource_owner_from_assertion
end

def authenticate_admin!
instance_eval &Doorkeeper.configuration.authenticate_admin
end
Expand Down
1 change: 1 addition & 0 deletions lib/doorkeeper/request.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'doorkeeper/request/authorization_code'
require 'doorkeeper/request/client_credentials'
require 'doorkeeper/request/assertion'
require 'doorkeeper/request/code'
require 'doorkeeper/request/password'
require 'doorkeeper/request/refresh_token'
Expand Down
25 changes: 25 additions & 0 deletions lib/doorkeeper/request/assertion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Doorkeeper
module Request
class Assertion
def self.build(server)
new(server.client, server.resource_owner_from_assertion, server)
end

attr_accessor :client, :resource_owner, :server

def initialize(client, resource_owner, server)
@client, @resource_owner, @server = client, resource_owner, server
end

def request
# TODO: For now OAuth::PasswordAccessTokenRequest is reused for the Assertion Flow. In need of
# OAuth::AssertionAccessTokenRequest in future
@request ||= OAuth::PasswordAccessTokenRequest.new(Doorkeeper.configuration, client, resource_owner, server.parameters)
end

def authorize
request.authorize
end
end
end
end
4 changes: 4 additions & 0 deletions lib/doorkeeper/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def resource_owner
context.send :resource_owner_from_credentials
end

def resource_owner_from_assertion
context.send :resource_owner_from_assertion
end

def credentials
methods = Doorkeeper.configuration.client_credentials_methods
@credentials ||= OAuth::Client::Credentials.from_request(context.request, *methods)
Expand Down
6 changes: 4 additions & 2 deletions spec/dummy/app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ class User

field :name, :type => String
field :password, :type => String
field :assertion, :type => String
end
when :mongo_mapper
class User
include MongoMapper::Document
timestamps!

key :name, String
key :password, String
key :name, String
key :password, String
key :assertion, String
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddAssertionToUsers < ActiveRecord::Migration
def change
add_column :users, :assertion, :string
end
end
7 changes: 4 additions & 3 deletions spec/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20120524202412) do
ActiveRecord::Schema.define(:version => 20130624151512) do

create_table "oauth_access_grants", :force => true do |t|
t.integer "resource_owner_id", :null => false
Expand Down Expand Up @@ -46,8 +46,8 @@
t.string "uid", :null => false
t.string "secret", :null => false
t.string "redirect_uri", :null => false
t.string "owner_type", :null => true, :default => "User"
t.integer "owner_id", :null => true
t.integer "owner_id"
t.string "owner_type"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Expand All @@ -59,6 +59,7 @@
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "password"
t.string "assertion"
end

end
74 changes: 74 additions & 0 deletions spec/requests/flows/assertion_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# coding: utf-8

require 'spec_helper_integration'

feature 'Resource Owner Assertion Flow inproperly set up' do
background do
client_exists
create_resource_owner
end

context 'with valid user assertion' do
scenario "should not issue new token" do
expect {
post assertion_endpoint_url(:client => @client, :resource_owner => @resource_owner)
}.to_not change { Doorkeeper::AccessToken.count }
end
end
end

feature 'Resource Owner Assertion Flow' do
background do
config_is_set(:resource_owner_from_assertion) { User.where(:assertion => params[:assertion]).first }
client_exists
create_resource_owner
end

context 'with valid user assertion' do
scenario "should issue new token" do
expect {
post assertion_endpoint_url(:client => @client, :resource_owner => @resource_owner)
}.to change { Doorkeeper::AccessToken.count }.by(1)

token = Doorkeeper::AccessToken.first

should_have_json 'access_token', token.token
end

scenario "should issue a refresh token if enabled" do
config_is_set(:refresh_token_enabled, true)

post assertion_endpoint_url(:client => @client, :resource_owner => @resource_owner)

token = Doorkeeper::AccessToken.first

should_have_json 'refresh_token', token.refresh_token
end

scenario 'should return the same token if it is still accessible' do
client_is_authorized(@client, @resource_owner)

post assertion_endpoint_url(:client => @client, :resource_owner => @resource_owner)

Doorkeeper::AccessToken.count.should be(1)

should_have_json 'access_token', Doorkeeper::AccessToken.first.token
end

end

context "with invalid user assertion" do
scenario "should not issue new token with bad assertion" do
expect {
post assertion_endpoint_url( :client => @client, :assertion => 'i_dont_exist' )
}.to_not change { Doorkeeper::AccessToken.count }
end

scenario "should not issue new token without assertion" do
expect {
post assertion_endpoint_url( :client => @client )
}.to_not change { Doorkeeper::AccessToken.count }
end

end
end
2 changes: 1 addition & 1 deletion spec/support/helpers/model_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ def client_exists(client_attributes = {})
end

def create_resource_owner
@resource_owner = User.create!(:name => "Joe", :password => "sekret")
@resource_owner = User.create!(:name => "Joe", :password => "sekret", :assertion => "assertion")
end

def authorization_code_exists(options = {})
Expand Down
12 changes: 12 additions & 0 deletions spec/support/helpers/url_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ def refresh_token_endpoint_url(options = {})
"/oauth/token?#{build_query(parameters)}"
end

def assertion_endpoint_url(options = {})
parameters = {
:code => options[:code],
:client_id => options[:client_id] || options[:client].uid,
:client_secret => options[:client_secret] || options[:client].secret,
:redirect_uri => options[:redirect_uri] || options[:client].redirect_uri,
:grant_type => options[:grant_type] || "assertion",
:assertion => options[:assertion] || (options[:resource_owner] ? options[:resource_owner].assertion : nil)
}
"/oauth/token?#{build_query(parameters)}"
end

def build_query(hash)
Rack::Utils.build_query(hash)
end
Expand Down

0 comments on commit 6865c34

Please sign in to comment.