From 8345f4ac5d97bcfa7c0db2cabfa2d7082ee91576 Mon Sep 17 00:00:00 2001 From: Martin Artnik Date: Tue, 24 Jan 2023 15:57:59 +0100 Subject: [PATCH] Throw error when authenticating with revoked tokens [SCI-7631] --- .../concerns/token_authentication.rb | 8 +++++ .../doorkeeper/access_tokens_controller.rb | 17 +++++++++ config/routes.rb | 2 ++ .../concerns/token_authentication_spec.rb | 36 +++++++++++++++++++ .../access_tokens_controller_spec.rb | 28 +++++++++++++++ 5 files changed, 91 insertions(+) create mode 100644 app/controllers/doorkeeper/access_tokens_controller.rb create mode 100644 spec/controllers/concerns/token_authentication_spec.rb create mode 100644 spec/controllers/doorkeeper/access_tokens_controller_spec.rb diff --git a/app/controllers/concerns/token_authentication.rb b/app/controllers/concerns/token_authentication.rb index a6ed6d2cd..a856007ee 100644 --- a/app/controllers/concerns/token_authentication.rb +++ b/app/controllers/concerns/token_authentication.rb @@ -17,6 +17,8 @@ module TokenAuthentication @token = request.headers['Authorization']&.sub('Bearer ', '') raise JWT::VerificationError, I18n.t('api.core.missing_token') unless @token + verify_token! + @token_iss = Api::CoreJwt.read_iss(@token) raise JWT::InvalidPayload, I18n.t('api.core.no_iss') unless @token_iss @@ -34,4 +36,10 @@ module TokenAuthentication @current_user = User.find_by(id: payload['sub']) raise JWT::InvalidPayload, I18n.t('api.core.no_user_mapping') unless current_user end + + def verify_token! + if Doorkeeper::AccessToken.where.not(revoked_at: nil).exists?(token: @token) + raise JWT::VerificationError, I18n.t('api.core.expired_token') + end + end end diff --git a/app/controllers/doorkeeper/access_tokens_controller.rb b/app/controllers/doorkeeper/access_tokens_controller.rb new file mode 100644 index 000000000..809f16907 --- /dev/null +++ b/app/controllers/doorkeeper/access_tokens_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Doorkeeper + class AccessTokensController < ApplicationController + before_action :find_token + + def revoke + @token.revoke + end + + private + + def find_token + @token = current_user.access_tokens.find(params[:id]) + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 284289e8c..0229a48e2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,8 @@ Rails.application.routes.draw do skip_controllers :applications, :authorized_applications, :token_info end + post 'access_tokens/revoke', to: 'doorkeeper/access_tokens#revoke' + # Addons def draw(routes_name) diff --git a/spec/controllers/concerns/token_authentication_spec.rb b/spec/controllers/concerns/token_authentication_spec.rb new file mode 100644 index 000000000..427c1bd87 --- /dev/null +++ b/spec/controllers/concerns/token_authentication_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +class TokenAuthenticatedController + attr_accessor :token, :current_user + + include TokenAuthentication + + def request + OpenStruct.new( + headers: { 'Authorization' => "Bearer #{@token}" } + ) + end +end + +describe TokenAuthentication do + let(:test_controller_instance) { TokenAuthenticatedController.new } + + let(:user) { create :user } + let(:access_token) { + user.access_tokens.create( + expires_in: 7500 + ) + } + + describe '#authenticate_request' do + it "rejects revoked token" do + test_controller_instance.token = access_token.token + test_controller_instance.current_user = user + + access_token.revoke + expect { test_controller_instance.send(:authenticate_request!) }.to raise_error(JWT::VerificationError) + end + end +end diff --git a/spec/controllers/doorkeeper/access_tokens_controller_spec.rb b/spec/controllers/doorkeeper/access_tokens_controller_spec.rb new file mode 100644 index 000000000..8de196e84 --- /dev/null +++ b/spec/controllers/doorkeeper/access_tokens_controller_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Doorkeeper::AccessTokensController, type: :controller do + login_user + + let!(:access_token) do + subject.current_user.access_tokens.create(expires_in: 7500) + end + + describe 'POST revoke' do + let(:params) do + { + id: access_token.id + } + end + + let(:action) do + put :revoke, params: params + end + + it 'revokes the access token' do + action + expect(access_token.reload.revoked_at).to_not be_nil + end + end +end