Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/envoy/extensions/filters/http/oauth2/v3alpha/oauth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ message OAuth2Credentials {

// OAuth config
//
// [#next-free-field: 10]
// [#next-free-field: 11]
message OAuth2Config {
// Endpoint on the authorization server to retrieve the access token from.
config.core.v3.HttpUri token_endpoint = 1;
Expand Down Expand Up @@ -76,6 +76,10 @@ message OAuth2Config {
// defaults to "user" scope.
// OAuth RFC https://tools.ietf.org/html/rfc6749#section-3.3
repeated string auth_scopes = 9;

// Optional resource parameter for authorization request
// RFC: https://tools.ietf.org/html/rfc8707
repeated string resources = 10;
}

// Filter config.
Expand Down
6 changes: 5 additions & 1 deletion api/envoy/extensions/filters/http/oauth2/v4alpha/oauth.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions docs/root/configuration/http/http_filters/oauth2_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ The following is an example configuring the filter.
- user
- openid
- email
# (Optional): set resource parameter for Authorization request
resources:
- oauth2-resource
- http://example.com

Below is a complete code example of how we employ the filter as one of
:ref:`HttpConnectionManager HTTP filters
Expand Down Expand Up @@ -124,6 +128,10 @@ Below is a complete code example of how we employ the filter as one of
- user
- openid
- email
# (Optional): set resource parameter for Authorization request
resources:
- oauth2-resource
- http://example.com
- name: envoy.router
tracing: {}
codec_type: "AUTO"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion source/extensions/filters/http/oauth2/filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ authScopesList(const Protobuf::RepeatedPtrField<std::string>& auth_scopes_protos
return scopes;
}

// Transforms the proto list into encoded resource params
// Takes care of percentage encoding http and https is needed
std::string encodeResourceList(const Protobuf::RepeatedPtrField<std::string>& resources_protos) {
std::string result = "";
for (const auto& resource : resources_protos) {
result += "&resource=" + Http::Utility::PercentEncoding::encode(resource, ":/=&? ");
}
return result;
}

// Sets the auth token as the Bearer token in the authorization header.
void setBearerToken(Http::RequestHeaderMap& headers, const std::string& token) {
headers.setInline(authorization_handle.handle(), absl::StrCat("Bearer ", token));
Expand All @@ -112,6 +122,7 @@ FilterConfig::FilterConfig(
stats_(FilterConfig::generateStats(stats_prefix, scope)),
encoded_auth_scopes_(Http::Utility::PercentEncoding::encode(
absl::StrJoin(authScopesList(proto_config.auth_scopes()), " "), ":/=&? ")),
encoded_resources_(encodeResourceList(proto_config.resources())),
forward_bearer_token_(proto_config.forward_bearer_token()),
pass_through_header_matchers_(headerMatchers(proto_config.pass_through_matcher())) {
if (!cluster_manager.clusters().hasCluster(oauth_token_endpoint_.cluster())) {
Expand Down Expand Up @@ -300,7 +311,10 @@ Http::FilterHeadersStatus OAuth2Filter::decodeHeaders(Http::RequestHeaderMap& he
const std::string new_url = fmt::format(
AuthorizationEndpointFormat, config_->authorizationEndpoint(), config_->clientId(),
config_->encodedAuthScopes(), escaped_redirect_uri, escaped_state);
response_headers->setLocation(new_url);

const std::string& resource_param = config_->encodedResources();

response_headers->setLocation(new_url + resource_param);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small nit but could we rename encodedResources to encodedResourceQueryParams and then inline resource_param like so:

response_headers->setLocation(new_url + config_->encodedResourceQueryParams());

? I think that would read a bit cleaner

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snowp, I have done the changes. The macos tests are failing because go binary could not be installed on the test setup.
Let me know how to move forward from here.

code is up for review

decoder_callbacks_->encodeHeaders(std::move(response_headers), true, REDIRECT_FOR_CREDENTIALS);

config_->stats().oauth_unauthorized_rq_.inc();
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/http/oauth2/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class FilterConfig {
std::string tokenSecret() const { return secret_reader_->tokenSecret(); }
FilterStats& stats() { return stats_; }
const std::string& encodedAuthScopes() const { return encoded_auth_scopes_; }
const std::string& encodedResources() const { return encoded_resources_; }

private:
static FilterStats generateStats(const std::string& prefix, Stats::Scope& scope);
Expand All @@ -137,6 +138,7 @@ class FilterConfig {
std::shared_ptr<SecretReader> secret_reader_;
FilterStats stats_;
const std::string encoded_auth_scopes_;
const std::string encoded_resources_;
const bool forward_bearer_token_ : 1;
const std::vector<Http::HeaderUtility::HeaderData> pass_through_header_matchers_;
};
Expand Down
8 changes: 8 additions & 0 deletions test/extensions/filters/http/oauth2/config_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ void expectInvalidSecretConfig(const std::string& failed_secret_name,
- user
- openid
- email
resources:
- oauth2-resource
- http://example.com
- https://example.com
)EOF";

OAuth2Config factory;
Expand Down Expand Up @@ -95,6 +99,10 @@ TEST(ConfigTest, CreateFilter) {
- user
- openid
- email
resources:
- oauth2-resource
- http://example.com
- https://example.com
)EOF";

OAuth2Config factory;
Expand Down
14 changes: 12 additions & 2 deletions test/extensions/filters/http/oauth2/filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ class OAuth2Test : public testing::Test {
p.add_auth_scopes("user");
p.add_auth_scopes("openid");
p.add_auth_scopes("email");
p.add_resources("oauth2-resource");
p.add_resources("http://example.com");
p.add_resources("https://example.com");
auto* matcher = p.add_pass_through_matcher();
matcher->set_name(":method");
matcher->set_exact_match("OPTIONS");
Expand Down Expand Up @@ -197,6 +200,9 @@ TEST_F(OAuth2Test, DefaultAuthScope) {
// Auth_scopes was not set, should return default value.
EXPECT_EQ(test_config_->encodedAuthScopes(), TEST_DEFAULT_SCOPE);

// resource is optional
EXPECT_EQ(test_config_->encodedResources(), "");

// Recreate the filter with current config and test if the scope was added
// as a query parameter in response headers.
init(test_config_);
Expand Down Expand Up @@ -324,7 +330,9 @@ TEST_F(OAuth2Test, OAuthErrorNonOAuthHttpCallback) {
TEST_CLIENT_ID + "&scope=" + TEST_ENCODED_AUTH_SCOPES +
"&response_type=code&"
"redirect_uri=http%3A%2F%2Ftraffic.example.com%2F"
"_oauth&state=http%3A%2F%2Ftraffic.example.com%2Fnot%2F_oauth"},
"_oauth&state=http%3A%2F%2Ftraffic.example.com%2Fnot%2F_oauth"
"&resource=oauth2-resource&resource=http%3A%2F%2Fexample.com"
"&resource=https%3A%2F%2Fexample.com"},
};

// explicitly tell the validator to fail the validation
Expand Down Expand Up @@ -634,7 +642,9 @@ TEST_F(OAuth2Test, OAuthTestFullFlowPostWithParameters) {
"&response_type=code&"
"redirect_uri=https%3A%2F%2Ftraffic.example.com%2F"
"_oauth&state=https%3A%2F%2Ftraffic.example.com%2Ftest%"
"3Fname%3Dadmin%26level%3Dtrace"},
"3Fname%3Dadmin%26level%3Dtrace"
"&resource=oauth2-resource&resource=http%3A%2F%2Fexample.com"
"&resource=https%3A%2F%2Fexample.com"},
};

// Fail the validation to trigger the OAuth flow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ name: oauth
- user
- openid
- email
resources:
- oauth2-resource
- http://example.com
- https://example.com
)EOF");

// Add the OAuth cluster.
Expand Down