-
Notifications
You must be signed in to change notification settings - Fork 895
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor OAuth 2.0 modules to easily implement extension grants #5476
Conversation
Motivation: Currently, access token grant flow is tightly coupled with AccessTokenRequest. As a result, we have to implement `OAuth2XXXGrant`, `OAuth2XXXGrantBuilder` and `XXXTokenRequest` to implement a new grant flow. An extension grant such as JWT is slightly different from the exisiting access token grant flow. The only difference is in body parameters. It is not pracitial to create three classes and use internal API for that. To resolve the problem, I designed new APIs and refactored the existing OAuth2 module so that users can easily implement a new extention grant. API design note: - `OAuth2Request`: Represents an OAuth 2.0 request that can be serialized to `HttpRequest`. - `AccessTokenRequest`: Provides body parameters and client authentication to obtain an access token. - This API will be used as an extension point to support new grants. - The implmentations of `AccessTokenRequest` - `ClientCredentialsAccessTokenRequest` - `ResourceOwnerPasswordAccessTokenRequest` - `JsonWebTokenAccessTokenRequest` - `ClientAuthentication`: Provides a client authentication for the OAuth 2.0 requests. `OAuth2Request` has `ClientAuthentication` as an optional value. - The interface is a drop-in replacement for `ClientAuthorization` which was a final class that does not allow customization. - Additionally, as per OAuth 2.0 spec, Client Authentication is a correct expession. - `OAuth2ResponseHandler`: Handles OAuth 2.0 responses. - This interface would be useful if users want to handle responses with a custom logic. - `TokenOperationRequest`: Provides body parameters for token revocation and token introspection. Modifications: - TBU
e19adeb
to
bd84dee
Compare
@max904-github Based on your work, I improved the API while maintaining the internal implementation. If available, please give me a review. 🙇♂️ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks much better now. 👍 👍 👍
Left minor comments and questions.
oauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/AbstractAccessTokenRequest.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/AccessTokenRequest.java
Outdated
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/AccessTokenRequest.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/AccessTokenRequest.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/OAuth2Request.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/TokenOperationRequest.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/TokenRevocationBuilder.java
Show resolved
Hide resolved
} | ||
return bodyParams = OAuth2Request.super.bodyParams(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bodyParams
does not contain clientAuthentication
if bodyParams()
is called before asHttpRequest
is called.
What do you think about raising an exception in such a situation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored the code a bit to produce bodyParams
correctly.
…ccessTokenRequest.java Co-authored-by: minux <songmw725@gmail.com>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5476 +/- ##
============================================
+ Coverage 74.02% 74.06% +0.03%
- Complexity 20781 21047 +266
============================================
Files 1801 1832 +31
Lines 76530 77493 +963
Branches 9749 9877 +128
============================================
+ Hits 56654 57394 +740
- Misses 15264 15421 +157
- Partials 4612 4678 +66 ☔ View full report in Codecov by Sentry. |
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/ClientAuthentication.java
Outdated
Show resolved
Hide resolved
...h2/src/main/java/com/linecorp/armeria/internal/common/auth/oauth2/AbstractOAuth2Request.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor comments but overall direction looks good 👍 Thanks @ikhoon 🙇 👍 🙇 Please merge when ready 🙇
...h2/src/main/java/com/linecorp/armeria/internal/common/auth/oauth2/AbstractOAuth2Request.java
Outdated
Show resolved
Hide resolved
* If null, this {@link ClientAuthentication} will be added as body parameters. | ||
*/ | ||
@Nullable | ||
String asHeaderValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional) I found the logic in AbstractOAuth2Request
slightly confusing.
Lines 80 to 83 in eaaf517
final String headerValue = clientAuthentication.asHeaderValue(); | |
if (headerValue == null) { | |
clientAuthentication.addAsBodyParams(formBuilder); | |
} |
I wonder if it's more clear to create an API which looks similar to adding body parameters.
e.g.
void addHeaderValues(RequestHeadersBuilde)
By doing so, I think it is also easier to support other authentication methods easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I think my thoughts were too focused on compatibility with the existing ClientAuthorization
.
...h2/src/main/java/com/linecorp/armeria/client/auth/oauth2/JsonWebTokenAccessTokenRequest.java
Show resolved
Hide resolved
.../src/main/java/com/linecorp/armeria/common/auth/oauth2/JsonWebTokenClientAuthentication.java
Show resolved
Hide resolved
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/TokenRevocation.java
Outdated
Show resolved
Hide resolved
…/oauth2/AbstractOAuth2Request.java Co-authored-by: jrhee17 <guins_j@guins.org>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀
Motivation:
Currently, the access token grant flow is tightly coupled with AccessTokenRequest. As a result, we have to implement
OAuth2XXXGrant
,OAuth2XXXGrantBuilder
, andXXXTokenRequest
to implement a new grant flow.An extension grant such as JWT is slightly different from the existing access token grant flow. The only difference is in body parameters. It is not practical to create three classes and use internal API for that.
To resolve the problem, I designed new APIs and refactored the existing OAuth2 module so that users could easily implement a new extension grant.
API design note:
OAuth2Request
: Represents an OAuth 2.0 request that can be serialized toHttpRequest
.AccessTokenRequest
: Provides body parameters and client authentication to obtain an access token.AccessTokenRequest
ClientCredentialsAccessTokenRequest
ResourceOwnerPasswordAccessTokenRequest
JsonWebTokenAccessTokenRequest
ClientAuthentication
: Provides client authentication for the OAuth 2.0 requests.OAuth2Request
hasClientAuthentication
as an optional value.ClientAuthorization
which was a final class that does not allow customization.OAuth2ResponseHandler
: Handles OAuth 2.0 responses.TokenOperationRequest
: Provides body parameters for token revocation and token introspection.OAuth2AuthorizationGrantBuilder
: A unified builder to fluently createOAuth2AuthorizationGrant
.OAuth2ResourceOwnerPasswordCredentialsGrantBuilder
andOAuth2ClientCredentialsGrantBuilder
.Example:
As you can see, the only difference is how you create
AccessTokenRequest
. The rest of the process will use the same API regardless of the grant type used.Modifications:
internal.client...AbstractAccessTokenRequest
internal.client...ClientCredentialsTokenRequest
internal.client...RefreshAccessTokenRequest
internal.client...ResourceOwnerPasswordCredentialsTokenRequest
internal.common...AbstractTokenOperationRequest
internal.common...TokenRevocationRequest
internal.common...TokenIntrospectionRequest
AccessTokenRequest
and their implementationsAccessTokenRequest
is an immutable class that only provides body parameters and client authentication.AccessTokenRequest
does not know how the request is processed. Implementations ofOAuth2AuthorizationGrant
andOAuth2ResponseHandler
will take responsibility for it.ClientCredentialsAccessTokenRequest
ResourceOwnerPasswordAccessTokenRequest
RefreshAccessTokenRequest
JsonWebTokenAccessTokenRequest
(NEW)OAuth2ResourceOwnerPasswordCredentialsGrant
OAuth2ResourceOwnerPasswordCredentialsGrantBuilder
OAuth2ClientCredentialsGrant
OAuth2ClientCredentialsGrantBuilder
ClientAuthentication
that provides authentication information toAuthorization
header or body parameters.ClientAuthorization
and internally delegated toClientAuthentication
.ClientPasswordClientAuthentication
AnyAuthorizationSchemeClientAuthentication
JsonWebTokenClientAuthentication
(NEW)TokenOperationRequest
to replaceTokenRevocationRequest
andTokenIntrospectionRequest
.Result:
AccessTokenRequest
.