Skip to content

Commit cdb05c0

Browse files
Merge pull request #18393 from opf/validate-scopes
Validate scopes of JWTs
2 parents 8b55cc3 + 5206187 commit cdb05c0

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

lib_static/open_project/authentication/strategies/warden/jwt_oidc.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ def authenticate!
2525
::OpenIDConnect::JwtParser.new(required_claims: ["sub"]).parse(@access_token).either(
2626
->(payload_and_provider) do
2727
payload, provider = payload_and_provider
28+
unless valid_scope?(payload)
29+
return fail_with_header! error: "insufficient_scope",
30+
error_description: "Requires scope #{scope} to access this resource."
31+
end
32+
2833
user = User.find_by(identity_url: "#{provider.slug}:#{payload['sub']}")
2934
authentication_result(user)
3035
end,
@@ -51,6 +56,11 @@ def authentication_result(user)
5156
)
5257
end
5358
end
59+
60+
def valid_scope?(payload)
61+
scopes = (payload["scope"] || "").split
62+
scopes.include?(scope.to_s)
63+
end
5464
end
5565
end
5666
end

spec/requests/api/v3/authentication_spec.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ def set_basic_auth_header(user, password)
406406
"manage-clients",
407407
"query-groups"] },
408408
"account" => { "roles" => ["manage-account", "manage-account-links", "view-profile"] } },
409-
"scope" => "email profile",
409+
"scope" => token_scope,
410410
"sid" => "eb235240-0b47-48fa-8b3e-f3b310d352e3",
411411
"email_verified" => false,
412412
"preferred_username" => "admin"
@@ -417,6 +417,7 @@ def set_basic_auth_header(user, password)
417417
let(:token_sub) { "b70e2fbf-ea68-420c-a7a5-0a287cb689c6" }
418418
let(:token_aud) { ["https://openproject.local", "master-realm", "account"] }
419419
let(:token_issuer) { "https://keycloak.local/realms/master" }
420+
let(:token_scope) { "email profile api_v3" }
420421
let(:expected_message) { "You did not provide the correct credentials." }
421422
let(:keys_request_stub) do
422423
stub_request(:get, "https://keycloak.local/realms/master/protocol/openid-connect/certs")
@@ -479,6 +480,20 @@ def set_basic_auth_header(user, password)
479480
end
480481
end
481482

483+
context "when the scope does not permit access to APIv3" do
484+
let(:token_scope) { "profile email" }
485+
486+
it "fails with HTTP 403 Forbidden" do
487+
get resource
488+
489+
expect(last_response).to have_http_status :forbidden
490+
error = "Requires scope api_v3 to access this resource."
491+
expect(last_response.header["WWW-Authenticate"])
492+
.to eq(%{Bearer realm="OpenProject API", error="insufficient_scope", error_description="#{error}"})
493+
expect(JSON.parse(last_response.body)).to eq(error_response_body)
494+
end
495+
end
496+
482497
context "when access token has expired already" do
483498
let(:token_exp) { 5.minutes.ago }
484499

0 commit comments

Comments
 (0)