From c344ea4e3a44ebdf43fd82ee0f9895fe3365cd69 Mon Sep 17 00:00:00 2001 From: Matthew Trew Date: Tue, 24 Feb 2026 10:34:43 +0000 Subject: [PATCH 1/3] Add endpoint for Profile API authorisation check --- .../api/profile_auth_check_controller.rb | 13 +++++++++++++ config/routes.rb | 2 ++ lib/profile_api_client.rb | 11 ++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/profile_auth_check_controller.rb diff --git a/app/controllers/api/profile_auth_check_controller.rb b/app/controllers/api/profile_auth_check_controller.rb new file mode 100644 index 000000000..e8ca57795 --- /dev/null +++ b/app/controllers/api/profile_auth_check_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Api + class ProfileAuthCheckController < ApiController + def index + authorised = ProfileApiClient.check_auth(token: current_user&.token) + + render json: { can_use_profile_api: authorised }, status: :ok + rescue ProfileApiClient::UnauthorizedError + render json: { can_use_profile_api: false }, status: :ok + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 500644cb6..2868ddb1a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -92,6 +92,8 @@ post '/google/auth/exchange-code', to: 'google_auth#exchange_code', defaults: { format: :json } resources :features, only: %i[index] + + resources :profile_auth_check, only: %i[index] end resource :github_webhooks, only: :create, defaults: { formats: :json } diff --git a/lib/profile_api_client.rb b/lib/profile_api_client.rb index c550da352..1a64efb23 100644 --- a/lib/profile_api_client.rb +++ b/lib/profile_api_client.rb @@ -36,12 +36,21 @@ def initialize(response) @response_status = response.status @response_headers = response.headers @response_body = response.body - super("Unexpected response from Profile API (status code #{response.status})") end end class << self + def check_auth(token:) + return true if ENV['BYPASS_OAUTH'].present? + + response = connection(token).get('/api/v1/access') + + response.status == 200 + rescue Faraday::BadRequestError, Faraday::UnauthorizedError + false + end + def create_school(token:, id:, code:) return { 'id' => id, 'schoolCode' => code } if ENV['BYPASS_OAUTH'].present? From 6ca2e341d12d2c6471df55981df288df150241fc Mon Sep 17 00:00:00 2001 From: Matthew Trew Date: Thu, 26 Feb 2026 15:27:02 +0000 Subject: [PATCH 2/3] Add spec for Profile auth check API --- spec/requests/api/profile_auth_check_spec.rb | 57 ++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 spec/requests/api/profile_auth_check_spec.rb diff --git a/spec/requests/api/profile_auth_check_spec.rb b/spec/requests/api/profile_auth_check_spec.rb new file mode 100644 index 000000000..4adb3f07c --- /dev/null +++ b/spec/requests/api/profile_auth_check_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Profile auth check API' do + let(:headers) { { Authorization: UserProfileMock::TOKEN } } + let(:school) { create(:school) } + let(:student) { create(:student, school:) } + + identity_url = "#{ENV.fetch('IDENTITY_URL')}/api/v1/access" + + describe 'GET /api/profile_auth_check' do + context 'when the profile API authorises the current user' do + it 'returns can_use_profile_api: true' do + # Arrange + authenticated_in_hydra_as(student) + stub_request(:get, identity_url).to_return(status: 200, headers:) + + # Act + get '/api/profile_auth_check', headers: headers + + # Assert + expect(response).to have_http_status(:ok) + expect(response.parsed_body).to eq('can_use_profile_api' => true) + end + end + + context 'when the profile API returns unauthorized' do + it 'returns can_use_profile_api: false' do + # Arrange + authenticated_in_hydra_as(student) + stub_request(:get, identity_url).to_return(status: 401, headers:) + + # Act + get '/api/profile_auth_check', headers: headers + + # Assert + expect(response).to have_http_status(:ok) + expect(response.parsed_body).to eq('can_use_profile_api' => false) + end + end + + context 'when there is no current user' do + it 'returns can_use_profile_api: false' do + # Arrange + stub_request(:get, identity_url).to_return(status: 400, headers:) + + # Act + get '/api/profile_auth_check' + + # Assert + expect(response).to have_http_status(:ok) + expect(response.parsed_body).to eq('can_use_profile_api' => false) + end + end + end +end From 1caa67bbf24bbf6e82482e3f1e39be5ed73f2596 Mon Sep 17 00:00:00 2001 From: Matthew Trew Date: Thu, 26 Feb 2026 15:57:21 +0000 Subject: [PATCH 3/3] Mock ENV lookups --- spec/requests/api/profile_auth_check_spec.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/requests/api/profile_auth_check_spec.rb b/spec/requests/api/profile_auth_check_spec.rb index 4adb3f07c..831a9efc2 100644 --- a/spec/requests/api/profile_auth_check_spec.rb +++ b/spec/requests/api/profile_auth_check_spec.rb @@ -6,15 +6,21 @@ let(:headers) { { Authorization: UserProfileMock::TOKEN } } let(:school) { create(:school) } let(:student) { create(:student, school:) } + let(:api_url) { 'http://example.com' } + let(:api_key) { 'api-key' } - identity_url = "#{ENV.fetch('IDENTITY_URL')}/api/v1/access" + before do + allow(ENV).to receive(:fetch).and_call_original + allow(ENV).to receive(:fetch).with('IDENTITY_URL').and_return(api_url) + allow(ENV).to receive(:fetch).with('PROFILE_API_KEY').and_return(api_key) + end describe 'GET /api/profile_auth_check' do context 'when the profile API authorises the current user' do it 'returns can_use_profile_api: true' do # Arrange authenticated_in_hydra_as(student) - stub_request(:get, identity_url).to_return(status: 200, headers:) + stub_request(:get, "#{ENV.fetch('IDENTITY_URL')}/api/v1/access").to_return(status: 200, headers:) # Act get '/api/profile_auth_check', headers: headers @@ -29,7 +35,7 @@ it 'returns can_use_profile_api: false' do # Arrange authenticated_in_hydra_as(student) - stub_request(:get, identity_url).to_return(status: 401, headers:) + stub_request(:get, "#{ENV.fetch('IDENTITY_URL')}/api/v1/access").to_return(status: 401, headers:) # Act get '/api/profile_auth_check', headers: headers @@ -43,7 +49,7 @@ context 'when there is no current user' do it 'returns can_use_profile_api: false' do # Arrange - stub_request(:get, identity_url).to_return(status: 400, headers:) + stub_request(:get, "#{ENV.fetch('IDENTITY_URL')}/api/v1/access").to_return(status: 400, headers:) # Act get '/api/profile_auth_check'