You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2019/11/06 18:16:59 UTC
[cxf-fediz] branch master updated: Porting Token Revocation changes
to Fediz
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git
The following commit(s) were added to refs/heads/master by this push:
new a170985 Porting Token Revocation changes to Fediz
a170985 is described below
commit a17098506cc1bb70196b8ab92f5c42c2ffc38c91
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Wed Nov 6 18:16:29 2019 +0000
Porting Token Revocation changes to Fediz
---
.../fediz/service/oidc/OAuthDataProviderImpl.java | 100 +++++++++++++
.../cxf/fediz/systests/oidc/AbstractOIDCTest.java | 160 ++++++++++++++++++++-
2 files changed, 258 insertions(+), 2 deletions(-)
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java
index 67b6d5c..0cbc666 100644
--- a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java
@@ -24,9 +24,12 @@ import java.util.List;
import java.util.Set;
import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.code.JCacheCodeDataProvider;
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
public class OAuthDataProviderImpl extends JCacheCodeDataProvider {
@@ -42,4 +45,101 @@ public class OAuthDataProviderImpl extends JCacheCodeDataProvider {
throw new OAuthServiceException("Required scopes are missing");
}
}
+
+ //
+ // BEGIN - TODO This can be removed once we pick up CXF 3.3.5
+ //
+
+ @Override
+ public ServerAccessToken refreshAccessToken(Client client, String refreshTokenKey,
+ List<String> restrictedScopes) throws OAuthServiceException {
+ RefreshToken currentRefreshToken = isRecycleRefreshTokens()
+ ? revokeRefreshToken(client, refreshTokenKey) : getRefreshToken(refreshTokenKey);
+ if (currentRefreshToken == null) {
+ throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED);
+ }
+ if (OAuthUtils.isExpired(currentRefreshToken.getIssuedAt(), currentRefreshToken.getExpiresIn())) {
+ if (!isRecycleRefreshTokens()) {
+ revokeRefreshToken(client, refreshTokenKey);
+ }
+ throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED);
+ }
+ if (isRecycleRefreshTokens()) {
+ revokeAccessTokens(client, currentRefreshToken);
+ }
+
+ ServerAccessToken at = doRefreshAccessToken(client, currentRefreshToken, restrictedScopes);
+ saveAccessToken(at);
+ if (isRecycleRefreshTokens()) {
+ createNewRefreshToken(at);
+ } else {
+ updateExistingRefreshToken(currentRefreshToken, at);
+ }
+ return at;
+ }
+
+ @Override
+ public void revokeToken(Client client, String tokenKey, String tokenTypeHint) throws OAuthServiceException {
+ ServerAccessToken accessToken = null;
+ if (!OAuthConstants.REFRESH_TOKEN.equals(tokenTypeHint)) {
+ accessToken = revokeAccessToken(client, tokenKey);
+ }
+ if (accessToken != null) {
+ handleLinkedRefreshToken(client, accessToken);
+ } else if (!OAuthConstants.ACCESS_TOKEN.equals(tokenTypeHint)) {
+ RefreshToken currentRefreshToken = revokeRefreshToken(client, tokenKey);
+ revokeAccessTokens(client, currentRefreshToken);
+ }
+ }
+
+ protected void handleLinkedRefreshToken(Client client, ServerAccessToken accessToken) {
+ if (accessToken != null && accessToken.getRefreshToken() != null) {
+ RefreshToken rt = getRefreshToken(accessToken.getRefreshToken());
+ if (rt == null) {
+ return;
+ }
+
+ unlinkRefreshAccessToken(rt, accessToken.getTokenKey());
+ if (rt.getAccessTokens().isEmpty()) {
+ revokeRefreshToken(client, rt.getTokenKey());
+ } else {
+ saveRefreshToken(rt);
+ }
+ }
+
+ }
+
+ protected void revokeAccessTokens(Client client, RefreshToken currentRefreshToken) {
+ if (currentRefreshToken != null) {
+ for (String accessTokenKey : currentRefreshToken.getAccessTokens()) {
+ revokeAccessToken(client, accessTokenKey);
+ }
+ }
+ }
+
+ protected ServerAccessToken revokeAccessToken(Client client, String accessTokenKey) {
+ ServerAccessToken at = getAccessToken(accessTokenKey);
+ if (at != null) {
+ if (!at.getClient().getClientId().equals(client.getClientId())) {
+ throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+ }
+ doRevokeAccessToken(at);
+ }
+ return at;
+ }
+
+ protected RefreshToken revokeRefreshToken(Client client, String refreshTokenKey) {
+ RefreshToken refreshToken = getRefreshToken(refreshTokenKey);
+ if (refreshToken != null) {
+ if (!refreshToken.getClient().getClientId().equals(client.getClientId())) {
+ throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+ }
+ doRevokeRefreshToken(refreshToken);
+ }
+ return refreshToken;
+ }
+
+ //
+ // END
+ //
}
diff --git a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
index aa5283d..ffdd055 100644
--- a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
+++ b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
@@ -962,6 +962,158 @@ abstract class AbstractOIDCTest {
webClient2.close();
}
+ @org.junit.Test
+ public void testAccessTokenRevocation() throws Exception {
+
+ String url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/idp/authorize"
+ + "?client_id=" + storedClientId
+ + "&response_type=code"
+ + "&scope=openid";
+ String user = "alice";
+ String password = "ecila";
+
+ // Login to the OIDC token endpoint + get the authorization code
+ WebClient webClient = setupWebClient(user, password);
+ String authorizationCode = loginAndGetAuthorizationCode(url, webClient);
+ assertNotNull(authorizationCode);
+
+ // Now use the code to get an IdToken
+
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/token";
+ WebRequest request = new WebRequest(new URL(url), HttpMethod.POST);
+
+ request.setRequestParameters(Arrays.asList(
+ new NameValuePair("client_id", storedClientId),
+ new NameValuePair("grant_type", "authorization_code"),
+ new NameValuePair("code", authorizationCode)));
+
+ WebClient webClient2 = setupWebClient("", "");
+ String data = storedClientId + ":" + storedClientPassword;
+ String authorizationHeader = "Basic "
+ + Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
+ webClient2.addRequestHeader("Authorization", authorizationHeader);
+ final UnexpectedPage responsePage = webClient2.getPage(request);
+ String response = responsePage.getWebResponse().getContentAsString();
+
+ // Check the IdToken
+ String idToken = getIdToken(response);
+ assertNotNull(idToken);
+ validateIdToken(idToken, storedClientId);
+
+ // Get the access token
+ String accessToken = parseToken(response, "access_token");
+ assertNotNull(accessToken);
+
+ // Introspect the token and check it's valid
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/introspect";
+ WebRequest introspectionRequest = new WebRequest(new URL(url), HttpMethod.POST);
+ introspectionRequest.setRequestParameters(Arrays.asList(
+ new NameValuePair("token", accessToken)));
+
+ UnexpectedPage introspectionResponsePage = webClient2.getPage(introspectionRequest);
+ String introspectionResponse = introspectionResponsePage.getWebResponse().getContentAsString();
+
+ assertTrue(introspectionResponse.contains("\"active\":true"));
+
+ // Now revoke the token
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/revoke";
+ WebRequest revocationRequest = new WebRequest(new URL(url), HttpMethod.POST);
+ revocationRequest.setRequestParameters(Arrays.asList(
+ new NameValuePair("token", accessToken)));
+
+ webClient2.getPage(revocationRequest);
+
+ // Now introspect the token again and check it's not valid
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/introspect";
+
+ introspectionResponsePage = webClient2.getPage(introspectionRequest);
+ introspectionResponse = introspectionResponsePage.getWebResponse().getContentAsString();
+
+ assertTrue(introspectionResponse.contains("\"active\":false"));
+
+ webClient.close();
+ webClient2.close();
+ }
+
+ @org.junit.Test
+ public void testAccessTokenRevocationWrongClient() throws Exception {
+
+ String url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/idp/authorize"
+ + "?client_id=" + storedClientId
+ + "&response_type=code"
+ + "&scope=openid";
+ String user = "alice";
+ String password = "ecila";
+
+ // Login to the OIDC token endpoint + get the authorization code
+ WebClient webClient = setupWebClient(user, password);
+ String authorizationCode = loginAndGetAuthorizationCode(url, webClient);
+ assertNotNull(authorizationCode);
+
+ // Now use the code to get an IdToken
+
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/token";
+ WebRequest request = new WebRequest(new URL(url), HttpMethod.POST);
+
+ request.setRequestParameters(Arrays.asList(
+ new NameValuePair("client_id", storedClientId),
+ new NameValuePair("grant_type", "authorization_code"),
+ new NameValuePair("code", authorizationCode)));
+
+ WebClient webClient2 = setupWebClient("", "");
+ String data = storedClientId + ":" + storedClientPassword;
+ String authorizationHeader = "Basic "
+ + Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
+ webClient2.addRequestHeader("Authorization", authorizationHeader);
+ final UnexpectedPage responsePage = webClient2.getPage(request);
+ String response = responsePage.getWebResponse().getContentAsString();
+
+ // Check the IdToken
+ String idToken = getIdToken(response);
+ assertNotNull(idToken);
+ validateIdToken(idToken, storedClientId);
+
+ // Get the access token
+ String accessToken = parseToken(response, "access_token");
+ assertNotNull(accessToken);
+
+ // Introspect the token and check it's valid
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/introspect";
+ WebRequest introspectionRequest = new WebRequest(new URL(url), HttpMethod.POST);
+ introspectionRequest.setRequestParameters(Arrays.asList(
+ new NameValuePair("token", accessToken)));
+
+ UnexpectedPage introspectionResponsePage = webClient2.getPage(introspectionRequest);
+ String introspectionResponse = introspectionResponsePage.getWebResponse().getContentAsString();
+
+ assertTrue(introspectionResponse.contains("\"active\":true"));
+
+ // Now try to revoke the token as the other client
+ WebClient webClient3 = setupWebClient("", "");
+ String data2 = storedClient2Id + ":" + storedClient2Password;
+ String authorizationHeader2 = "Basic "
+ + Base64.getEncoder().encodeToString(data2.getBytes(StandardCharsets.UTF_8));
+ webClient3.addRequestHeader("Authorization", authorizationHeader2);
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/revoke";
+ WebRequest revocationRequest = new WebRequest(new URL(url), HttpMethod.POST);
+ revocationRequest.setRequestParameters(Arrays.asList(
+ new NameValuePair("token", accessToken)));
+
+ webClient3.getPage(revocationRequest);
+ webClient3.close();
+
+ // Now introspect the token again and check it's still valid
+ url = "https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/oauth2/introspect";
+
+ introspectionResponsePage = webClient2.getPage(introspectionRequest);
+ introspectionResponse = introspectionResponsePage.getWebResponse().getContentAsString();
+
+ assertTrue(introspectionResponse.contains("\"active\":true"));
+
+ webClient.close();
+ webClient2.close();
+ }
+
private static WebClient setupWebClient(String user, String password) {
final WebClient webClient = new WebClient();
webClient.getOptions().setUseInsecureSSL(true);
@@ -1025,9 +1177,13 @@ abstract class AbstractOIDCTest {
}
private static String getIdToken(String parentString) {
+ return parseToken(parentString, "id_token");
+ }
+
+ private static String parseToken(String parentString, String tag) {
String foundString =
- parentString.substring(parentString.indexOf("id_token")
- + "id_token\":\"".length());
+ parentString.substring(parentString.indexOf(tag)
+ + (tag + "\":\"").length());
int ampersandIndex = foundString.indexOf('"');
if (ampersandIndex < 1) {
ampersandIndex = foundString.length();