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