Commit 3b1b50a

mo <mo@mokhan.ca>
2018-10-28 17:50:55
create the user info endpoint.
1 parent eb3794d
Changed files (5)
app
config
spec
requests
app/controllers/oauth/mes_controller.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Oauth
+  class MesController < ApplicationController
+    def show
+      render json: @claims
+    end
+
+    private
+
+    def authenticate!
+      @claims = authenticate_with_http_token do |token, _options|
+        claims = Token.claims_for(token)
+        Token.revoked?(claims[:jti]) ? nil : claims
+      end
+      request_http_token_authentication if @claims.nil? || @claims.empty?
+    end
+  end
+end
app/controllers/oauth/tokens_controller.rb
@@ -100,9 +100,7 @@ module Oauth
     end
 
     def revoked_tokens
-      Rails.cache.fetch("revoked-tokens", expires_in: 10.minutes) do
-        Hash[Token.revoked.pluck(:id).map { |x| [x, true] }]
-      end
+      Token.revoked_token_identifiers
     end
   end
 end
app/models/token.rb
@@ -57,6 +57,16 @@ class Token < ApplicationRecord
   end
 
   class << self
+    def revoked?(jti)
+      revoked_token_identifiers[jti]
+    end
+
+    def revoked_token_identifiers
+      Rails.cache.fetch("revoked-tokens", expires_in: 10.minutes) do
+        Hash[Token.revoked.pluck(:id).map { |x| [x, true] }]
+      end
+    end
+
     def claims_for(token, token_type: :access)
       if token_type == :any
         claims = claims_for(token, token_type: :access)
config/routes.rb
@@ -17,6 +17,7 @@ Rails.application.routes.draw do
   end
   namespace :oauth do
     resource :authorizations, only: [:show, :create]
+    resource :me, only: [:show]
     resources :clients, only: [:create]
     resource :tokens, only: [:create] do
       post :introspect
spec/requests/oauth/users_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe '/oauth/me' do
+  describe "GET /oauth/me" do
+    context "when the access_token is valid" do
+      let(:token) { create(:access_token) }
+      let(:headers) { { 'Authorization' => "Bearer #{token.to_jwt}" } }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
+      before { get '/oauth/me', headers: headers }
+
+      specify { expect(response).to have_http_status(:ok) }
+      specify { expect(response['Content-Type']).to include('application/json') }
+      specify { expect(json[:sub]).to eql(token.claims[:sub]) }
+      specify { expect(json[:aud]).to eql(token.claims[:aud]) }
+      specify { expect(json[:iss]).to eql(token.claims[:iss]) }
+      specify { expect(json[:exp]).to eql(token.claims[:exp]) }
+      specify { expect(json[:iat]).to eql(token.claims[:iat]) }
+    end
+
+    context "when the token is revoked" do
+      let(:headers) { { 'Authorization' => "Bearer #{token.to_jwt}" } }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+      let(:token) { create(:access_token, :revoked) }
+
+      before { get '/oauth/me', headers: headers }
+
+      specify { expect(response).to have_http_status(:unauthorized) }
+    end
+
+    context "when the token is expired" do
+      let(:headers) { { 'Authorization' => "Bearer #{token.to_jwt}" } }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+      let(:token) { create(:access_token, :expired) }
+
+      before { get '/oauth/me', headers: headers }
+
+      specify { expect(response).to have_http_status(:unauthorized) }
+    end
+  end
+end