[CXF-6692] Preparing Default Ehcache provider to save jose token reps only if preferred, test to follow
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/279f7e6b Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/279f7e6b Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/279f7e6b Branch: refs/heads/master-jaxrs-2.1 Commit: 279f7e6b8e9775d3300a661c915580754055d0c7 Parents: 833386f Author: Sergey Beryozkin <[email protected]> Authored: Thu Aug 11 16:57:44 2016 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Thu Aug 11 16:57:44 2016 +0100 ---------------------------------------------------------------------- .../oauth2/filters/JwtAccessTokenValidator.java | 113 +++++++++++++++ .../filters/LocalJwtAccessTokenValidator.java | 113 --------------- .../provider/AbstractOAuthDataProvider.java | 10 +- .../DefaultEHCacheOAuthDataProvider.java | 37 ++++- .../jwt/AbstactJwtAccessTokenValidator.java | 59 -------- .../oauth2/tokens/jwt/JwtAccessTokenUtils.java | 143 ------------------- .../oauth2/utils/JwtAccessTokenUtils.java | 108 ++++++++++++++ .../security/oauth2/utils/OAuthConstants.java | 2 + .../oauth2/filters/filters-serverJwt.xml | 2 +- 9 files changed, 264 insertions(+), 323 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/JwtAccessTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/JwtAccessTokenValidator.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/JwtAccessTokenValidator.java new file mode 100644 index 0000000..252bed7 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/JwtAccessTokenValidator.java @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cxf.rs.security.oauth2.filters; + +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.common.AccessTokenValidation; +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.provider.AccessTokenValidator; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; + +public class JwtAccessTokenValidator extends JoseJwtConsumer implements AccessTokenValidator { + + public List<String> getSupportedAuthorizationSchemes() { + return Collections.singletonList(OAuthConstants.BEARER_AUTHORIZATION_SCHEME); + } + + public AccessTokenValidation validateAccessToken(MessageContext mc, + String authScheme, + String authSchemeData, + MultivaluedMap<String, String> extraProps) + throws OAuthServiceException { + try { + JwtToken token = super.getJwtToken(authSchemeData); + return convertClaimsToValidation(token.getClaims()); + } catch (Exception ex) { + throw new OAuthServiceException(ex); + } + } + + + private AccessTokenValidation convertClaimsToValidation(JwtClaims claims) { + AccessTokenValidation atv = new AccessTokenValidation(); + atv.setInitialValidationSuccessful(true); + if (claims.getAudience() != null) { + atv.setClientId(claims.getAudience()); + } + if (claims.getIssuedAt() != null) { + atv.setTokenIssuedAt(claims.getIssuedAt()); + } else { + atv.setTokenIssuedAt(new Date().getTime()); + } + if (claims.getExpiryTime() != null) { + atv.setTokenLifetime(claims.getExpiryTime() - atv.getTokenIssuedAt()); + } + Object resourceAud = claims.getClaim(OAuthConstants.RESOURCE_INDICATOR); + if (resourceAud != null) { + List<String> auds = null; + if (resourceAud instanceof List) { + auds = CastUtils.cast((List<?>)resourceAud); + } else { + auds = Collections.singletonList((String)resourceAud); + } + atv.setAudiences(auds); + } + if (claims.getIssuer() != null) { + atv.setTokenIssuer(claims.getIssuer()); + } + Object scope = claims.getClaim(OAuthConstants.SCOPE); + if (scope != null) { + String[] scopes = scope instanceof String + ? scope.toString().split(" ") : CastUtils.cast((List<?>)scope).toArray(new String[]{}); + List<OAuthPermission> perms = new LinkedList<OAuthPermission>(); + for (String s : scopes) { + if (!StringUtils.isEmpty(s)) { + perms.add(new OAuthPermission(s.trim())); + } + } + atv.setTokenScopes(perms); + } + String username = (String)claims.getClaim("preferred_username"); + if (username != null) { + UserSubject userSubject = new UserSubject(username); + if (claims.getSubject() != null) { + userSubject.setId(claims.getSubject()); + } + atv.setTokenSubject(userSubject); + } else if (claims.getSubject() != null) { + atv.setTokenSubject(new UserSubject(claims.getSubject())); + } + return atv; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java deleted file mode 100644 index afabf56..0000000 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.cxf.rs.security.oauth2.filters; - -import java.util.Collections; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -import javax.ws.rs.core.MultivaluedMap; - -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.helpers.CastUtils; -import org.apache.cxf.jaxrs.ext.MessageContext; -import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; -import org.apache.cxf.rs.security.jose.jwt.JwtClaims; -import org.apache.cxf.rs.security.jose.jwt.JwtToken; -import org.apache.cxf.rs.security.oauth2.common.AccessTokenValidation; -import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; -import org.apache.cxf.rs.security.oauth2.common.UserSubject; -import org.apache.cxf.rs.security.oauth2.provider.AccessTokenValidator; -import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; -import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; - -public class LocalJwtAccessTokenValidator extends JoseJwtConsumer implements AccessTokenValidator { - - public List<String> getSupportedAuthorizationSchemes() { - return Collections.singletonList(OAuthConstants.BEARER_AUTHORIZATION_SCHEME); - } - - public AccessTokenValidation validateAccessToken(MessageContext mc, - String authScheme, - String authSchemeData, - MultivaluedMap<String, String> extraProps) - throws OAuthServiceException { - try { - JwtToken token = super.getJwtToken(authSchemeData); - return convertClaimsToValidation(token.getClaims()); - } catch (Exception ex) { - throw new OAuthServiceException(ex); - } - } - - - private AccessTokenValidation convertClaimsToValidation(JwtClaims claims) { - AccessTokenValidation atv = new AccessTokenValidation(); - atv.setInitialValidationSuccessful(true); - if (claims.getAudience() != null) { - atv.setClientId(claims.getAudience()); - } - if (claims.getIssuedAt() != null) { - atv.setTokenIssuedAt(claims.getIssuedAt()); - } else { - atv.setTokenIssuedAt(new Date().getTime()); - } - if (claims.getExpiryTime() != null) { - atv.setTokenLifetime(claims.getExpiryTime() - atv.getTokenIssuedAt()); - } - Object resourceAud = claims.getClaim("resource"); - if (resourceAud != null) { - List<String> auds = null; - if (resourceAud instanceof List) { - auds = CastUtils.cast((List<?>)resourceAud); - } else { - auds = Collections.singletonList((String)resourceAud); - } - atv.setAudiences(auds); - } - if (claims.getIssuer() != null) { - atv.setTokenIssuer(claims.getIssuer()); - } - Object scope = claims.getClaim("scope"); - if (scope != null) { - String[] scopes = scope instanceof String - ? scope.toString().split(" ") : CastUtils.cast((List<?>)scope).toArray(new String[]{}); - List<OAuthPermission> perms = new LinkedList<OAuthPermission>(); - for (String s : scopes) { - if (!StringUtils.isEmpty(s)) { - perms.add(new OAuthPermission(s.trim())); - } - } - atv.setTokenScopes(perms); - } - String username = (String)claims.getClaim("preferred_username"); - if (username != null) { - UserSubject userSubject = new UserSubject(username); - if (claims.getSubject() != null) { - userSubject.setId(claims.getSubject()); - } - atv.setTokenSubject(userSubject); - } else if (claims.getSubject() != null) { - atv.setTokenSubject(new UserSubject(claims.getSubject())); - } - return atv; - } - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java index dbbb167..ec8ead5 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java @@ -117,11 +117,11 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl // OAuth2 resource indicators (resource server audience) if (!at.getAudiences().isEmpty()) { List<String> resourceAudiences = at.getAudiences(); - claims.setClaim("resource", + claims.setClaim(OAuthConstants.RESOURCE_INDICATOR, resourceAudiences.size() == 1 ? resourceAudiences.get(0) : resourceAudiences); } - for (Map.Entry<String, String> entry : at.getExtraProperties().entrySet()) { - claims.setClaim(entry.getKey(), entry.getValue()); + if (!at.getExtraProperties().isEmpty()) { + claims.setClaim("extra_properties", at.getExtraProperties()); } // Can be used to check at RS/etc which grant was used to get this token issued if (at.getGrantType() != null) { @@ -131,12 +131,12 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl // (and is no longer valid) when this token was issued; relevant only if the authorization // code flow was used if (at.getGrantCode() != null) { - claims.setClaim("grant_code", at.getGrantType()); + claims.setClaim(OAuthConstants.AUTHORIZATION_CODE_GRANT, at.getGrantCode()); } // Can be used to link the clients (especially public ones) to this token // to have a knowledge which client instance is using this token - might be handy at the RS/etc if (at.getClientCodeVerifier() != null) { - claims.setClaim("code_verifier", at.getClientCodeVerifier()); + claims.setClaim(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, at.getClientCodeVerifier()); } // ServerAccessToken 'nonce' property, if available, can be ignored for the purpose for persisting it // further as a JWT claim - as it is only used once by (OIDC) IdTokenResponseFilter http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java index 7d376ed..c49e2ef 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java @@ -36,11 +36,13 @@ import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; 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.common.UserSubject; import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken; import org.apache.cxf.rs.security.oauth2.utils.EHCacheUtil; +import org.apache.cxf.rs.security.oauth2.utils.JwtAccessTokenUtils; public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { public static final String CLIENT_CACHE_KEY = "cxf.oauth2.client.cache"; @@ -52,6 +54,8 @@ public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { private Ehcache clientCache; private Ehcache accessTokenCache; private Ehcache refreshTokenCache; + private boolean storeJwtTokenKeyOnly; + private JoseJwtConsumer jwtTokenConsumer; public DefaultEHCacheOAuthDataProvider() { this(DEFAULT_CONFIG_URL, BusFactory.getThreadDefaultBus(true)); @@ -124,8 +128,19 @@ public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { @Override public ServerAccessToken getAccessToken(String accessToken) throws OAuthServiceException { - return getCacheValue(accessTokenCache, accessToken, ServerAccessToken.class); + ServerAccessToken at = null; + if (isUseJwtFormatForAccessTokens() && isStoreJwtTokenKeyOnly()) { + String jose = getCacheValue(accessTokenCache, accessToken, String.class); + if (jose != null) { + JoseJwtConsumer theConsumer = jwtTokenConsumer == null ? new JoseJwtConsumer() : jwtTokenConsumer; + at = JwtAccessTokenUtils.createAccessTokenFromJwt(theConsumer, jose, this); + } + } else { + at = getCacheValue(accessTokenCache, accessToken, ServerAccessToken.class); + } + return at; } + @Override protected void doRevokeAccessToken(ServerAccessToken at) { accessTokenCache.remove(at.getTokenKey()); @@ -140,7 +155,13 @@ public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { } protected void saveAccessToken(ServerAccessToken serverToken) { - putCacheValue(accessTokenCache, serverToken.getTokenKey(), serverToken, serverToken.getExpiresIn()); + Object accessTokenObject = null; + if (isUseJwtFormatForAccessTokens() && isStoreJwtTokenKeyOnly()) { + accessTokenObject = serverToken.getTokenKey(); + } else { + accessTokenObject = serverToken; + } + putCacheValue(accessTokenCache, serverToken.getTokenKey(), accessTokenObject, serverToken.getExpiresIn()); } protected void saveRefreshToken(RefreshToken refreshToken) { @@ -218,4 +239,16 @@ public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { cacheManager.shutdown(); } + public boolean isStoreJwtTokenKeyOnly() { + return storeJwtTokenKeyOnly; + } + + public void setStoreJwtTokenKeyOnly(boolean storeJwtTokenKeyOnly) { + this.storeJwtTokenKeyOnly = storeJwtTokenKeyOnly; + } + + public void setJwtTokenConsumer(JoseJwtConsumer jwtTokenConsumer) { + this.jwtTokenConsumer = jwtTokenConsumer; + } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/AbstactJwtAccessTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/AbstactJwtAccessTokenValidator.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/AbstactJwtAccessTokenValidator.java deleted file mode 100644 index cd7fdb6..0000000 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/AbstactJwtAccessTokenValidator.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.cxf.rs.security.oauth2.tokens.jwt; - -import java.util.Collections; -import java.util.List; - -import javax.ws.rs.core.MultivaluedMap; - -import org.apache.cxf.jaxrs.ext.MessageContext; -import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; -import org.apache.cxf.rs.security.oauth2.common.AccessTokenValidation; -import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; -import org.apache.cxf.rs.security.oauth2.provider.AccessTokenValidator; -import org.apache.cxf.rs.security.oauth2.provider.OAuthDataProvider; -import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; - -public abstract class AbstactJwtAccessTokenValidator extends JoseJwtConsumer - implements AccessTokenValidator { - private OAuthDataProvider dataProvider; - - @Override - public List<String> getSupportedAuthorizationSchemes() { - return Collections.singletonList("*"); - } - - @Override - public AccessTokenValidation validateAccessToken(MessageContext mc, - String authScheme, - String authSchemeData, - MultivaluedMap<String, String> extraProps) - throws OAuthServiceException { - ServerAccessToken at = dataProvider.getAccessToken(authSchemeData); - super.getJwtToken(at.getTokenKey()); - return new AccessTokenValidation(at); - } - - public void setDataProvider(OAuthDataProvider dataProvider) { - this.dataProvider = dataProvider; - } - - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/JwtAccessTokenUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/JwtAccessTokenUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/JwtAccessTokenUtils.java deleted file mode 100644 index 76d371f..0000000 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/tokens/jwt/JwtAccessTokenUtils.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.cxf.rs.security.oauth2.tokens.jwt; - -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.util.List; - -import javax.crypto.SecretKey; - -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; -import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; -import org.apache.cxf.rs.security.jose.jwe.JweDecryption; -import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; -import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; -import org.apache.cxf.rs.security.jose.jwe.JweUtils; -import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; -import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; -import org.apache.cxf.rs.security.jose.jws.NoneJwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jwt.JwtClaims; -import org.apache.cxf.rs.security.jose.jwt.JwtToken; -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.tokens.bearer.BearerAccessToken; - -public final class JwtAccessTokenUtils { - private JwtAccessTokenUtils() { - - } - public static ServerAccessToken encryptToAccessToken(JwtToken jwt, - Client client, - SecretKey key) { - JweEncryptionProvider jweEncryption = - JweUtils.getDirectKeyJweEncryption(key, ContentAlgorithm.A128GCM); - return encryptToAccessToken(jwt, client, jweEncryption); - - } - public static ServerAccessToken encryptToAccessToken(JwtToken jwt, - Client client, - JweEncryptionProvider jweEncryption) { - String jwtString = new JwsJwtCompactProducer(jwt) - .signWith(new NoneJwsSignatureProvider()); - String tokenId = jweEncryption.encrypt(getBytes(jwtString), null); - return toAccessToken(jwt, client, tokenId); - } - private static ServerAccessToken toAccessToken(JwtToken jwt, - Client client, - String tokenId) { - JwtClaims claims = jwt.getClaims(); - validateJwtSubjectAndAudience(claims, client); - Long issuedAt = claims.getIssuedAt(); - Long notBefore = claims.getNotBefore(); - Long expiresIn = notBefore - issuedAt; - - return new BearerAccessToken(client, tokenId, issuedAt, expiresIn); - } - public static JwtToken decryptFromfromAccessToken(String tokenId, SecretKey key) { - JweDecryption jweDecryption = JweUtils.getDirectKeyJweDecryption(key, ContentAlgorithm.A128GCM); - return decryptFromAccessToken(tokenId, jweDecryption); - } - public static JwtToken decryptFromAccessToken(String tokenId, JweDecryptionProvider jweDecryption) { - String decrypted = jweDecryption.decrypt(tokenId).getContentText(); - JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(decrypted); - return consumer.getJwtToken(); - } - public static ServerAccessToken signToAccessToken(JwtToken jwt, - Client client, - RSAPrivateKey key) { - JwsSignatureProvider jws = - JwsUtils.getPrivateKeySignatureProvider(key, SignatureAlgorithm.RS256); - return signToAccessToken(jwt, client, jws); - - } - public static ServerAccessToken signToAccessToken(JwtToken jwt, - Client client, - JwsSignatureProvider jws) { - String jwtString = new JwsJwtCompactProducer(jwt).signWith(jws); - return toAccessToken(jwt, client, jwtString); - } - public static JwtToken verifyAccessToken(String tokenId, RSAPublicKey key) { - JwsSignatureVerifier jws = JwsUtils.getPublicKeySignatureVerifier(key, - SignatureAlgorithm.RS256); - return verifyAccessToken(tokenId, jws); - } - public static JwtToken verifyAccessToken(String tokenId, JwsSignatureVerifier jws) { - JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(tokenId); - if (consumer.verifySignatureWith(jws)) { - return consumer.getJwtToken(); - } else { - throw new SecurityException(); - } - } - - private static void validateJwtSubjectAndAudience(JwtClaims claims, Client c) { - if (claims.getSubject() == null || !claims.getSubject().equals(c.getClientId())) { - throw new SecurityException("Invalid subject"); - } - // validate audience - List<String> audiences = claims.getAudiences(); - if (audiences.isEmpty()) { - throw new SecurityException("Invalid audience"); - } - - if (!c.getRegisteredAudiences().isEmpty()) { - boolean match = false; - for (String audience : audiences) { - if (c.getRegisteredAudiences().contains(audience)) { - match = true; - break; - } - } - if (!match) { - throw new SecurityException("Invalid audience"); - } - } - // TODO: the issuer is indirectly validated by validating the signature - // but an extra check can be done - } - - private static byte[] getBytes(String str) { - return StringUtils.toBytesUTF8(str); - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/JwtAccessTokenUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/JwtAccessTokenUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/JwtAccessTokenUtils.java new file mode 100644 index 0000000..746403b --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/JwtAccessTokenUtils.java @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cxf.rs.security.oauth2.utils; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.provider.ClientRegistrationProvider; +import org.apache.cxf.rs.security.oauth2.tokens.bearer.BearerAccessToken; + +public final class JwtAccessTokenUtils { + private JwtAccessTokenUtils() { + + } + + public static ServerAccessToken createAccessTokenFromJwt(JoseJwtConsumer consumer, + String jose, + ClientRegistrationProvider clientProvider) { + JwtClaims claims = consumer.getJwtToken(jose).getClaims(); + + Client c = clientProvider.getClient(claims.getStringProperty(JwtConstants.CLAIM_AUDIENCE)); + long issuedAt = claims.getLongProperty(JwtConstants.CLAIM_ISSUED_AT); + long lifetime = claims.getLongProperty(JwtConstants.CLAIM_EXPIRY) - issuedAt; + BearerAccessToken at = new BearerAccessToken(c, jose, lifetime, issuedAt); + + Object resourceAud = claims.getClaim(OAuthConstants.RESOURCE_INDICATOR); + if (resourceAud != null) { + List<String> auds = null; + if (resourceAud instanceof List) { + auds = CastUtils.cast((List<?>)resourceAud); + } else { + auds = Collections.singletonList((String)resourceAud); + } + at.setAudiences(auds); + } + String issuer = claims.getStringProperty(JwtConstants.CLAIM_ISSUER); + if (issuer != null) { + at.setIssuer(issuer); + } + Object scope = claims.getClaim(OAuthConstants.SCOPE); + if (scope != null) { + String[] scopes = scope instanceof String + ? scope.toString().split(" ") : CastUtils.cast((List<?>)scope).toArray(new String[]{}); + List<OAuthPermission> perms = new LinkedList<OAuthPermission>(); + for (String s : scopes) { + if (!StringUtils.isEmpty(s)) { + perms.add(new OAuthPermission(s.trim())); + } + } + at.setScopes(perms); + } + String username = claims.getStringProperty("preferred_username"); + String subject = claims.getStringProperty(JwtConstants.CLAIM_SUBJECT); + if (username != null) { + UserSubject userSubject = new UserSubject(username); + if (subject != null) { + userSubject.setId(subject); + } + at.setSubject(userSubject); + } else if (subject != null) { + at.setSubject(new UserSubject(subject)); + } + + String grantType = claims.getStringProperty(OAuthConstants.GRANT_TYPE); + if (grantType != null) { + at.setGrantType(grantType); + } + String grantCode = claims.getStringProperty("grant_code"); + if (grantCode != null) { + at.setGrantCode(grantCode); + } + + Map<String, String> extraProperties = CastUtils.cast((Map<?, ?>)claims.getClaim("extra_propertirs")); + if (extraProperties != null) { + at.getExtraProperties().putAll(extraProperties); + } + + + return at; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index 71d517c..635c016 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -27,6 +27,8 @@ public final class OAuthConstants { public static final String CLIENT_ID = "client_id"; public static final String CLIENT_SECRET = "client_secret"; public static final String CLIENT_AUDIENCE = "audience"; + public static final String RESOURCE_INDICATOR = "resource"; + public static final String NONCE = "nonce"; public static final String REDIRECT_URI = "redirect_uri"; http://git-wip-us.apache.org/repos/asf/cxf/blob/279f7e6b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml index 9ef8099..c9dd49b 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml @@ -104,7 +104,7 @@ under the License. </jaxrs:providers> </jaxrs:server> - <bean id="localTokenValidator" class="org.apache.cxf.rs.security.oauth2.filters.LocalJwtAccessTokenValidator"/> + <bean id="localTokenValidator" class="org.apache.cxf.rs.security.oauth2.filters.JwtAccessTokenValidator"/> <bean id="oAuthFilterLocalValidation" class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter"> <property name="tokenValidator" ref="localTokenValidator"/>
