Commit 443ecda

mo <mo.khan@gmail.com>
2018-09-17 01:54:37
start to implement saml 2.0 assertion grant.
1 parent 2a18538
Changed files (2)
app
spec
app/controllers/oauths_controller.rb
@@ -21,24 +21,34 @@ class OauthsController < ApplicationController
   def token
     response.headers['Cache-Control'] = 'no-store'
     response.headers['Pragma'] = 'no-cache'
-    if token_params[:grant_type] == 'authorization_code'
-      authorization = Authorization.active.find_by!(code: token_params[:code])
+
+    if params[:grant_type] == 'authorization_code'
+      authorization = Authorization.active.find_by!(code: params[:code])
       @access_token, @refresh_token = authorization.exchange
-    elsif token_params[:grant_type] == 'refresh_token'
-      refresh_token = token_params[:refresh_token]
+    elsif params[:grant_type] == 'refresh_token'
+      refresh_token = params[:refresh_token]
       jti = Token.claims_for(refresh_token, token_type: :refresh)[:jti]
       @access_token, @refresh_token = Token.find_by!(uuid: jti).exchange
-    elsif token_params[:grant_type] == 'client_credentials'
+    elsif params[:grant_type] == 'client_credentials'
       @access_token = current_client.exchange
-    elsif token_params[:grant_type] == 'password'
+    elsif params[:grant_type] == 'password'
       user = User.login(params[:username], params[:password])
       return render "bad_request", formats: :json, status: :bad_request unless user
       @access_token, @refresh_token = user.issue_tokens_to(current_client)
+    elsif params[:grant_type] == 'urn:ietf:params:oauth:grant-type:saml2-bearer'
+      xml = Nokogiri::XML(Base64.urlsafe_decode64(params[:assertion]))
+      assertion = Saml::Kit::Assertion.new(xml)
+      if assertion.valid?
+        @access_token, @refresh_token = User.find_by!(uuid: assertion.name_id).issue_tokens_to(current_client)
+      else
+        return render "bad_request", formats: :json, status: :bad_request
+      end
     else
       return render "bad_request", formats: :json, status: :bad_request
     end
     render formats: :json
   rescue StandardError => error
+    puts error.inspect
     Rails.logger.error(error)
     render "bad_request", formats: :json, status: :bad_request
   end
@@ -47,10 +57,6 @@ class OauthsController < ApplicationController
 
   attr_reader :current_client
 
-  def token_params
-    params.permit(:grant_type, :code, :refresh_token)
-  end
-
   def http_basic_authenticate!
     @current_client = authenticate_with_http_basic do |client_id, client_secret|
       Client.find_by(uuid: client_id)&.authenticate(client_secret)
spec/requests/oauth_spec.rb
@@ -174,5 +174,31 @@ RSpec.describe '/oauth' do
         specify { expect(refresh_token.reload).to be_revoked }
       end
     end
+
+    context "when exchanging a SAML 2.0 assertion grant for tokens" do
+      context "when the assertion is valid" do
+        let(:user) { instance_double(User, name_id_for: SecureRandom.uuid, assertion_attributes_for: {}) }
+        let(:saml_request) { double(id: SecureRandom.uuid, issuer: Saml::Kit.configuration.entity_id) }
+
+        before :each do
+          saml = Saml::Kit::Response.build(user, saml_request)
+          post '/oauth/token', params: {
+            grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
+            assertion: Base64.urlsafe_encode64(saml.assertion.to_xml),
+          }, headers: headers
+        end
+
+        specify { expect(response).to have_http_status(:ok) }
+        specify { expect(response.headers['Content-Type']).to include('application/json') }
+        specify { expect(response.headers['Cache-Control']).to include('no-store') }
+        specify { expect(response.headers['Pragma']).to eql('no-cache') }
+
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+        specify { expect(json[:access_token]).to be_present }
+        specify { expect(json[:token_type]).to eql('Bearer') }
+        specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
+        specify { expect(json[:refresh_token]).to be_present }
+      end
+    end
   end
 end