Different principals in spring security context when 'in-bean' login method used - spring

For couple of days I have been working on integrating Facebook with our application. I have successfully made the connection working and now after Facebook Login I copy the user to our database and later on I want to use our internal principal within context.
For the Spring security login we overloaded our authentication-manager with our class implemeting UserDetailsService.
When someone logs in with facebook, he has abstract credentials he can not know.
I used this method in my Facebook login controller to log him in:
Authentication auth = new UsernamePasswordAuthenticationToken(
profile.getId(), new Md5PasswordEncoder().encodePassword(
profile.getEmail() + profile.getId(), null),
(Collection<? extends GrantedAuthority>) getAuthorities(profile.getId()));
PROBLEM:
Within some controllers I use
public String profile(Locale locale, Model model, HttpSession session, Principal principal)and then principal actually contains different Objects.
For regular spring security login its:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4527d081: Principal: org.springframework.security.core.userdetails.User#586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: A5E9AB9E4AEE7486EC4B4F6133F77320; Granted Authorities: ROLE_ADMIN
But after login with the method in the controller its:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken#cd4699c5: Principal: 1405308431; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_CLIENT
QUESTION:
I really don't want to distinguish between these types in all my controllers. Why it differs?! It obviously is a User (my type) object when we login with normal credentials and its just a String for facebook. How can I change it so facebook login would give me User object in my security context too?

You need to set up SignInAdapter as described at the end of this chapiter. In the SignInAdapter.signIn(...) method you can load your user object from DB then prepare authentication object and inject it in security context holder.

I didn't really get the 'prepare authentication object' at first but it was really easy;
I post this example for future clarification maybe one will need it :)
public class FacebookAuthenticationToken extends AbstractAuthenticationToken
{
private final org.springframework.security.core.userdetails.UserDetails principal;
private String credentials;
public FacebookAuthenticationToken(org.springframework.security.core.userdetails.UserDetails details,FacebookProfile profile, Collection<? extends GrantedAuthority> authorities){
super(authorities);
this.principal = details;
this.credentials = new Md5PasswordEncoder().encodePassword(profile.getEmail()+profile.getId(), null);
super.setAuthenticated(true); // must use super, as we override
}
private static final long serialVersionUID = -7545290433068513777L;
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
}
And now i understood I could have even used the UsernamePasswordAuthenticationToken,
just provide the Object principal with correct class. Silly me.

Related

Spring Security 5 with multiple searchbase groups fails while singel group base with same credentials does not. What do i miss here?

This question is about how to use multiple group search bases instead of the one.
I used an example provided by samaddico (simple spring security + LDAP example), modified it for single group search base with provided server / user / LDAP configuration text. It uses a service account to connect to ldap and a user which then tries to authenticate for certain simple web pages.
This approach works but lacks ability to collect membership / roles from different groups in the search tree.
Spring Security provides classes LdapContextSource and MultipleLdapAuthoritiesPopulator to allow for searching for roles in different locations.
Now here is the code which will result in the error shown below:
LDAP Configuration:
* Create an implementation of org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator which can call
multiple instances of LdapAuthoritiesPopulator.
* Then create one LdapAuthoritiesPopulatorfor each 'groupSearchBase' that I wanted to query.
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
LdapContextSource contextSource = contextSource();
MultipleLdapAuthoritiesPopulator multipleLdapAuthoritiesPopulator = new MultipleLdapAuthoritiesPopulator(
new DefaultLdapAuthoritiesPopulator(contextSource, ldapGroupSearchBaseA),
new DefaultLdapAuthoritiesPopulator(contextSource, ldapGroupSearchBaseB),
new DefaultLdapAuthoritiesPopulator(contextSource, ldapGroupSearchBaseC));
auth
.ldapAuthentication()
.contextSource(contextSource)
.ldapAuthoritiesPopulator(multipleLdapAuthoritiesPopulator)
.userSearchFilter(ldapUserSearchFilter)
.userSearchBase(ldapUserSearchBase);
}
class MultipleLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private List<LdapAuthoritiesPopulator> authoritiesPopulators;
public MultipleLdapAuthoritiesPopulator(LdapAuthoritiesPopulator...authoritiesPopulators) {
this.authoritiesPopulators = Arrays.asList(authoritiesPopulators);
}
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
List<GrantedAuthority> grantedAuthorities = authoritiesPopulators.stream()
.map(authPopulator -> authPopulator.getGrantedAuthorities(userData, username))
.flatMap(Collection::stream)
.collect(Collectors.toList());
return grantedAuthorities;
}
}
/**
* Creates context source object instead of configuring it with AuthenticationBuilder
* #return Context source object used for accessing ldap server
*/
#Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource= new LdapContextSource();
contextSource.setUrl(ldapUrl);
contextSource.setUserDn(ldapManagerDn);
contextSource.setPassword(ldapManagerPassword);
contextSource.afterPropertiesSet();
return contextSource;
}
Session Configuration:
/**
* This is essential to make sure that the Spring Security session registry is notified when the session is destroyed.
* #return
*/
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
The Spring Application tells me that my service account got successful connected to LDAP server.
DEBUG 17220 --- [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#c1e15be1: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: 0A82CE8FA4FB9EB248D756EEE8134CAE; Granted Authorities: ROLE_ANONYMOUS
The error then is thrown when the found user is beeing tried to bind:
DEBUG 17220 --- [nio-8080-exec-8] o.s.s.l.a.BindAuthenticator : Failed to bind as CN=familyName\, name,OU=Group: org.springframework.ldap.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 52e, v3839 ]; nested exception is javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 52e, v3839 ]
DEBUG 17220 --- [nio-8080-exec-8] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
To sum this up: my credentials are correct for a single group base without using LdapContextSource and MultipleLdapAuthoritiesPopulator. But the authentication process seems not to provide the enteret password for my user with multiple group bases.
After spending some time in figuring out a solution i had to admit that there was no efficient way to create a solution i.e. with overwriting methods or classes.
But i stumbled on a change request for spring security, precicely for this use case when multiple group search bases need to be checked.
It is implemented since Spring Security version 5.4.1 (i believe) or included when using Spring Starter parent version 2.4.2.
Simply add the option to your authentification method:
.groupSearchSubtree(true)
The complete updated method example for authentication looks like this then:
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.ldapAuthentication()
.contextSource()
.url(ldapUrl)
.managerDn(ldapManagerDn)
.managerPassword(ldapManagerPassword)
.and()
.userSearchFilter(ldapUserSearchFilter)
.userSearchBase(ldapUserSearchBase)
.groupSearchFilter(ldapGroupSearchFilter)
.groupSearchBase(ldapGroupSearchBase)
.groupSearchSubtree(true)
;
You see that there is no need for three different nodes and no more custom context object to be forwarded any more, simply add the parent node for the group search base and let the subtree search do the rest.
It might have been nice to figure out a way by myself, but using an incorporated solution of the framework is surely the better way to go.

Spring OAuth2.0: Getting User Roles based on ClientId (Authorization Code Grant Type)

I have a setup of spring boot OAuth for AuthServer and it is resposible for serving a number of few resource server for authentication using spring-security-jwt.
My problem is while authenticating I need to load the roles of a user but specific to the clientId.
eg: If user1 have roles ROLE_A, ROLE_B for client1 and ROLE_C, ROLE_D for client2, then 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, ROLE_D because I am getting roles based on username.
If I need to have a role based on the client then I need clientId.
FYI,
I am using the authorization code flow for authentication.
I have seen similar question but that is based on password grant but I am trying on authorization code flow and that solution doesn't work for me.
Password grant question link
Below is my code where I need clientId
MyAuthenticationProvider.java
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
String clientId = ? // how to get it
....
}
}
MyUserDetailsService.java
#Override
public UserDetails loadUserByUsername(String username) {
String clientId = ? // how to get it
....
}
}
You probably need to see OAuth2Authentication in Spring-security. When your client is authenticated by oauth2, then your "authentication" is actually instance of OAuth2Authentication that eventually implements Authentication.
If you see the implementation of OAuth2Authentication, it's done as below;
public Object getPrincipal() {
return this.userAuthentication == null ? this.storedRequest.getClientId() : this.userAuthentication
.getPrincipal();
}
so if request included "clientId', then you should be able to get clientId by calling getPrincipal() and typecasting to String as long as your request didn't include user authentication.
For your 2nd case, username is actually considered as clientId. You need to call in-memory, RDBMS, or whatever implementation that has clientId stored and returns ClientDetails. You'll be able to have some idea by looking into Spring security's ClientDetailsUserDetailsService class.
Since I didn't get any appropriate solution for my question, I am posting the solution that I used after digging source code and research.
MyJwtAccessTokenConverter.java (Extend JwtAccessTokenConverter and implement enhance method)
public class OAuthServerJwtAccessTokenConverter extends JwtAccessTokenConverter {
....
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String clientId = authentication.getOAuth2Request().getClientId();
// principal can be string or UserDetails based on whether we are generating access token or refreshing access token
Object principal = authentication.getUserAuthentication().getPrincipal();
....
}
....
}
Info:
In enhance method, we will get clientId from authentication.getOAuth2Request() and userDetails/user_name from authentication.getUserAuthentication().
Along with JwtAccessTokenConverter, AuthenticationProvider and UserDetailsService are required for authentication in generating access token step and refresh token step respectively.
get authorization header from request then parse from base64 to get the client-id.
something like this:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes())
.getRequest();
String authHeader = request
.getHeader("Authorization");

Spring Security pre-authentication - gives me a new session even though principal is unchanged

I have implemented a spring security preathentication filter in my Grails application in order to integrate with Tivoli Access Manager.
The filter getting called for every request in my web application - yet even though the filter returns the same principal as a previous request, it seems to create a new session. I have created a ApplicationListener to listen for authentication events and I can see a new AuthenticationSuccessEvent, with a new session id for each request.
This means all my session variables get cleared each request- which wouldn't be a big deal but it breaks the uploadr plugin.
When I turn debug logging on for my preauthentication filter I see that it thinks the principal has changed, even though it has not:
2015-03-04 11:34:57.769 foobar.TamAuthenticationFilter Checking secure context token: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken#f0666480: Principal: grails.plugin.springsecurity.userdetails.GrailsUser#3125618: Username: 66734; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_APPROVER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 87928D9E25D98DD3CCFAC5D67689E609; Granted Authorities: ROLE_ADMIN, ROLE_APPROVER
2015-03-04 11:34:57.770 foobar.TamAuthenticationFilter Pre-authenticated principal has changed to 66734 and will be reauthenticated
2015-03-04 11:34:57.770 foobar.TamAuthenticationFilter Invalidating existing session
2015-03-04 11:34:57.771 foobar.TamAuthenticationFilter preAuthenticatedPrincipal = 66734, trying to authenticate
How can I make spring security use the same session for each principal returned by the pre authentication filter, rather than create a new one for every request?
here is my filter:
package foobar
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter
import grails.util.Environment
import grails.util.Holders
import groovy.util.logging.Log4j
#Log4j
class TamAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
java.lang.Object getPreAuthenticatedCredentials(javax.servlet.http.HttpServletRequest request)
{
"N/A"
}
java.lang.Object getPreAuthenticatedPrincipal(javax.servlet.http.HttpServletRequest request)
{
Long staffId = getStaffIdFromTamHeader(request)
if(!staffId)
log.error "iv-user header not found"
return staffId
}
/**
* Get Staff ID from the ivUser Tamheader.
* #param request
* #return
*/
static public Long getStaffIdFromTamHeader(request) {
return request.getHeader("iv-user")
}
}
LoggingSecurityEventListener:
package foobar
import groovy.util.logging.Log4j
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event.AbstractAuthenticationEvent
#Log4j
class LoggingSecurityEventListener implements
ApplicationListener<AbstractAuthenticationEvent> {
void onApplicationEvent(AbstractAuthenticationEvent event) {
def username = event.authentication.principal
def address = event.authentication.details.remoteAddress
def sessionId = event.authentication.details.sessionId
log.info "event=${event.class.simpleName} username=${username} remoteAddress=${address} sessionId=${sessionId}"
}
}
resources.groovy:
beans = {
//
// grabs the user id from the tam headers
//
tamAuthenticationFilter(TamAuthenticationFilter) {
authenticationManager = ref('authenticationManager')
checkForPrincipalChanges = true
}
tamAuthenticationProvider(PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('authenticationUserDetailsService')
}
//
// we do not want to redirect to the auth/login page since we are using tam
//
authenticationEntryPoint(Http403ForbiddenEntryPoint)
securityEventListener(LoggingSecurityEventListener)
}
config.groovy:
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.providerNames = ['tamAuthenticationProvider']
grails.plugin.springsecurity.userLookup.userDomainClassName = 'strobe.auth.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'strobe.auth.UserRole'
grails.plugin.springsecurity.authority.className = 'strobe.auth.Role'
grails.plugin.springsecurity.securityConfigType="InterceptUrlMap"
grails.plugin.springsecurity.interceptUrlMap = [
'/foobar/**': ['ROLE_USER']]
bootstrap.groovy:
def init = { servletContext -> SpringSecurityUtils.clientRegisterFilter('tamAuthenticationFilter',
SecurityFilterPosition.PRE_AUTH_FILTER.order + 10)
}
I have found the solution to my problem- I was returning a Long from getPreauthenticatedPrincipal() which confuses spring security, as the method requiresAuthentication() in AbstractPreauthenticatedProcessingFilter has this line of code:
if ((principal instanceof String) && currentUser.getName().equals(principal)) {
return false;
}
It expects the principal to be a String. Every time I returned a long it would reauthenticate and give me a new session.
I can't believe the solution was that simple and it took me 2 days to figure out!!!
Part of the problem I think is the scant documentation on preauthentication in spring and also the method signature of:
java.lang.Object getPreAuthenticatedPrincipal(javax.servlet.http.HttpServletRequest request)
which doesn't clearly suggest a return type.

How can I get the current user roles from Spring security 3.1

I have loaded the roles from the database for the current user. And I can access the user role with spring security expression in JSP, and can hide the options and URLs which are not authorized with hasRole. Now I wanted to have it in the servlet and display it in the logs (or store in the user object session). How can we achieve it?
You can try something like this:
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
You have the collection of roles in the authorities variable.
If you develop on Java 8, it's getting easier.
To get all user roles:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Set<String> roles = authentication.getAuthorities().stream()
.map(r -> r.getAuthority()).collect(Collectors.toSet());
To check if the user has a particular role, for example, ROLE_USER:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean hasUserRole = authentication.getAuthorities().stream()
.anyMatch(r -> r.getAuthority().equals("ROLE_USER"));
Try to call getUserPrincipal() from HttpServletRequest.
I've created a custom hasRole function for my project.
public static boolean hasRole (String roleName)
{
return SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(roleName));
}
To complete both answers...
Here is a Spring security implementation of getUserPrincipal, so you can see that the getUserPrincipal actually is SecurityContextHolder
public Principal getUserPrincipal() {
Authentication auth = getAuthentication();
if ((auth == null) || (auth.getPrincipal() == null)) {
return null;
}
return auth;
}
// And the getAuthentication
private Authentication getAuthentication() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!trustResolver.isAnonymous(auth)) {
return auth;
}
return null;
}
This may help someone.
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.ui.Model;
import org.springframework.security.core.userdetails.User;
#GetMapping("/home")
public String getHomePage(Authentication authentication, Model model) {
User u = (User) authentication.getPrincipal();
model.addAttribute("cu", u);
return "sb/homePage";
}
And in template Thymeleaf:
Current user:</br>
<div th:if="${cu}">
Username: [[${cu.username}]]</br>
Password: [[${cu.password}]]</br>
Role: [[${cu.authorities[0]}]]</br>
Enabled: [[${cu.enabled}]]</br>
Full: [[${cu}]]</br>
</div>
<div th:unless="${cu}">
Not logged-in!
</div>

Howto integrate spring-social authentication with spring-security?

I've got a little webapp secured by spring-security using a username/password combo on a sql-db as credentials.
I now want to add facebook/twitter authentication with spring-social. Using the examples I am able to store the users credentials in my db. I'm now working on authenticating the user against his current session on my app using the following piece of code:
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
User user = userService.getUserById(Long.parseLong(userId));
user.setPassword(this.passwordEncoder.encodePassword(user.getAccessToken(), this.salt));
this.userService.store(user);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getDisplayName(), user.getAccessToken());
HttpServletRequest req = request.getNativeRequest(HttpServletRequest.class); // generate session if one doesn't exist
token.setDetails(new WebAuthenticationDetails(req));
Authentication authenticatedUser = this.authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
return "/user/dashboard";
}
The authentication works, I am not getting any BadCredential-exceptions. But after being redirected to /user/dashboard I am thrown back to the login.
I am out of ideas, a similar piece of code for authenticating the session is working after a classical signup.
Does anyone have any ideas why this happens or how to debug this?
Thanks very much in advance!
Hendrik
I have similar code that works for me, and also adds "remember me" support:
// lookup by id, which in my case is the login
User user = userService.findByLogin(userId);
// code to populate the user's roles
List<GrantedAuthority> authorities = ...;
// create new instance of my UserDetails implementation
UserDetailsImpl springSecurityUser = new UserDetailsImpl(user, authorities);
// create new Authentication using UserDetails instance, password, and roles
Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser, user.getPassword(), authorities);
// set the Authentication in the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
// optional: remember-me support (must #Autowire in TokenBasedRememberMeServices)
tokenBasedRememberMeServices.onLoginSuccess(
(HttpServletRequest) request.getNativeRequest(),
(HttpServletResponse) request.getNativeResponse(),
authentication);

Resources