Reputation: 4886
I have multiple clients registered for my oauth2 auth server. lets say user1 have roles such as ROLE_A
, ROLE_B
for client1, same user has roes such as ROLE_C
, ROLE_D
for client2. now when the user logins either using client1 or client2 he is able to see all the four roles ie. ROLE_A
, ROLE_B
, ROLE_C
and ROLE_D
.
My requirement was when the user1 logins to client1 it should return only the roles ROLE_A
and ROLE_B
. when he logins using client2 it should return only ROLE_C
and ROLE_D
For achieving this, what I planned is within the authenticate function, I need to get the clientId. so using the clientId and the username I can find the corresponding roles allocated to the user from the db (client-user-roles-mapping table). .But the issue is I don't know how to get the clientId within the authenticate function
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = // HERE HOW TO GET THE CLIENT ID
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
}
Can anyone please help me on this
UPDATE 1
CustomAuthenticationProvider.java
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private LDAPAuthenticationProvider ldapAuthentication;
@Autowired
private AuthRepository authRepository;
public CustomAuthenticationProvider() {
super();
}
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = // HERE HOW TO GET THE CLIENT ID
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
}
public boolean invokeAuthentication(String username, String password, Boolean isClientValidation) {
try {
Map<String, Object> userDetails = ldapAuthentication.authenticateUser(username, password);
if(Boolean.parseBoolean(userDetails.get("success").toString())) {
return true;
}
} catch (Exception exception) {
log.error("Exception in invokeAuthentication::: " + exception.getMessage());
}
return false;
}
@Override
public boolean supports(Class<? extends Object> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
private Collection<SimpleGrantedAuthority> fillUserAuthorities(Set<String> roles) {
Collection<SimpleGrantedAuthority> authorties = new ArrayList<SimpleGrantedAuthority>();
for(String role : roles) {
authorties.add(new SimpleGrantedAuthority(role));
}
return authorties;
}
}
Upvotes: 6
Views: 7125
Reputation: 2872
For your requirement, since you want to just access additional parameters from the request, you could try out the following in your CustomAuthenticationProvider
class
@Autowired
private HttpServletRequest request;
Add the following logic to read the httpRequest parameters and add your logic to access the authorization key
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println("Header Name - " + headerName + ", Value - " + request.getHeader(headerName));
}
}
Now, you will have the encode Basic Authentication field which you can decode like the one below
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
String credentials = new String(Base64.getDecoder().decode(base64Credentials),
Charset.forName("UTF-8"));
// client/secret = clientId:secret
final String[] values = credentials.split(":",2);
Upvotes: 2
Reputation: 183
Here is you code after modification
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = getClientId();
// validate client ID before use
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
private String getClientId(){
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
final String authorizationHeaderValue = request.getHeader("Authorization");
final String base64AuthorizationHeader = Optional.ofNullable(authorizationHeaderValue)
.map(headerValue->headerValue.substring("Basic ".length())).orElse("");
if(StringUtils.isNotEmpty(base64AuthorizationHeader)){
String decodedAuthorizationHeader = new String(Base64.getDecoder().decode(base64AuthorizationHeader), Charset.forName("UTF-8"));
return decodedAuthorizationHeader.split(":")[0];
}
return "";
}
more info about RequestContextHolder
Upvotes: 6
Reputation: 20135
UsernamePasswordAuthenticationToken
A POJO is required to hold not just the username and the password but also the client identifier.
public ExtendedUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
private final String clientId;
public ExtendedUsernamePasswordAuthenticationToken(Object principal
, Object credentials
, String clientId) {
super(principal, credentials);
this.clientId = clientId;
}
public String getClientId() { return clientId; }
}
UsernamePasswordAuthenticationFilter
The authentication process needs to be tweaked so that the client identifier is passed to the authentication code in addition to the username and password.
public class ExtendedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public ExtendedUsernamePasswordAuthenticationFilter () { super(); }
@Override
public public Authentication attemptAuthentication(HttpServletRequest request
, HttpServletResponse response)
throws AuthenticationException {
// See the source code of UsernamePasswordAuthenticationFilter
// to implement this. Instead of creating an instance of
// UsernamePasswordAuthenticationToken, create an instance of
// ExtendedUsernamePasswordAuthenticationToken, something along
// the lines of:
final String username = obtainUsername(request);
final String password = obtainPassword(request);
final String clientId = obtainClientId(request);
...
final Authentication authentication = new ExtendedUsernamePasswordAuthenticationToken(username, password, clientId);
return getAuthenticationManager().authenticate(authentication);
}
}
public CustomAuthenticationProvider implements AuthenticationProvider {
...
@Override
public boolean supports(final Class<?> authentication) {
return authentication.isAssignableFrom(ExtendedUsernamePasswordAuthenticationToken.class);
}
@Override
public Authentication authenticate(final Authentication authentication)
throws AuthenticationException {
}
}
<bean class="com.path.to.filter.ExtendedUsernamePasswordAuthenticationFilter" id="formAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<http ... >
<security:custom-filter position="FORM_LOGIN_FILTER" ref="formAuthenticationFilter"/>
...
</http>
or, if using Java configuration:
@Bean
public ExtendedUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter(final AuthenticationManager authenticationManager) {
final ExtendedUsernamePasswordAuthenticationFilter filter = new ExtendedUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
...
}
Upvotes: 2