Refactored auth provider to call the userinfo endpoint and provide info inside the auth token.
parent
8520fcbf72
commit
fbd6e67af8
|
@ -432,7 +432,7 @@ public class AbstractOIDCAuthenticationFilter extends
|
||||||
// construct an OpenIdConnectAuthenticationToken and return
|
// construct an OpenIdConnectAuthenticationToken and return
|
||||||
// a Authentication object w/the userId and the idToken
|
// a Authentication object w/the userId and the idToken
|
||||||
|
|
||||||
OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken(userId, idTokenValue, accessTokenValue, refreshTokenValue);
|
OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken(userId, idToken.getClaims().getIssuer(), serverConfig, idTokenValue, accessTokenValue, refreshTokenValue);
|
||||||
|
|
||||||
Authentication authentication = this.getAuthenticationManager().authenticate(token);
|
Authentication authentication = this.getAuthenticationManager().authenticate(token);
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,25 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.mitre.openid.connect.client;
|
package org.mitre.openid.connect.client;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nemonik
|
* @author nemonik
|
||||||
*
|
*
|
||||||
|
@ -32,7 +41,8 @@ import org.springframework.util.Assert;
|
||||||
public class OpenIdConnectAuthenticationProvider implements
|
public class OpenIdConnectAuthenticationProvider implements
|
||||||
AuthenticationProvider, InitializingBean {
|
AuthenticationProvider, InitializingBean {
|
||||||
|
|
||||||
private AuthenticationUserDetailsService<OpenIdConnectAuthenticationToken> userDetailsService;
|
private UserInfoFetcher userInfoFetcher = new UserInfoFetcher();
|
||||||
|
|
||||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -43,8 +53,6 @@ public class OpenIdConnectAuthenticationProvider implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
Assert.notNull(this.userDetailsService,
|
|
||||||
"The userDetailsService must be set");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -63,12 +71,26 @@ public class OpenIdConnectAuthenticationProvider implements
|
||||||
|
|
||||||
if (authentication instanceof OpenIdConnectAuthenticationToken) {
|
if (authentication instanceof OpenIdConnectAuthenticationToken) {
|
||||||
|
|
||||||
|
// Default authorities set
|
||||||
|
// TODO: let this be configured
|
||||||
|
Collection<SimpleGrantedAuthority> authorities = Sets.newHashSet(new SimpleGrantedAuthority("ROLE_USER"));
|
||||||
|
|
||||||
OpenIdConnectAuthenticationToken token = (OpenIdConnectAuthenticationToken) authentication;
|
OpenIdConnectAuthenticationToken token = (OpenIdConnectAuthenticationToken) authentication;
|
||||||
|
|
||||||
UserDetails userDetails = userDetailsService.loadUserDetails(token);
|
UserInfo userInfo = userInfoFetcher.loadUserInfo(token);
|
||||||
|
|
||||||
return new OpenIdConnectAuthenticationToken(userDetails,
|
if (userInfo == null) {
|
||||||
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), token.getUserId(),
|
// TODO: user Info not found -- error?
|
||||||
|
} else {
|
||||||
|
if (!Strings.isNullOrEmpty(userInfo.getUserId()) && userInfo.getUserId().equals(token.getUserId())) {
|
||||||
|
// the userinfo came back and the user_id fields don't match what was in the id_token
|
||||||
|
throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + userInfo.getUserId() + " / " + token.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OpenIdConnectAuthenticationToken(token.getUserId(),
|
||||||
|
token.getIssuer(),
|
||||||
|
userInfo, authoritiesMapper.mapAuthorities(authorities),
|
||||||
token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue());
|
token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +104,6 @@ public class OpenIdConnectAuthenticationProvider implements
|
||||||
this.authoritiesMapper = authoritiesMapper;
|
this.authoritiesMapper = authoritiesMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUserDetailsService(
|
|
||||||
AuthenticationUserDetailsService<OpenIdConnectAuthenticationToken> userDetailsService) {
|
|
||||||
this.userDetailsService = userDetailsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -96,7 +113,6 @@ public class OpenIdConnectAuthenticationProvider implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(Class<?> authentication) {
|
public boolean supports(Class<?> authentication) {
|
||||||
return OpenIdConnectAuthenticationToken.class
|
return OpenIdConnectAuthenticationToken.class.isAssignableFrom(authentication);
|
||||||
.isAssignableFrom(authentication);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,16 @@ package org.mitre.openid.connect.client;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.mitre.openid.connect.config.OIDCServerConfiguration;
|
||||||
import org.mitre.openid.connect.model.IdToken;
|
import org.mitre.openid.connect.model.IdToken;
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Michael Walsh, Justin Richer
|
* @author Michael Walsh, Justin Richer
|
||||||
|
@ -32,51 +37,73 @@ public class OpenIdConnectAuthenticationToken extends AbstractAuthenticationToke
|
||||||
|
|
||||||
private static final long serialVersionUID = 22100073066377804L;
|
private static final long serialVersionUID = 22100073066377804L;
|
||||||
|
|
||||||
private final Object principle;
|
private final Object principal;
|
||||||
private final String idTokenValue; // string representation of the id token
|
private final String idTokenValue; // string representation of the id token
|
||||||
private final String accessTokenValue; // string representation of the access token
|
private final String accessTokenValue; // string representation of the access token
|
||||||
private final String refreshTokenValue; // string representation of the refresh token
|
private final String refreshTokenValue; // string representation of the refresh token
|
||||||
|
private final String issuer; // issuer URL (parsed from the id token)
|
||||||
private final String userId; // user id (parsed from the id token)
|
private final String userId; // user id (parsed from the id token)
|
||||||
|
|
||||||
|
private final transient OIDCServerConfiguration serverConfiguration; // server configuration used to fulfill this token, don't serialize it
|
||||||
|
private final transient UserInfo userInfo; // user info container, don't serialize it b/c it might be huge and can be re-fetched
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs OpenIdConnectAuthenticationToken provided
|
* Constructs OpenIdConnectAuthenticationToken with a full set of authorities, marking this as authenticated.
|
||||||
*
|
*
|
||||||
* @param principle
|
* Set to authenticated.
|
||||||
* @param authorities
|
*
|
||||||
|
* Constructs a Principal out of the user_id and issuer.
|
||||||
* @param userId
|
* @param userId
|
||||||
|
* @param authorities
|
||||||
|
* @param principal
|
||||||
* @param idToken
|
* @param idToken
|
||||||
*/
|
*/
|
||||||
public OpenIdConnectAuthenticationToken(Object principle,
|
public OpenIdConnectAuthenticationToken(String userId, String issuer,
|
||||||
Collection<? extends GrantedAuthority> authorities, String userId,
|
UserInfo userInfo, Collection<? extends GrantedAuthority> authorities,
|
||||||
String idTokenValue, String accessTokenValue, String refreshTokenValue) {
|
String idTokenValue, String accessTokenValue, String refreshTokenValue) {
|
||||||
|
|
||||||
super(authorities);
|
super(authorities);
|
||||||
|
|
||||||
this.principle = principle;
|
this.principal = ImmutableMap.of("user_id", userId, "issuer", issuer);
|
||||||
|
this.userInfo = userInfo;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
this.issuer = issuer;
|
||||||
this.idTokenValue = idTokenValue;
|
this.idTokenValue = idTokenValue;
|
||||||
this.accessTokenValue = accessTokenValue;
|
this.accessTokenValue = accessTokenValue;
|
||||||
this.refreshTokenValue = refreshTokenValue;
|
this.refreshTokenValue = refreshTokenValue;
|
||||||
|
|
||||||
|
this.serverConfiguration = null; // we don't need a server config anymore
|
||||||
|
|
||||||
setAuthenticated(true);
|
setAuthenticated(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs OpenIdConnectAuthenticationToken provided
|
* Constructs OpenIdConnectAuthenticationToken for use as a data shuttle from the filter to the auth provider.
|
||||||
*
|
*
|
||||||
* @param idToken
|
* Set to not-authenticated.
|
||||||
|
*
|
||||||
|
* Constructs a Principal out of the user_id and issuer.
|
||||||
* @param userId
|
* @param userId
|
||||||
|
* @param idToken
|
||||||
*/
|
*/
|
||||||
public OpenIdConnectAuthenticationToken(String userId, String idTokenValue, String accessTokenValue, String refreshTokenValue) {
|
public OpenIdConnectAuthenticationToken(String userId, String issuer,
|
||||||
|
OIDCServerConfiguration serverConfiguration,
|
||||||
|
String idTokenValue, String accessTokenValue, String refreshTokenValue) {
|
||||||
|
|
||||||
super(new ArrayList<GrantedAuthority>(0));
|
super(new ArrayList<GrantedAuthority>(0));
|
||||||
|
|
||||||
this.principle = userId;
|
this.principal = ImmutableMap.of("user_id", userId, "issuer", issuer);
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
this.issuer = issuer;
|
||||||
this.idTokenValue = idTokenValue;
|
this.idTokenValue = idTokenValue;
|
||||||
this.accessTokenValue = accessTokenValue;
|
this.accessTokenValue = accessTokenValue;
|
||||||
this.refreshTokenValue = refreshTokenValue;
|
this.refreshTokenValue = refreshTokenValue;
|
||||||
|
|
||||||
|
this.userInfo = null; // we don't have a UserInfo yet
|
||||||
|
|
||||||
|
this.serverConfiguration = serverConfiguration;
|
||||||
|
|
||||||
|
|
||||||
setAuthenticated(false);
|
setAuthenticated(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +125,7 @@ public class OpenIdConnectAuthenticationToken extends AbstractAuthenticationToke
|
||||||
@Override
|
@Override
|
||||||
public Object getPrincipal() {
|
public Object getPrincipal() {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return principle;
|
return principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserId() {
|
public String getUserId() {
|
||||||
|
@ -126,5 +153,26 @@ public class OpenIdConnectAuthenticationToken extends AbstractAuthenticationToke
|
||||||
return refreshTokenValue;
|
return refreshTokenValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the serverConfiguration
|
||||||
|
*/
|
||||||
|
public OIDCServerConfiguration getServerConfiguration() {
|
||||||
|
return serverConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the issuer
|
||||||
|
*/
|
||||||
|
public String getIssuer() {
|
||||||
|
return issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the userInfo
|
||||||
|
*/
|
||||||
|
public UserInfo getUserInfo() {
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package org.mitre.openid.connect.client;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.mitre.openid.connect.model.DefaultUserInfo;
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
public class UserInfoFetcher {
|
||||||
|
|
||||||
|
public UserInfo loadUserInfo(OpenIdConnectAuthenticationToken token) {
|
||||||
|
|
||||||
|
HttpClient httpClient = new DefaultHttpClient();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||||
|
|
||||||
|
RestTemplate restTemplate = new RestTemplate(factory);
|
||||||
|
|
||||||
|
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||||
|
form.add("access_token", token.getAccessTokenValue());
|
||||||
|
|
||||||
|
String userInfoString = restTemplate.postForObject(token.getServerConfiguration().getUserInfoUrl(), form, String.class);
|
||||||
|
|
||||||
|
JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject();
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
DefaultUserInfo userInfo = gson.fromJson(userInfoJson, DefaultUserInfo.class);
|
||||||
|
|
||||||
|
return new DefaultUserInfo();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,8 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.mitre.openid.connect.config;
|
package org.mitre.openid.connect.config;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nemonik
|
* @author nemonik
|
||||||
*
|
*
|
||||||
|
@ -39,6 +41,8 @@ public class OIDCServerConfiguration {
|
||||||
|
|
||||||
private String jwkSigningUrl;
|
private String jwkSigningUrl;
|
||||||
|
|
||||||
|
private String userInfoUrl;
|
||||||
|
|
||||||
public String getAuthorizationEndpointURI() {
|
public String getAuthorizationEndpointURI() {
|
||||||
return authorizationEndpointURI;
|
return authorizationEndpointURI;
|
||||||
}
|
}
|
||||||
|
@ -111,17 +115,27 @@ public class OIDCServerConfiguration {
|
||||||
this.jwkSigningUrl = jwkSigningUrl;
|
this.jwkSigningUrl = jwkSigningUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* @return the userInfoUrl
|
||||||
return "OIDCServerConfiguration [authorizationEndpointURI="
|
*/
|
||||||
+ authorizationEndpointURI + ", tokenEndpointURI="
|
public String getUserInfoUrl() {
|
||||||
+ tokenEndpointURI + ", clientSecret=" + clientSecret
|
return userInfoUrl;
|
||||||
+ ", clientId=" + clientId + ", issuer=" + issuer
|
}
|
||||||
+", x509EncryptedUrl="
|
|
||||||
+ x509EncryptUrl + ", jwkEncryptedUrl="
|
/**
|
||||||
+ jwkEncryptUrl + ", x509SigningUrl="
|
* @param userInfoUrl the userInfoUrl to set
|
||||||
+ x509SigningUrl + ", jwkSigningUrl="
|
*/
|
||||||
+ jwkSigningUrl + "]";
|
public void setUserInfoUrl(String userInfoUrl) {
|
||||||
}
|
this.userInfoUrl = userInfoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OIDCServerConfiguration [authorizationEndpointURI=" + authorizationEndpointURI + ", tokenEndpointURI=" + tokenEndpointURI + ", clientSecret=" + clientSecret + ", clientId=" + clientId + ", issuer=" + issuer + ", x509EncryptUrl=" + x509EncryptUrl + ", x509SigningUrl="
|
||||||
|
+ x509SigningUrl + ", jwkEncryptUrl=" + jwkEncryptUrl + ", jwkSigningUrl=" + jwkSigningUrl + ", userInfoUrl=" + userInfoUrl + "]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue