-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Implement Token Revocation Endpoint #84
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
Closed
babuv2
wants to merge
1
commit into
spring-projects:master
from
babuv2:topic/babuv/tokenRevocationEndpoint
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
48 changes: 48 additions & 0 deletions
48
...ngframework/security/oauth2/server/authorization/DefaultOAuth2TokenRevocationService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.security.oauth2.server.authorization; | ||
|
||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* An {@link OAuth2TokenRevocationService} that revokes tokens. | ||
* | ||
* @author Vivek Babu | ||
* @see OAuth2AuthorizationService | ||
* @since 0.0.1 | ||
*/ | ||
public final class DefaultOAuth2TokenRevocationService implements OAuth2TokenRevocationService { | ||
|
||
private OAuth2AuthorizationService authorizationService; | ||
|
||
/** | ||
* Constructs an {@code DefaultOAuth2TokenRevocationService}. | ||
*/ | ||
public DefaultOAuth2TokenRevocationService(OAuth2AuthorizationService authorizationService) { | ||
Assert.notNull(authorizationService, "authorizationService cannot be null"); | ||
this.authorizationService = authorizationService; | ||
} | ||
|
||
@Override | ||
public void revoke(String token, TokenType tokenType) { | ||
final OAuth2Authorization authorization = this.authorizationService.findByTokenAndTokenType(token, tokenType); | ||
if (authorization != null) { | ||
final OAuth2Authorization revokedAuthorization = OAuth2Authorization.from(authorization) | ||
.revoked(true).build(); | ||
this.authorizationService.save(revokedAuthorization); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...rg/springframework/security/oauth2/server/authorization/OAuth2TokenRevocationService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.security.oauth2.server.authorization; | ||
|
||
/** | ||
* Implementations of this interface are responsible for the revocation of | ||
* OAuth2 tokens. | ||
* | ||
* @author Vivek Babu | ||
* @since 0.0.1 | ||
*/ | ||
public interface OAuth2TokenRevocationService { | ||
|
||
/** | ||
* Revokes the given token. | ||
* | ||
* @param token the token to be revoked | ||
* @param tokenType the type of token to be revoked | ||
*/ | ||
void revoke(String token, TokenType tokenType); | ||
} |
99 changes: 99 additions & 0 deletions
99
...uth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.security.oauth2.server.authorization.authentication; | ||
|
||
import org.springframework.security.authentication.AuthenticationProvider; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; | ||
import org.springframework.security.oauth2.core.OAuth2Error; | ||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes; | ||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; | ||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenRevocationService; | ||
import org.springframework.security.oauth2.server.authorization.TokenType; | ||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; | ||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Token Revocation. | ||
* | ||
* @author Vivek Babu | ||
* @since 0.0.1 | ||
* @see OAuth2TokenRevocationAuthenticationToken | ||
* @see OAuth2AuthorizationService | ||
* @see OAuth2TokenRevocationService | ||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section 2.1 Revocation Request</a> | ||
*/ | ||
public class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider { | ||
|
||
private OAuth2AuthorizationService authorizationService; | ||
private OAuth2TokenRevocationService tokenRevocationService; | ||
|
||
/** | ||
* Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the provided parameters. | ||
* | ||
* @param authorizationService the authorization service | ||
* @param tokenRevocationService the token revocation service | ||
*/ | ||
public OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService, | ||
OAuth2TokenRevocationService tokenRevocationService) { | ||
Assert.notNull(authorizationService, "authorizationService cannot be null"); | ||
Assert.notNull(tokenRevocationService, "tokenRevocationService cannot be null"); | ||
this.authorizationService = authorizationService; | ||
this.tokenRevocationService = tokenRevocationService; | ||
} | ||
|
||
@Override | ||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthenticationToken = | ||
(OAuth2TokenRevocationAuthenticationToken) authentication; | ||
|
||
OAuth2ClientAuthenticationToken clientPrincipal = null; | ||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(tokenRevocationAuthenticationToken.getPrincipal() | ||
.getClass())) { | ||
clientPrincipal = (OAuth2ClientAuthenticationToken) tokenRevocationAuthenticationToken.getPrincipal(); | ||
} | ||
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) { | ||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT)); | ||
} | ||
|
||
final RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); | ||
final String tokenTypeHint = tokenRevocationAuthenticationToken.getTokenTypeHint(); | ||
final String token = tokenRevocationAuthenticationToken.getToken(); | ||
final OAuth2Authorization authorization = authorizationService.findByTokenAndTokenType(token, | ||
TokenType.ACCESS_TOKEN); | ||
|
||
OAuth2TokenRevocationAuthenticationToken successfulAuthentication = | ||
new OAuth2TokenRevocationAuthenticationToken(token, registeredClient, tokenTypeHint); | ||
|
||
if (authorization == null) { | ||
return successfulAuthentication; | ||
} | ||
|
||
if (!registeredClient.getClientId().equals(authorization.getRegisteredClientId())) { | ||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT)); | ||
} | ||
|
||
tokenRevocationService.revoke(token, TokenType.ACCESS_TOKEN); | ||
return successfulAuthentication; | ||
} | ||
|
||
@Override | ||
public boolean supports(Class<?> authentication) { | ||
return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication); | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
.../oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.security.oauth2.server.authorization.authentication; | ||
|
||
import org.springframework.lang.Nullable; | ||
import org.springframework.security.authentication.AbstractAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.oauth2.server.authorization.Version; | ||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; | ||
import org.springframework.util.Assert; | ||
|
||
import java.util.Collections; | ||
|
||
/** | ||
* An {@link Authentication} implementation used for OAuth 2.0 Client Authentication. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo in javadoc |
||
* | ||
* @author Vivek Babu | ||
* @since 0.0.1 | ||
* @see AbstractAuthenticationToken | ||
* @see RegisteredClient | ||
* @see OAuth2TokenRevocationAuthenticationProvider | ||
*/ | ||
public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken { | ||
private static final long serialVersionUID = Version.SERIAL_VERSION_UID; | ||
private final String tokenTypeHint; | ||
private Authentication clientPrincipal; | ||
private String token; | ||
private RegisteredClient registeredClient; | ||
|
||
public OAuth2TokenRevocationAuthenticationToken(String token, | ||
Authentication clientPrincipal, @Nullable String tokenTypeHint) { | ||
super(Collections.emptyList()); | ||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); | ||
Assert.hasText(token, "token cannot be empty"); | ||
this.token = token; | ||
this.clientPrincipal = clientPrincipal; | ||
this.tokenTypeHint = tokenTypeHint; | ||
} | ||
|
||
public OAuth2TokenRevocationAuthenticationToken(String token, | ||
RegisteredClient registeredClient, @Nullable String tokenTypeHint) { | ||
super(Collections.emptyList()); | ||
Assert.notNull(registeredClient, "registeredClient cannot be null"); | ||
Assert.hasText(token, "token cannot be empty"); | ||
this.token = token; | ||
this.registeredClient = registeredClient; | ||
this.tokenTypeHint = tokenTypeHint; | ||
setAuthenticated(true); | ||
} | ||
|
||
@Override | ||
public Object getPrincipal() { | ||
return this.clientPrincipal != null ? this.clientPrincipal : this.registeredClient | ||
.getClientId(); | ||
} | ||
|
||
@Override | ||
public Object getCredentials() { | ||
return ""; | ||
} | ||
|
||
/** | ||
* Returns the token. | ||
* | ||
* @return the token | ||
*/ | ||
public String getToken() { | ||
return this.token; | ||
} | ||
|
||
/** | ||
* Returns the token type hint. | ||
* | ||
* @return the token type hint | ||
*/ | ||
public String getTokenTypeHint() { | ||
return tokenTypeHint; | ||
} | ||
|
||
/** | ||
* Returns the {@link RegisteredClient registered client}. | ||
* | ||
* @return the {@link RegisteredClient} | ||
*/ | ||
public @Nullable | ||
RegisteredClient getRegisteredClient() { | ||
return this.registeredClient; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
OAuth2Authorization.revoked
does not indicate which token is revoked so this member will not work.Let me draw out a scenario:
code
in the Authorization Responsecode
is leaked to a malicious clientcode
and the Authorization Server must detect this and revoke thecode
code
should be rejected. As well, the "real" client will not be able to obtain the access token since thecode
was revoked. Instead the client will have to restart the flow.So we do need some kind of construct in
OAuth2Authorization
that maps a token (code, access token or refresh token) to some extra metadata. One attribute would be a revoked flag. We'll also likely want to know the time it was revoked. We may store additional attributes related to the token, eg. thecode
can only be used once.Give this some further thought and the type of construct we'll need so it can support storing metadata/attributes related to a specific token.
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.
@babuv2 I've been giving this some further thought and I'd like to propose the following class as a holder of token metadata. The name maps nicely as referenced in the Token Introspection RFC:
How about we start with this and see how it shapes up. Sounds good?