Skip to content

Commit

Permalink
add assertion strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
dorren committed May 12, 2012
1 parent 08e7b4c commit 560fd78
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 2 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -61,9 +61,9 @@ instance will be returned as usual and on 400+ status code responses, the
Response instance will contain the OAuth2::Error instance.

## Authorization Grants
Currently the Authorization Code, Resource Owner Password Credentials, and Client Credentials
Currently the Authorization Code, Resource Owner Password Credentials, Client Credentials, and Assertion
authentication grant types have helper strategy classes that simplify client
use. They are available via the #auth_code, #password, and #client_credentials methods respectively.
use. They are available via the #auth_code, #password, #client_credentials, and #assertion methods respectively.

auth_url = client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback')
token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback')
Expand All @@ -72,6 +72,8 @@ use. They are available via the #auth_code, #password, and #client_credentials

token = client.client_credentials.get_token

token = client.assertion.get_token(assertion_params)

If you want to specify additional headers to be sent out with the
request, add a 'headers' hash under 'params':

Expand Down
1 change: 1 addition & 0 deletions lib/oauth2.rb
Expand Up @@ -4,5 +4,6 @@
require 'oauth2/strategy/auth_code'
require 'oauth2/strategy/password'
require 'oauth2/strategy/client_credentials'
require 'oauth2/strategy/assertion'
require 'oauth2/access_token'
require 'oauth2/response'
4 changes: 4 additions & 0 deletions lib/oauth2/client.rb
Expand Up @@ -153,5 +153,9 @@ def password
def client_credentials
@client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
end

def assertion
@assertion ||= OAuth2::Strategy::Assertion.new(self)
end
end
end
75 changes: 75 additions & 0 deletions lib/oauth2/strategy/assertion.rb
@@ -0,0 +1,75 @@
require 'httpauth'
require 'jwt'

module OAuth2
module Strategy
# The Client Assertion Strategy
#
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
#
# Sample usage:
# client = OAuth2::Client.new(client_id, client_secret,
# :site => 'http://localhost:8080')
#
# params = {:hmac_secret => "some secret",
# # or :private_key => "private key string",
# :iss => "http://localhost:3001",
# :prn => "me@here.com",
# :exp => Time.now.utc.to_i + 3600}
#
# access = client.assertion.get_token(params)
# access.token # actual access_token string
# access.get("/api/stuff") # making api calls with access token in header
#
class Assertion < Base
# Not used for this strategy
#
# @raise [NotImplementedError]
def authorize_url
raise NotImplementedError, "The authorization endpoint is not used in this strategy"
end

# Retrieve an access token given the specified client.
#
# @param [Hash] params assertion params
# pass either :hmac_secret or :private_key, but not both.
#
# params :hmac_secret, secret string.
# params :private_key, private key string.
#
# params :iss, issuer
# params :aud, audience, optional
# params :prn, principal, current user
# params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600
#
# @param [Hash] opts options
def get_token(params={}, opts={})
hash = build_request(params)
@client.get_token(hash, opts.merge('refresh_token' => nil))
end

def build_request(params)
assertion = build_assertion(params)
{:grant_type => "assertion",
:assertion_type => "urn:ietf:params:oauth:grant-type:jwt-bearer",
:assertion => assertion,
:scope => params[:scope]
}.merge(client_params)
end

def build_assertion(params)
claims = {:iss => params[:iss],
:aud => params[:aud],
:prn => params[:prn],
:exp => params[:exp]
}
if params[:hmac_secret]
jwt_assertion = JWT.encode(claims, params[:hmac_secret], "HS256")
elsif params[:private_key]
jwt_assertion = JWT.encode(claims, params[:private_key], "RS256")
end
end
end
end
end

1 change: 1 addition & 0 deletions oauth2.gemspec
Expand Up @@ -6,6 +6,7 @@ Gem::Specification.new do |gem|
gem.add_dependency 'httpauth', '~> 0.1'
gem.add_dependency 'multi_json', '~> 1.0'
gem.add_dependency 'rack', '~> 1.4'
gem.add_dependency 'jwt', '~> 0.1.4'
gem.add_development_dependency 'addressable'
gem.add_development_dependency 'multi_xml'
gem.add_development_dependency 'rake'
Expand Down
57 changes: 57 additions & 0 deletions spec/oauth2/strategy/assertion_spec.rb
@@ -0,0 +1,57 @@
require 'helper'

describe OAuth2::Strategy::Assertion do
let(:client) do
cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
cli.connection.build do |b|
b.adapter :test do |stub|
stub.post('/oauth/token') do |env|
case @mode
when "formencoded"
[200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
when "json"
[200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
end
end
end
end
cli
end

let(:params) { {:hmac_secret => 'foo'}}

subject {client.assertion}

describe "#authorize_url" do
it "should raise NotImplementedError" do
lambda {subject.authorize_url}.should raise_error(NotImplementedError)
end
end

%w(json formencoded).each do |mode|
describe "#get_token (#{mode})" do
before do
@mode = mode
@access = subject.get_token(params)
end

it 'returns AccessToken with same Client' do
@access.client.should == client
end

it 'returns AccessToken with #token' do
@access.token.should == 'salmon'
end

it 'returns AccessToken with #expires_in' do
@access.expires_in.should == 600
end

it 'returns AccessToken with #expires_at' do
@access.expires_at.should_not be_nil
end
end
end

end

0 comments on commit 560fd78

Please sign in to comment.