main
  1# frozen_string_literal: true
  2
  3require 'rails_helper'
  4
  5RSpec.describe '/oauth/authorizations' do
  6  context "when the user is logged in" do
  7    let(:current_user) { create(:user) }
  8
  9    before { http_login(current_user) }
 10
 11    describe "GET /oauth/authorizations" do
 12      let(:state) { SecureRandom.uuid }
 13
 14      context "when the client id is known" do
 15        let(:client) { create(:client) }
 16
 17        context "when requesting an authorization code" do
 18          before { get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'code', state: state, redirect_uri: client.redirect_uris[0] } }
 19
 20          specify { expect(response).to have_http_status(:ok) }
 21          specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
 22        end
 23
 24        context "when requesting an access token" do
 25          before { get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uris[0] } }
 26
 27          specify { expect(response).to have_http_status(:ok) }
 28          specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
 29        end
 30
 31        context "when an incorrect response_type is provided" do
 32          before { get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'invalid', redirect_uri: client.redirect_uris[0] } }
 33
 34          specify { expect(response).to redirect_to("#{client.redirect_uris[0]}#error=unsupported_response_type") }
 35        end
 36
 37        context "when the redirect uri does not match" do
 38          before { get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'invalid', redirect_uri: SecureRandom.uuid } }
 39
 40          specify { expect(response).to redirect_to("#{client.redirect_uris[0]}#error=invalid_request") }
 41        end
 42      end
 43    end
 44
 45    describe "POST /oauth/authorizations" do
 46      context "when the client id is known" do
 47        let(:client) { create(:client) }
 48        let(:state) { SecureRandom.uuid }
 49
 50        context "when the client requested an authorization code" do
 51          before do
 52            get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'code', state: state, redirect_uri: client.redirect_uris[0] }
 53            post "/oauth/authorizations"
 54          end
 55
 56          specify { expect(response).to redirect_to(client.redirect_url(code: Authorization.last.code, state: state)) }
 57        end
 58
 59        context "when the client requested a token" do
 60          let(:token) { Token.access.active.last&.to_jwt }
 61          let(:scope) { "admin" }
 62
 63          before do
 64            get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uris[0] }
 65            post "/oauth/authorizations"
 66          end
 67
 68          specify { expect(response).to redirect_to("#{client.redirect_uris[0]}#access_token=#{token}&token_type=Bearer&expires_in=300&scope=#{scope}&state=#{state}") }
 69        end
 70
 71        context "when the client requested a token using a valid PKCE with S256" do
 72          let(:token) { Token.access.active.last&.to_jwt }
 73          let(:code_verifier) { SecureRandom.hex(128) }
 74          let(:code_challenge) { Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)) }
 75
 76          before do
 77            get "/oauth/authorizations", params: {
 78              client_id: client.to_param,
 79              response_type: 'code',
 80              code_challenge: code_challenge,
 81              code_challenge_method: 'S256',
 82              state: state,
 83              redirect_uri: client.redirect_uris[0]
 84            }
 85            post "/oauth/authorizations"
 86          end
 87
 88          specify { expect(response).to redirect_to(client.redirect_url(code: Authorization.last.code, state: state)) }
 89          specify { expect(Authorization.last).to be_sha256 }
 90          specify { expect(Authorization.last.challenge).to eql(code_challenge) }
 91        end
 92
 93        context "when the client requested a token using a valid PKCE with plain" do
 94          let(:token) { Token.access.active.last&.to_jwt }
 95          let(:code_verifier) { SecureRandom.hex(128) }
 96
 97          before do
 98            get "/oauth/authorizations", params: {
 99              client_id: client.to_param,
100              response_type: 'code',
101              code_challenge: code_verifier,
102              code_challenge_method: 'plain',
103              state: state,
104              redirect_uri: client.redirect_uris[0]
105            }
106            post "/oauth/authorizations"
107          end
108
109          specify { expect(response).to redirect_to(client.redirect_url(code: Authorization.last.code, state: state)) }
110          specify { expect(Authorization.last).to be_plain }
111          specify { expect(Authorization.last.challenge).to eql(code_verifier) }
112        end
113
114        context "when the client requested a token using a valid PKCE with the default code_challenge_method" do
115          let(:token) { Token.access.active.last&.to_jwt }
116          let(:code_verifier) { SecureRandom.hex(128) }
117
118          before do
119            get "/oauth/authorizations", params: {
120              client_id: client.to_param,
121              response_type: 'code',
122              code_challenge: code_verifier,
123              state: state,
124              redirect_uri: client.redirect_uris[0]
125            }
126            post "/oauth/authorizations"
127          end
128
129          specify { expect(response).to redirect_to(client.redirect_url(code: Authorization.last.code, state: state)) }
130          specify { expect(Authorization.last).to be_plain }
131          specify { expect(Authorization.last.challenge).to eql(code_verifier) }
132        end
133
134        context "when the client did not make an appropriate request" do
135          before { post "/oauth/authorizations" }
136
137          specify { expect(response).to have_http_status(:bad_request) }
138        end
139
140        context "when the state parameter looks malicious" do
141          let(:state) { "<script>alert('hi');</script>" }
142
143          before do
144            get "/oauth/authorizations", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uris[0] }
145            post "/oauth/authorizations"
146          end
147
148          specify { expect(response).to redirect_to(client.redirect_url(error: 'invalid_request')) }
149        end
150      end
151    end
152  end
153end