main
  1# frozen_string_literal: true
  2
  3require 'rails_helper'
  4
  5RSpec.describe "/oauth/clients" do
  6  describe "POST /oauth/clients" do
  7    let(:redirect_uris) { [generate(:uri), generate(:uri)] }
  8    let(:client_name) { FFaker::Name.name }
  9    let(:logo_uri) { generate(:uri) }
 10    let(:jwks_uri) { generate(:uri) }
 11    let(:json) { JSON.parse(response.body, symbolize_names: true) }
 12    let(:last_client) { Client.order(created_at: :asc).last }
 13
 14    context "when the registration request is valid" do
 15      before do
 16        post "/oauth/clients", params: {
 17          redirect_uris: redirect_uris,
 18          client_name: client_name,
 19          token_endpoint_auth_method: :client_secret_basic,
 20          logo_uri: logo_uri,
 21          jwks_uri: jwks_uri,
 22        }
 23      end
 24
 25      specify { expect(response).to have_http_status(:created) }
 26      specify { expect(response.headers['Set-Cookie']).to be_nil }
 27      specify { expect(response.content_type).to start_with("application/json") }
 28      specify { expect(response.headers['Cache-Control']).to include("no-store") }
 29      specify { expect(response.headers['Pragma']).to eql("no-cache") }
 30      specify { expect(json[:client_id]).to eql(last_client.to_param) }
 31      specify { expect(json[:client_secret]).to be_present }
 32      specify { expect(json[:client_id_issued_at]).to eql(last_client.created_at.to_i) }
 33      specify { expect(json[:client_secret_expires_at]).to be_zero }
 34      specify { expect(json[:redirect_uris]).to match_array(redirect_uris) }
 35      specify { expect(json[:grant_types]).to match_array(last_client.grant_types.map(&:to_s)) }
 36      specify { expect(json[:client_name]).to eql(client_name) }
 37      specify { expect(json[:token_endpoint_auth_method]).to eql('client_secret_basic') }
 38      specify { expect(json[:logo_uri]).to eql(logo_uri) }
 39      specify { expect(json[:jwks_uri]).to eql(jwks_uri) }
 40    end
 41
 42    context "when the registrations is missing valid redirect_uris" do
 43      before do
 44        post "/oauth/clients", params: {
 45          redirect_uris: [],
 46          client_name: client_name,
 47          token_endpoint_auth_method: :client_secret_basic,
 48          logo_uri: logo_uri,
 49          jwks_uri: jwks_uri,
 50        }
 51      end
 52
 53      specify { expect(response).to have_http_status(:bad_request) }
 54      specify { expect(json[:error]).to eql("invalid_redirect_uri") }
 55      specify { expect(json[:error_description]).to be_present }
 56    end
 57
 58    context "when the registration request is missing a client name" do
 59      before do
 60        post "/oauth/clients", params: {
 61          redirect_uris: redirect_uris,
 62          client_name: "",
 63          token_endpoint_auth_method: :client_secret_basic,
 64          logo_uri: logo_uri,
 65          jwks_uri: jwks_uri,
 66        }
 67      end
 68
 69      specify { expect(response).to have_http_status(:bad_request) }
 70      specify { expect(json[:error]).to eql("invalid_client_metadata") }
 71      specify { expect(json[:error_description]).to be_present }
 72    end
 73  end
 74
 75  describe "GET /oauth/clients/:id" do
 76    context "when the credentials are valid" do
 77      let(:client) { create(:client) }
 78      let(:access_token) { create(:access_token, subject: client) }
 79      let(:headers) { { 'Authorization' => "Bearer #{access_token.to_jwt}" } }
 80      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 81
 82      before { get "/oauth/clients/#{client.to_param}", headers: headers }
 83
 84      specify { expect(response).to have_http_status(:ok) }
 85      specify { expect(response.content_type).to start_with('application/json') }
 86      specify { expect(response.headers['Set-Cookie']).to be_nil }
 87      specify { expect(json[:client_id]).to eql(client.to_param) }
 88      pending { expect(json[:client_secret]).to eql(client.password) }
 89      specify { expect(json[:client_id_issued_at]).to eql(client.created_at.to_i) }
 90      specify { expect(json[:client_secret_expires_at]).to be_zero }
 91      specify { expect(json[:redirect_uris]).to match_array(client.redirect_uris) }
 92      specify { expect(json[:grant_types]).to match_array(client.grant_types.map(&:to_s)) }
 93      specify { expect(json[:client_name]).to eql(client.name) }
 94      specify { expect(json[:token_endpoint_auth_method]).to eql('client_secret_basic') }
 95      specify { expect(json[:logo_uri]).to eql(client.logo_uri) }
 96      specify { expect(json[:jwks_uri]).to eql(client.jwks_uri) }
 97      pending { expect(json[:registration_client_uri]).to eql(oauth_client_path(client)) }
 98      pending { expect(json[:registration_access_token]).to be_present }
 99    end
100
101    context "when one client tries to read another client" do
102      let(:client) { create(:client) }
103      let(:other_client) { create(:client) }
104      let(:access_token) { create(:access_token, subject: client) }
105      let(:headers) { { 'Authorization' => "Bearer #{access_token.to_jwt}" } }
106      let(:json) { JSON.parse(response.body, symbolize_names: true) }
107
108      before { get "/oauth/clients/#{other_client.id}", headers: headers }
109
110      specify { expect(response).to have_http_status(:forbidden) }
111    end
112
113    context "when the client id does not exist" do
114      let(:client) { create(:client) }
115      let(:access_token) { create(:access_token, subject: client) }
116      let(:headers) { { 'Authorization' => "Bearer #{access_token.to_jwt}" } }
117
118      before { get "/oauth/clients/#{SecureRandom.uuid}", headers: headers }
119
120      specify { expect(response).to have_http_status(:unauthorized) }
121      specify { expect(access_token.reload).to be_revoked }
122    end
123
124    context "when an authorization header is not provided" do
125      let(:client) { create(:client) }
126
127      before { get "/oauth/clients/#{client.to_param}", headers: {} }
128
129      specify { expect(response).to have_http_status(:unauthorized) }
130    end
131  end
132
133  describe "PUT /oauth/clients/:id" do
134    context "when the credentials are valid" do
135      let(:headers) { { 'Authorization' => "Bearer #{access_token.to_jwt}" } }
136      let(:client) { create(:client) }
137      let(:access_token) { create(:access_token, subject: client) }
138
139      context "when the request body is valid" do
140        let(:request_body) do
141          {
142            client_id: client.to_param,
143            client_name: FFaker::Name.name,
144            grant_types: [:authorization_code, :refresh_token],
145            jwks_uri: generate(:uri),
146            logo_uri: generate(:uri),
147            redirect_uris: [generate(:uri), generate(:uri)],
148            token_endpoint_auth_method: :client_secret_basic,
149          }
150        end
151
152        before { put "/oauth/clients/#{client.to_param}", params: request_body, headers: headers }
153
154        specify { expect(response).to have_http_status(:ok) }
155        specify { expect(response.content_type).to start_with('application/json') }
156        specify { expect(json[:client_id]).to eql(client.to_param) }
157        pending { expect(json[:client_secret]).to eql(client.password) }
158        specify { expect(json[:client_id_issued_at]).to eql(client.created_at.to_i) }
159        specify { expect(json[:client_secret_expires_at]).to be_zero }
160        specify { expect(json[:redirect_uris]).to match_array(request_body[:redirect_uris]) }
161        pending { expect(json[:grant_types]).to match_array(request_body[:grant_types].map(&:to_s)) }
162        specify { expect(json[:client_name]).to eql(request_body[:client_name]) }
163        specify { expect(json[:token_endpoint_auth_method]).to eql(request_body[:token_endpoint_auth_method].to_s) }
164        specify { expect(json[:logo_uri]).to eql(request_body[:logo_uri]) }
165        specify { expect(json[:jwks_uri]).to eql(request_body[:jwks_uri]) }
166
167        specify "Valid values of client metadata fields in this request MUST replace, not augment, the values previously associated with this client."
168        specify "Omitted fields MUST be treated as null or empty values by the server, indicating the client's request to delete them from the client's registration."
169        specify "The client MUST includes its 'client_id' field in the request, and it MUST be the same as its currently issued client identifier."
170      end
171
172      context "when the request body is invalid" do
173        let(:request_body) do
174          {
175            client_id: client.to_param,
176            client_name: "",
177            grant_types: [:authorization_code, :refresh_token],
178            jwks_uri: generate(:uri),
179            logo_uri: generate(:uri),
180            redirect_uris: [generate(:uri), generate(:uri)],
181            token_endpoint_auth_method: :client_secret_basic,
182          }
183        end
184
185        before { put "/oauth/clients/#{client.to_param}", params: request_body, headers: headers }
186
187        specify { expect(response).to have_http_status(:bad_request) }
188        specify { expect(response.content_type).to start_with('application/json') }
189        specify { expect(json[:error]).to eql("invalid_client_metadata") }
190        specify { expect(json[:error_description]).to eql("Name can't be blank") }
191      end
192    end
193
194    specify "request MUST NOT include the 'registration_access_token'"
195    specify "request MUST NOT include the 'registration_client_uri'"
196    specify "request MUST NOT include the 'client_secret_expires_at'"
197    specify "request MUST NOT include the 'client_id_issued_at'"
198    specify "If the client includes the `client_secret` field in the request, the value of this field MUST match the currently issued client secret for that client"
199    specify "The client MUST NOT be allowed to overwrite its existing client secret with its own chosen value."
200  end
201end