Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add assertion strategy #105

Merged
merged 1 commit into from May 12, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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