Refactored to use TokenEnhancer rather than a custom TokenGranter.

pull/105/merge
Amanda Anganes 2012-05-30 16:14:00 -04:00
parent 16aa0c59b5
commit 424f8bb737
5 changed files with 84 additions and 276 deletions

View File

@ -41,6 +41,7 @@ import org.springframework.security.oauth2.common.exceptions.InvalidClientExcept
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -69,6 +70,9 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
@Autowired
private OAuth2RefreshTokenEntityFactory refreshTokenFactory;
@Autowired
private TokenEnhancer tokenEnhancer;
@Override
public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) throws AuthenticationException, InvalidClientException {
if (authentication != null &&
@ -135,6 +139,8 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
token.setRefreshToken(refreshToken);
}
tokenEnhancer.enhance(token, authentication);
tokenRepository.saveAccessToken(token);
return token;
@ -336,6 +342,11 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
return this;
}
public DefaultOAuth2ProviderTokenServicesBuilder setTokenEnhancer(TokenEnhancer tokenEnhancer) {
instance.tokenEnhancer = tokenEnhancer;
return this;
}
public OAuth2TokenEntityService finish() {
return instance;
}
@ -362,6 +373,20 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
public OAuth2RefreshTokenEntity saveRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
return tokenRepository.saveRefreshToken(refreshToken);
}
/**
* @return the tokenEnhancer
*/
public TokenEnhancer getTokenEnhancer() {
return tokenEnhancer;
}
/**
* @param tokenEnhancer the tokenEnhancer to set
*/
public void setTokenEnhancer(TokenEnhancer tokenEnhancer) {
this.tokenEnhancer = tokenEnhancer;
}

View File

@ -1,260 +0,0 @@
/*******************************************************************************
* Copyright 2012 The MITRE Corporation
*
* 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
*
* 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.mitre.openid.connect.token;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.mitre.oauth2.service.impl.DefaultOAuth2ProviderTokenService;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.model.IdToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.AuthorizationRequestFactory;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationRequestHolder;
import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
/**
* AccessToken granter for Authorization Code flow.
*
* Note: does this need to be able to grant straight OAuth2.0 Access Tokens as
* well as Connect Access Tokens?
*
*
* @author AANGANES
*
*/
@Component
public class ConnectAuthCodeTokenGranter implements TokenGranter {
private static final String GRANT_TYPE = "authorization_code";
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthorizationRequestFactory authorizationRequestFactory;
@Autowired
private ConfigurationPropertiesBean configBean;
//TODO: Do we need to modify/update this?
@Autowired
private OAuth2TokenEntityService tokenServices;
@Autowired
private IdTokenGeneratorService idTokenService;
@Autowired
private JwtSigningAndValidationService jwtService;
/**
* Default empty constructor
*/
public ConnectAuthCodeTokenGranter() {
}
/**
* Constructor for unit tests
*
* @param tokenServices
* @param authorizationCodeServices
* @param clientDetailsService
*/
public ConnectAuthCodeTokenGranter(
DefaultOAuth2ProviderTokenService tokenServices,
AuthorizationCodeServices authorizationCodeServices,
ClientDetailsService clientDetailsService, AuthorizationRequestFactory authorizationRequestFactory) {
setTokenServices(tokenServices);
setAuthorizationCodeServices(authorizationCodeServices);
setAuthorizationRequestFactory(authorizationRequestFactory);
}
/**
* Grant an OpenID Connect Access Token
*
* @param grantType
* @param parameters
* @param clientId
* @param scope
*/
@Override
public OAuth2AccessToken grant(String grantType,
Map<String, String> parameters, String clientId, Set<String> scope) {
if (!GRANT_TYPE.equals(grantType)) {
return null;
}
String authorizationCode = parameters.get("code");
String redirectUri = parameters.get("redirect_uri");
if (authorizationCode == null) {
throw new OAuth2Exception("An authorization code must be supplied.");
}
AuthorizationRequestHolder storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
if (storedAuth == null) {
throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
}
AuthorizationRequest unconfirmedAuthorizationRequest = storedAuth.getAuthenticationRequest();
if (unconfirmedAuthorizationRequest.getRedirectUri() != null
&& !unconfirmedAuthorizationRequest.getRedirectUri().equals(redirectUri)) {
throw new RedirectMismatchException("Redirect URI mismatch.");
}
if (clientId != null && !clientId.equals(unconfirmedAuthorizationRequest.getClientId())) {
// just a sanity check.
throw new InvalidClientException("Client ID mismatch");
}
// From SECOAUTH: Secret is not required in the authorization request, so it won't be available
// in the unconfirmedAuthorizationCodeAuth. We do want to check that a secret is provided
// in the new request, but that happens elsewhere.
//Validate credentials
AuthorizationRequest authorizationRequest = authorizationRequestFactory.createAuthorizationRequest(parameters, clientId,
grantType, unconfirmedAuthorizationRequest.getScope());
if (authorizationRequest == null) {
return null;
}
Authentication userAuth = storedAuth.getUserAuthentication();
//TODO: should not need cast
OAuth2AccessTokenEntity token = (OAuth2AccessTokenEntity) tokenServices.createAccessToken(new OAuth2Authentication(authorizationRequest, userAuth));
token.getJwt().getClaims().setAudience(clientId);
token.getJwt().getClaims().setIssuer(configBean.getIssuer());
token.getJwt().getClaims().setIssuedAt(new Date());
// handle expiration
token.getJwt().getClaims().setExpiration(token.getExpiration());
jwtService.signJwt(token.getJwt());
/**
* Authorization request scope MUST include "openid", but access token request
* may or may not include the scope parameter. As long as the AuthorizationRequest
* has the proper scope, we can consider this a valid OpenID Connect request.
*/
if (authorizationRequest.getScope().contains("openid")) {
String userId = userAuth.getName();
IdToken idToken = idTokenService.generateIdToken(userId, configBean.getIssuer());
idToken.getClaims().setAudience(clientId);
idToken.getClaims().setIssuedAt(new Date());
idToken.getClaims().setIssuer(configBean.getIssuer());
String nonce = unconfirmedAuthorizationRequest.getAuthorizationParameters().get("nonce");
if (!Strings.isNullOrEmpty(nonce)) {
idToken.getClaims().setNonce(nonce);
}
// TODO: expiration? other fields?
//Sign
//TODO: check client to see if they have a preferred alg, attempt to use that
jwtService.signJwt(idToken);
token.setIdToken(idToken);
}
tokenServices.saveAccessToken(token);
return token;
}
/**
* @return the authorizationCodeServices
*/
public AuthorizationCodeServices getAuthorizationCodeServices() {
return authorizationCodeServices;
}
/**
* @param authorizationCodeServices the authorizationCodeServices to set
*/
public void setAuthorizationCodeServices(AuthorizationCodeServices authorizationCodeServices) {
this.authorizationCodeServices = authorizationCodeServices;
}
public AuthorizationRequestFactory getAuthorizationRequestFactory() {
return this.authorizationRequestFactory;
}
public void setAuthorizationRequestFactory(AuthorizationRequestFactory authorizationRequestFactory) {
this.authorizationRequestFactory = authorizationRequestFactory;
}
public OAuth2TokenEntityService getTokenServices() {
return tokenServices;
}
public void setTokenServices(OAuth2TokenEntityService tokenServices) {
this.tokenServices = tokenServices;
}
public ConfigurationPropertiesBean getConfigBean() {
return configBean;
}
public void setConfigBean(ConfigurationPropertiesBean configBean) {
this.configBean = configBean;
}
public IdTokenGeneratorService getIdTokenService() {
return idTokenService;
}
public void setIdTokenService(IdTokenGeneratorService idTokenService) {
this.idTokenService = idTokenService;
}
public JwtSigningAndValidationService getJwtService() {
return jwtService;
}
public void setJwtService(JwtSigningAndValidationService jwtService) {
this.jwtService = jwtService;
}
}

View File

@ -1,27 +1,40 @@
/*******************************************************************************
* Copyright 2012 The MITRE Corporation
*
* 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
*
* 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.mitre.openid.connect.token;
import java.util.Date;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.model.IdToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Service;
import com.google.common.base.Strings;
@Service
public class ConnectTokenEnhancer implements TokenEnhancer {
@Autowired
private ConfigurationPropertiesBean configBean;
@Autowired
private OAuth2TokenEntityService tokenServices;
@Autowired
private IdTokenGeneratorService idTokenService;
@ -33,15 +46,17 @@ public class ConnectTokenEnhancer implements TokenEnhancer {
OAuth2AccessTokenEntity token = (OAuth2AccessTokenEntity) accessToken;
String clientId = "";
String clientId = authentication.getAuthorizationRequest().getClientId();
token.getJwt().getClaims().setAudience(clientId);
token.getJwt().getClaims().setIssuer(configBean.getIssuer());
token.getJwt().getClaims().setIssuedAt(new Date());
// handle expiration
token.getJwt().getClaims().setExpiration(token.getExpiration());
//TODO: check for client's preferred signer alg and use that
jwtService.signJwt(token.getJwt());
/**
@ -58,22 +73,43 @@ public class ConnectTokenEnhancer implements TokenEnhancer {
idToken.getClaims().setIssuedAt(new Date());
idToken.getClaims().setIssuer(configBean.getIssuer());
String nonce = authentication.getAuthorizationRequest().getParameters().get("nonce");
String nonce = authentication.getAuthorizationRequest().getAuthorizationParameters().get("nonce");
if (!Strings.isNullOrEmpty(nonce)) {
idToken.getClaims().setNonce(nonce);
}
// TODO: expiration? other fields?
//Sign
//TODO: check client to see if they have a preferred alg, attempt to use that
//TODO: check for client's preferred signer alg and use that
jwtService.signJwt(idToken);
token.setIdToken(idToken);
}
tokenServices.saveAccessToken(token);
return token;
}
public ConfigurationPropertiesBean getConfigBean() {
return configBean;
}
public void setConfigBean(ConfigurationPropertiesBean configBean) {
this.configBean = configBean;
}
public IdTokenGeneratorService getIdTokenService() {
return idTokenService;
}
public void setIdTokenService(IdTokenGeneratorService idTokenService) {
this.idTokenService = idTokenService;
}
public JwtSigningAndValidationService getJwtService() {
return jwtService;
}
public void setJwtService(JwtSigningAndValidationService jwtService) {
this.jwtService = jwtService;
}
}

View File

@ -44,9 +44,16 @@
<property name="realmName" value="openidconnect" />
</bean>
<!-- SECOAUTH Authorization Server, with our custom token granter plugged in -->
<!-- Now using default SECOAUTH token granter -->
<bean id="authCodeTokenGranter" class="org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter">
<constructor-arg name="tokenServices" ref="defaultOAuth2ProviderTokenService"/>
<constructor-arg name="authorizationRequestFactory" ref="authorizationRequestFactory"/>
<constructor-arg name="authorizationCodeServices" ref="authCodeServices"/>
</bean>
<!-- SECOAUTH Authorization Server -->
<oauth:authorization-server client-details-service-ref="defaultOAuth2ClientDetailsEntityService"
token-services-ref="defaultOAuth2ProviderTokenService" token-granter-ref="connectAuthCodeTokenGranter"
token-services-ref="defaultOAuth2ProviderTokenService" token-granter-ref="authCodeTokenGranter"
user-approval-handler-ref="userApprovalHandler" authorization-endpoint-url="/openidconnect/auth" token-endpoint-url="/openidconnect/token">
<oauth:authorization-code authorization-code-services-ref="authCodeServices" />
</oauth:authorization-server>
@ -61,7 +68,7 @@
<authentication-provider user-service-ref="clientUserDetailsService" />
</authentication-manager>
<bean id="clientCredentialsChecker" class="org.springframework.security.oauth2.provider.ClientCredentialsChecker">
<bean id="authorizationRequestFactory" class="org.springframework.security.oauth2.provider.DefaultAuthorizationRequestFactory">
<constructor-arg>
<bean class="org.mitre.oauth2.service.impl.DefaultOAuth2ClientDetailsEntityService"/>
</constructor-arg>

@ -1 +1 @@
Subproject commit 361863198057546a2202bfe25685eae6f76c8425
Subproject commit 71575ac21bfc6cee1671f9a4f8b52b160de27e67