Commit 1ebe73f

mo <mo@mokhan.ca>
2018-10-27 16:59:27
RFC-7009 - check audience of token before revoking
The authorization server first validates the client credentials (in case of a confidential client) and then verifies whether the token was issued to the client making the revocation request. If this validation fails, the request is refused and the client is informed of the error by the authorization server as described below.
1 parent ebc2c25
Changed files (4)
app
spec
requests
app/controllers/oauth/tokens_controller.rb
@@ -26,7 +26,7 @@ module Oauth
 
     def revoke
       claims = Token.claims_for(params[:token], token_type: :any)
-      Token.find(claims[:jti]).revoke! unless claims.empty?
+      current_client.revoke(Token.find(claims[:jti])) unless claims.empty?
       render plain: "", status: :ok
     rescue StandardError => error
       logger.error(error)
app/models/client.rb
@@ -44,6 +44,10 @@ class Client < ApplicationRecord
     end
   end
 
+  def revoke(token)
+    token.revoke! if token.issued_to?(self)
+  end
+
   def valid_redirect_uri?(redirect_uri)
     redirect_uris.include? redirect_uri
   end
app/models/token.rb
@@ -17,6 +17,10 @@ class Token < ApplicationRecord
     end
   end
 
+  def issued_to?(audience)
+    self.audience == audience
+  end
+
   def revoke!
     update!(revoked_at: Time.now)
   end
spec/requests/oauth/tokens_spec.rb
@@ -362,7 +362,7 @@ RSpec.describe '/oauth/tokens' do
   describe "POST /oauth/tokens/revoke" do
     context "when the client credentials are valid" do
       context "when the access token is active and known" do
-        let(:token) { create(:access_token) }
+        let(:token) { create(:access_token, audience: client) }
 
         before { post '/oauth/tokens/revoke', params: { token: token.to_jwt, token_type_hint: :access_token }, headers: headers }
 
@@ -371,8 +371,18 @@ RSpec.describe '/oauth/tokens' do
         specify { expect(token.reload).to be_revoked }
       end
 
+      context "when the token was not issued to this client" do
+        let(:token) { create(:access_token, audience: other_client) }
+        let(:other_client) { create(:client) }
+
+        before { post '/oauth/tokens/revoke', params: { token: token.to_jwt, token_type_hint: :access_token }, headers: headers }
+
+        specify { expect(response).to have_http_status(:ok) }
+        specify { expect(token.reload).not_to be_revoked }
+      end
+
       context "when the refresh token is active and known" do
-        let(:token) { create(:refresh_token) }
+        let(:token) { create(:refresh_token, audience: client) }
 
         before { post '/oauth/tokens/revoke', params: { token: token.to_jwt, token_type_hint: :refresh_token }, headers: headers }
 
@@ -382,7 +392,7 @@ RSpec.describe '/oauth/tokens' do
       end
 
       context "when the access token is expired" do
-        let(:token) { create(:access_token, :expired) }
+        let(:token) { create(:access_token, :expired, audience: client) }
 
         before { post '/oauth/tokens/revoke', params: { token: token.to_jwt, token_type_hint: :refresh_token }, headers: headers }