From aa2e7ce0119cd0087d4ea74da75bbcccf0787430 Mon Sep 17 00:00:00 2001 From: Fotos Georgiadis Date: Fri, 31 May 2019 01:44:11 +0200 Subject: [PATCH] Add #{before,after}_successful_authorization hooks in Doorkeeper::TokensController --- CHANGELOG.md | 1 + .../doorkeeper/tokens_controller.rb | 15 +- spec/controllers/tokens_controller_spec.rb | 154 ++++++++++++++---- 3 files changed, 139 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08df5097e..7739c2901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ User-visible changes worth mentioning. ## master - [#PR ID] Add your description here. +- [#1264] Add :before_successful_authorization and :after_successful_authorization hooks in TokensController - [#1263]: Response properly when introspection fails and fix configurations's user guide. ## 5.2.0.rc1 diff --git a/app/controllers/doorkeeper/tokens_controller.rb b/app/controllers/doorkeeper/tokens_controller.rb index 6e2c62289..612eb045f 100644 --- a/app/controllers/doorkeeper/tokens_controller.rb +++ b/app/controllers/doorkeeper/tokens_controller.rb @@ -88,7 +88,20 @@ def strategy end def authorize_response - @authorize_response ||= strategy.authorize + @authorize_response ||= begin + before_successful_authorization + auth = strategy.authorize + after_successful_authorization unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse) + auth + end + end + + def after_successful_authorization + Doorkeeper.configuration.after_successful_authorization.call(self) + end + + def before_successful_authorization + Doorkeeper.configuration.before_successful_authorization.call(self) end def revocation_error_response diff --git a/spec/controllers/tokens_controller_spec.rb b/spec/controllers/tokens_controller_spec.rb index 716f58d8a..a550aadb6 100644 --- a/spec/controllers/tokens_controller_spec.rb +++ b/spec/controllers/tokens_controller_spec.rb @@ -3,31 +3,139 @@ require "spec_helper" describe Doorkeeper::TokensController do - describe "when authorization has succeeded" do - let(:token) { double(:token, authorize: true) } + let(:client) { FactoryBot.create :application } + let!(:user) { User.create!(name: "Joe", password: "sekret") } - it "returns the authorization" do - skip "verify need of these specs" + before do + Doorkeeper.configure do + resource_owner_from_credentials do + User.first + end + end - expect(token).to receive(:authorization) + allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["password"]) + end - post :create + subject { JSON.parse(response.body) } + + describe "POST #create" do + before do + post :create, params: { + client_id: client.uid, + client_secret: client.secret, + grant_type: "password", + } + end + + it "responds after authorization" do + expect(response).to be_successful + end + + it "includes access token in response" do + expect(subject["access_token"]).to eq(Doorkeeper::AccessToken.first.token) + end + + it "includes token type in response" do + expect(subject["token_type"]).to eq("Bearer") + end + + it "includes token expiration in response" do + expect(subject["expires_in"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in) + end + + it "issues the token for the current client" do + expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id) + end + + it "issues the token for the current resource owner" do + expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id) end end - describe "when authorization has failed" do - it "returns the error response" do - token = double(:token, authorize: false) - allow(controller).to receive(:token) { token } + describe "POST #create with errors" do + before do + post :create, params: { + client_id: client.uid, + client_secret: "invalid", + grant_type: "password", + } + end - post :create + it "responds after authorization" do + expect(response).to be_unauthorized + end - expect(response.status).to eq 400 - expect(response.headers["WWW-Authenticate"]).to match(/Bearer/) + it "include error in response" do + expect(subject["error"]).to eq("invalid_client") + end + + it "include error_description in response" do + expect(subject["error_description"]).to be + end + + it "does not include access token in response" do + expect(subject["access_token"]).to be_nil + end + + it "does not include token type in response" do + expect(subject["token_type"]).to be_nil + end + + it "does not include token expiration in response" do + expect(subject["expires_in"]).to be_nil + end + + it "does not issue any access token" do + expect(Doorkeeper::AccessToken.all).to be_empty + end + end + + describe "POST #create with callbacks" do + after do + client.update_attribute :redirect_uri, "urn:ietf:wg:oauth:2.0:oob" + end + + describe "when successful" do + after do + post :create, params: { + client_id: client.uid, + client_secret: client.secret, + grant_type: "password", + } + end + + it "should call :before_successful_authorization callback" do + expect(Doorkeeper.configuration) + .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class)) + end + + it "should call :after_successful_authorization callback" do + expect(Doorkeeper.configuration) + .to receive_message_chain(:after_successful_authorization, :call).with(instance_of(described_class)) + end + end + + describe "with errors" do + after do + post :create, params: { + client_id: client.uid, + client_secret: "invalid", + grant_type: "password", + } + end + + it "should call :before_successful_authorization callback" do + expect(Doorkeeper.configuration) + .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class)) + end + + it "should not call :after_successful_authorization callback" do + expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization) + end end end - describe "when there is a failure due to a custom error" do + describe "POST #create with custom error" do it "returns the error response with a custom message" do # I18n looks for `doorkeeper.errors.messages.custom_message` in locale files custom_message = "my_message" @@ -58,7 +166,7 @@ end # http://tools.ietf.org/html/rfc7009#section-2.2 - describe "revoking tokens" do + describe "POST #revoke" do let(:client) { FactoryBot.create(:application) } let(:access_token) { FactoryBot.create(:access_token, application: client) } @@ -117,21 +225,7 @@ end end - describe "authorize response memoization" do - it "memoizes the result of the authorization" do - strategy = double(:strategy, authorize: true) - expect(strategy).to receive(:authorize).once - allow(controller).to receive(:strategy) { strategy } - allow(controller).to receive(:create) do - 2.times { controller.send :authorize_response } - controller.render json: {}, status: :ok - end - - post :create - end - end - - describe "when requested token introspection" do + describe "POST #introspect" do let(:client) { FactoryBot.create(:application) } let(:access_token) { FactoryBot.create(:access_token, application: client) } let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }