Skip to content

Commit dc94e5e

Browse files
babuv2jgrandja
authored andcommitted
Implement Token Revocation Endpoint
Closes gh-83
1 parent 18f8b3a commit dc94e5e

9 files changed

+958
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization;
17+
18+
import org.springframework.util.Assert;
19+
20+
/**
21+
* An {@link OAuth2TokenRevocationService} that revokes tokens.
22+
*
23+
* @author Vivek Babu
24+
* @see OAuth2AuthorizationService
25+
* @since 0.0.1
26+
*/
27+
public final class DefaultOAuth2TokenRevocationService implements OAuth2TokenRevocationService {
28+
29+
private OAuth2AuthorizationService authorizationService;
30+
31+
/**
32+
* Constructs an {@code DefaultOAuth2TokenRevocationService}.
33+
*/
34+
public DefaultOAuth2TokenRevocationService(OAuth2AuthorizationService authorizationService) {
35+
Assert.notNull(authorizationService, "authorizationService cannot be null");
36+
this.authorizationService = authorizationService;
37+
}
38+
39+
@Override
40+
public void revoke(String token, TokenType tokenType) {
41+
final OAuth2Authorization authorization = this.authorizationService.findByTokenAndTokenType(token, tokenType);
42+
if (authorization != null) {
43+
final OAuth2Authorization revokedAuthorization = OAuth2Authorization.from(authorization)
44+
.revoked(true).build();
45+
this.authorizationService.save(revokedAuthorization);
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization;
17+
18+
/**
19+
* Implementations of this interface are responsible for the revocation of
20+
* OAuth2 tokens.
21+
*
22+
* @author Vivek Babu
23+
* @since 0.0.1
24+
*/
25+
public interface OAuth2TokenRevocationService {
26+
27+
/**
28+
* Revokes the given token.
29+
*
30+
* @param token the token to be revoked
31+
* @param tokenType the type of token to be revoked
32+
*/
33+
void revoke(String token, TokenType tokenType);
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization.authentication;
17+
18+
import org.springframework.security.authentication.AuthenticationProvider;
19+
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.core.AuthenticationException;
21+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
22+
import org.springframework.security.oauth2.core.OAuth2Error;
23+
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
24+
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
25+
import org.springframework.security.oauth2.server.authorization.OAuth2TokenRevocationService;
26+
import org.springframework.security.oauth2.server.authorization.TokenType;
27+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
28+
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
29+
import org.springframework.util.Assert;
30+
31+
/**
32+
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Token Revocation.
33+
*
34+
* @author Vivek Babu
35+
* @since 0.0.1
36+
* @see OAuth2TokenRevocationAuthenticationToken
37+
* @see OAuth2AuthorizationService
38+
* @see OAuth2TokenRevocationService
39+
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section 2.1 Revocation Request</a>
40+
*/
41+
public class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider {
42+
43+
private OAuth2AuthorizationService authorizationService;
44+
private OAuth2TokenRevocationService tokenRevocationService;
45+
46+
/**
47+
* Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the provided parameters.
48+
*
49+
* @param authorizationService the authorization service
50+
* @param tokenRevocationService the token revocation service
51+
*/
52+
public OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService,
53+
OAuth2TokenRevocationService tokenRevocationService) {
54+
Assert.notNull(authorizationService, "authorizationService cannot be null");
55+
Assert.notNull(tokenRevocationService, "tokenRevocationService cannot be null");
56+
this.authorizationService = authorizationService;
57+
this.tokenRevocationService = tokenRevocationService;
58+
}
59+
60+
@Override
61+
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
62+
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthenticationToken =
63+
(OAuth2TokenRevocationAuthenticationToken) authentication;
64+
65+
OAuth2ClientAuthenticationToken clientPrincipal = null;
66+
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(tokenRevocationAuthenticationToken.getPrincipal()
67+
.getClass())) {
68+
clientPrincipal = (OAuth2ClientAuthenticationToken) tokenRevocationAuthenticationToken.getPrincipal();
69+
}
70+
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) {
71+
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
72+
}
73+
74+
final RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
75+
final String tokenTypeHint = tokenRevocationAuthenticationToken.getTokenTypeHint();
76+
final String token = tokenRevocationAuthenticationToken.getToken();
77+
final OAuth2Authorization authorization = authorizationService.findByTokenAndTokenType(token,
78+
TokenType.ACCESS_TOKEN);
79+
80+
OAuth2TokenRevocationAuthenticationToken successfulAuthentication =
81+
new OAuth2TokenRevocationAuthenticationToken(token, registeredClient, tokenTypeHint);
82+
83+
if (authorization == null) {
84+
return successfulAuthentication;
85+
}
86+
87+
if (!registeredClient.getClientId().equals(authorization.getRegisteredClientId())) {
88+
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
89+
}
90+
91+
tokenRevocationService.revoke(token, TokenType.ACCESS_TOKEN);
92+
return successfulAuthentication;
93+
}
94+
95+
@Override
96+
public boolean supports(Class<?> authentication) {
97+
return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication);
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization.authentication;
17+
18+
import org.springframework.lang.Nullable;
19+
import org.springframework.security.authentication.AbstractAuthenticationToken;
20+
import org.springframework.security.core.Authentication;
21+
import org.springframework.security.oauth2.server.authorization.Version;
22+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
23+
import org.springframework.util.Assert;
24+
25+
import java.util.Collections;
26+
27+
/**
28+
* An {@link Authentication} implementation used for OAuth 2.0 Client Authentication.
29+
*
30+
* @author Vivek Babu
31+
* @since 0.0.1
32+
* @see AbstractAuthenticationToken
33+
* @see RegisteredClient
34+
* @see OAuth2TokenRevocationAuthenticationProvider
35+
*/
36+
public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken {
37+
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
38+
private final String tokenTypeHint;
39+
private Authentication clientPrincipal;
40+
private String token;
41+
private RegisteredClient registeredClient;
42+
43+
public OAuth2TokenRevocationAuthenticationToken(String token,
44+
Authentication clientPrincipal, @Nullable String tokenTypeHint) {
45+
super(Collections.emptyList());
46+
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
47+
Assert.hasText(token, "token cannot be empty");
48+
this.token = token;
49+
this.clientPrincipal = clientPrincipal;
50+
this.tokenTypeHint = tokenTypeHint;
51+
}
52+
53+
public OAuth2TokenRevocationAuthenticationToken(String token,
54+
RegisteredClient registeredClient, @Nullable String tokenTypeHint) {
55+
super(Collections.emptyList());
56+
Assert.notNull(registeredClient, "registeredClient cannot be null");
57+
Assert.hasText(token, "token cannot be empty");
58+
this.token = token;
59+
this.registeredClient = registeredClient;
60+
this.tokenTypeHint = tokenTypeHint;
61+
setAuthenticated(true);
62+
}
63+
64+
@Override
65+
public Object getPrincipal() {
66+
return this.clientPrincipal != null ? this.clientPrincipal : this.registeredClient
67+
.getClientId();
68+
}
69+
70+
@Override
71+
public Object getCredentials() {
72+
return "";
73+
}
74+
75+
/**
76+
* Returns the token.
77+
*
78+
* @return the token
79+
*/
80+
public String getToken() {
81+
return this.token;
82+
}
83+
84+
/**
85+
* Returns the token type hint.
86+
*
87+
* @return the token type hint
88+
*/
89+
public String getTokenTypeHint() {
90+
return tokenTypeHint;
91+
}
92+
93+
/**
94+
* Returns the {@link RegisteredClient registered client}.
95+
*
96+
* @return the {@link RegisteredClient}
97+
*/
98+
public @Nullable
99+
RegisteredClient getRegisteredClient() {
100+
return this.registeredClient;
101+
}
102+
}

0 commit comments

Comments
 (0)