Skip to content

Commit

Permalink
Merge pull request #86 from nov/feature/client_assertion_auth_on_serv…
Browse files Browse the repository at this point in the history
…er_side

support client assertion auth method
  • Loading branch information
nov authored Aug 6, 2021
2 parents e18495a + ffeba9d commit fe138af
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/rack/oauth2/server/abstract/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def initialize(error = :bad_request, description = nil, options = {})

class Unauthorized < Error
def initialize(error = :unauthorized, description = nil, options = {})
@skip_www_authenticate = options[:skip_www_authenticate]
super 401, error, description, options
end
end
Expand Down
11 changes: 10 additions & 1 deletion lib/rack/oauth2/server/token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def extensions

class Request < Abstract::Request
attr_required :grant_type
attr_optional :client_secret
attr_optional :client_secret, :client_assertion, :client_assertion_type

def initialize(env)
auth = Rack::Auth::Basic::Request.new(env)
Expand All @@ -56,6 +56,15 @@ def initialize(env)
else
super
@client_secret = params['client_secret']
@client_assertion = params['client_assertion']
@client_assertion_type = params['client_assertion_type']
if client_assertion.present? && client_assertion_type == URN::ClientAssertionType::JWT_BEARER
require 'json/jwt'
@client_id = JSON::JWT.decode(
client_assertion,
:skip_verification
)[:sub] rescue nil
end
end
@grant_type = params['grant_type'].to_s
end
Expand Down
4 changes: 3 additions & 1 deletion lib/rack/oauth2/server/token/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class BadRequest < Abstract::BadRequest
class Unauthorized < Abstract::Unauthorized
def finish
super do |response|
response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
unless @skip_www_authenticate
response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
end
end
end
end
Expand Down
69 changes: 69 additions & 0 deletions spec/rack/oauth2/server/token_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,60 @@
end
end

context 'when client_id is given via JWT client assertion' do
before do
require 'json/jwt'
params[:client_assertion] = JSON::JWT.new(
sub: params[:client_id]
# NOTE: actual client_assertion should have more claims.
).sign('client_secret').to_s
params[:client_assertion_type] = Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER
params.delete(:client_id)
end

context 'when client_assertion is invalid JWT' do
before do
params[:client_assertion] = 'invalid-jwt'
end
its(:status) { should == 400 }
its(:content_type) { should == 'application/json' }
its(:body) { should include '"error":"invalid_request"' }
end

context 'when client_assertion_type is missing' do
before do
params.delete(:client_assertion_type)
end
its(:status) { should == 400 }
its(:content_type) { should == 'application/json' }
its(:body) { should include '"error":"invalid_request"' }
end

context 'when client_assertion_type is unknown' do
before do
params[:client_assertion_type] = 'unknown'
end
its(:status) { should == 400 }
its(:content_type) { should == 'application/json' }
its(:body) { should include '"error":"invalid_request"' }
end

context 'when client_assertion issuer is different from client_id' do
before do
params[:client_id] = 'another_client_id'
end
its(:status) { should == 400 }
its(:content_type) { should == 'application/json' }
its(:body) { should include '"error":"invalid_request"' }
end

context 'otherwise' do
its(:status) { should == 200 }
its(:content_type) { should == 'application/json' }
its(:body) { should include '"access_token":"access_token"' }
end
end

Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION.each do |error, default_message|
status = if error == :invalid_client
401
Expand All @@ -87,7 +141,22 @@
its(:content_type) { should == 'application/json' }
its(:body) { should include "\"error\":\"#{error}\"" }
its(:body) { should include "\"error_description\":\"#{default_message}\"" }
if error == :invalid_client
its(:headers) { should include 'WWW-Authenticate' }
end
end
end

context 'when skip_www_authenticate option is specified on invalid_client' do
let(:app) do
Rack::OAuth2::Server::Token.new do |request, response|
request.invalid_client!(
Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION[:invalid_client],
skip_www_authenticate: true
)
end
end
its(:headers) { should_not include 'WWW-Authenticate' }
end

context 'when responding' do
Expand Down

0 comments on commit fe138af

Please sign in to comment.