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