We can dynamically update a logged-in user's authorities, without having to log out and log in, by resetting the Authentication object (security token) in the Spring SecurityContextHolder by using this code.
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> updatedAuthorities = new ArrayList<>(auth.getAuthorities());
updatedAuthorities.add(...); //add your role here [e.g., new SimpleGrantedAuthority("ROLE_NEW_ROLE")]
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);
But this code doesn't update authorities in database and I want to update the authorities in database tables of OAuth oauth_access_token and oauth_refresh_token tables. Actually I am working on a social app where user authorities change frequently.
Does Spring provide this feature out of the box?
Or do you have any custom Logic?
You can use TokenStore::storeAccessToken
This worked for my app, when autorities can be chaneged without user logout/login
private final TokenStore tokenStore;
public void updateAuthorities() {
var auth = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> newAuthorities = <Your autorities list>;
var newAuth = new UsernamePasswordAuthenticationToken(
auth.getPrincipal(),
auth.getCredentials(),
newAuthorities
);
newAuth.setDetails(auth.getDetails());
var oauth2Auth = new OAuth2Authentication(auth.getOAuth2Request(), newAuth);
oauth2Auth.setDetails(auth.getDetails());
oauth2Auth.setAuthenticated(true);
OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(auth);
tokenStore.storeAccessToken(existingAccessToken, oauth2Auth);
SecurityContextHolder.getContext().setAuthentication(oauth2Auth);
}
Related
I am trying to implement SSO in our app using keycloak-spring-security-adapter. The logging itself is working fine, but inside the app we have modules availability based on user roles/groups and i am not able to get user roles from SecurityContext to show users only what they should see.
SecurityContext context = SecurityContextHolder.getContext();
if(context.getAuthentication() != null) {
KeycloakPrincipal principal = (KeycloakPrincipal) context.getAuthentication().getPrincipal();
KeycloakSecurityContext session = principal.getKeycloakSecurityContext();
AccessToken accessToken = session.getToken();
AccessToken.Access realmAccess = accessToken.getRealmAccess();
logger.info("KEYCLOAK ROLES: " + realmAccess.getRoles());
above logger for my user always gives this:
KEYCLOAK ROLES: [offline_access, uma_authorization]
And these are not the roles registered in keycloak server, because the one used for authenticating my user is:
GSAP_APPLICATION_SUPPORT
I am not able to log into the app with user that is not a member of any keycloak-registered groups so thats why i know this process works fine.
Is there a way of getting list of current user roles from keycloak based on userId/token?
Hardcoding the roles checking inside the service is not a best practice, it's a common approach to divide role based functionalities by API like:
api/v1/admin/**, api/v1/user/**
Using this you can restrict the access to API by roles:
http.authorizeExchange()
.pathMatchers("your_endpoint").hasAnyRole("desired_role");
PS Please pay attention that keycloak adds the "ROLE_" prefix to the rolename, so you can
use ROLE_admin, ROLE_user in your configuration
or
use role names without "ROLE_" prefix (admin, user), and implement the JWT auth converter(example for Reactive (webFlux), you can do similar for Tomcat):
:
Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> getJwtAuthenticationConverter() {var converter = new ReactiveJwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Map<String, Object> realmAccess = jwt.getClaim("realm_access");
Collection<String> roles = (Collection<String>) realmAccess.get("roles");
return Flux.fromIterable(roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.toList());
});
return converter;
}
I've got the following set up:
Central auth server written with spring boot that is currently working (I can curl and receive an access token, jdbc token store, etc)
Multiple applications owned by the same developer, sharing the same customer base on different domains. IE: John Doe for app1 is the same as John Doe for app2.
I have an existing application (app1 above) that is jsf 2.2 with spring security configured for login purposes. That application works stand alone right now, with it's own user base.
This is the flow I am trying to obtain:
Resource Owner Password Credential OAuth Flow
So we would want:
User goes to app1
User enters user and password into app1 login page
User hits "login"
Some sort of configuration in Spring would then take the loginByUsername request, get access token from the central oauth server
We now have app1 access - the user could have one of three roles (ADMIN, USER, SUPERUSER).
When they go to (say) app1/views/createEntry.xhtml, we would confirm the access token we currently have is still active on the auth server.
The resource server would technically be the resources on the app1 server (right?)
I'm new to this oauth2.0 process (and spring really), but I think this is the flow I want. How do I set this up with Spring Security? I've seen a security setting called oauth2login() that I think is what we COULD want, but I think that is more authorization code flow.
I haven't found a very good example of this using the password flow.
I do trust each of the applications in this process, hence the password flow. We control the network that maintains traffic between the auth server and the other applications.
Edit: SSO isn't an option because of requirements and our customer base. The applications are unique enough that it doesn't make sense, but the user should be able to log into any of our applications with those credentials.
Edit 2: Sorry for second edit. I would like to add that I've added a resource configuration on app1 and it actually seems like it works - I've secured anything /views/* and when I attempt to go their, I get the expected "Full Authentication required" message.
Edit 3: I think I am making some progress -
First, I created a spring component that implements AuthenticationProvider and then overwrote the authenticate method so that I created a ResourceOwnerPasswordResourceDetails object with all my properties (client id, client secret, grant type, scope, etc) and called the authorization server to get a token. My excitement to see my log refresh for the authorization server was high.
Next step I need to figure out is how to generate an extension of org.springframework.security.core.userdetails.User so that I can store the privileges for the user.
Also - I can't quite figure out yet how the token is stored. I know the auth server generates a token and stores in jdbc, but where/how does the token get stored on the client side?
For those that were curious, here is how I set up the authentication provider on my client (app1). I still have issues with the resource server (ill ask a separate question), but here is what I did:
Custom authenticator:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private AppUserDAO appUserDAO;
private String accessTokenUri = "http://localhost:8080/oauth/token";
private String clientId = "clientid";
private String clientSecret = "clientsecret";
public AccessTokenProvider userAccessTokenProvider() {
ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
return accessTokenProvider;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = authentication.getName();
final String password = authentication.getCredentials().toString();
List<String> scopes = new ArrayList<String>();
scopes.add("read");
final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setUsername(username);
resource.setPassword(password);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setGrantType("password");
resource.setScope(scopes);
// Generate an access token
final OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
template.setAccessTokenProvider(userAccessTokenProvider());
OAuth2AccessToken accessToken = null;
try {
accessToken = template.getAccessToken();
System.out.println("Grabbed access token from " + accessTokenUri);
}
catch (OAuth2AccessDeniedException e) {
if (e.getCause() instanceof ResourceAccessException) {
final String errorMessage = String.format(
"While authenticating user '%s': " + "Unable to access accessTokenUri '%s'.", username,
accessTokenUri);
throw new AuthenticationServiceException(errorMessage, e);
}
throw new BadCredentialsException(String.format("Access denied for user '%s'.", username), e);
}
catch (OAuth2Exception e) {
throw new AuthenticationServiceException(
String.format("Unable to perform OAuth authentication for user '%s'.", username), e);
}
// Determine roles for user
List<GrantedAuthority> grantList = ...
// Create custom user for the principal
User user = .....
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null /*dont store password*/, grantList);
return token;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
....
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
I am doing a project on library management system in spring boot security.
In order to calculate the fines for the issued books according to the roles i wan the current user role after borrowing a book.
Current user name, role book_id and fine will be stored in other table.
I am able to get the current users username, but not able to get role the current user.
Could someone please help me out!
//Part of Controller class
#RequestMapping("/homepage/borrowBook")
public String addBookings(Bookings bk, HttpServletRequest rqst) {
rqst.setAttribute("mode", "MODE_BORROW");
return "homepage";
}
#PostMapping("/homepage/save-borrow")
public String saveBorrow(Bookings bk, HttpServletRequest rqst, Authentication auth) {
rqst.setAttribute("mode", "MODE_BORROW");
if (BookRepo.exists(bk.getBook_id())) {
bk.setUser(auth.getName());
/////here i want the current user authority to be saved/checked.
bookingsRepo.save(bk);
return "homepage";
} else {
rqst.setAttribute("error", "Book doesn't exist");
return "homepage";
}
}
You can use Authentication.getAuthorities() to get the roles of the currently logged in user.
You can get the authorities using the SecurityContextHolder or through the inject Authentication object at your controller.
Find below through the SecurityContextHolder
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Collection<SimpleGrantedAuthority> list = (Collection<SimpleGrantedAuthority>) auth.getAuthorities();
for (SimpleGrantedAuthority permission : list) {
System.out.println(permission.getAuthority());
}
If you need any other information about the logged in user, you can access the UserDetails as follows
User userDetails = (User) auth.getPrincipal();
1.Using spring security oauth2 dependcy.Making successful authentication to google but i cant get refresh token.How do i get refresh token ?ı can get only access token from PrincapalUser object.
in WebSecurityConfigurer Adapter
2.
private OAuth2ClientAuthenticationProcessingFilter filter() {
// Creating the filter for "/google/login" url
OAuth2ClientAuthenticationProcessingFilter oAuth2Filter = new
OAuth2ClientAuthenticationProcessingFilter(
"/google/login");
authorizationCodeResourceDetails.setPreEstablishedRedirectUri("http://localhost:8080/");
List<String> scopes = authorizationCodeResourceDetails.getScope();
authorizationCodeResourceDetails.setGrantType("authorization_code");
// Creating the rest template for getting connected with OAuth service.
// The configuration parameters will inject while creating the bean.
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(authorizationCodeResourceDetails,
oauth2ClientContext);
oAuth2Filter.setRestTemplate(oAuth2RestTemplate);
// setting the token service. It will help for getting the token and
// user details from the OAuth Service
String userInfo = resourceServerProperties.getUserInfoUri();
String clientId = resourceServerProperties.getClientId();
UserInfoTokenServices tokenService = new UserInfoTokenServices(resourceServerProperties.getUserInfoUri(),
resourceServerProperties.getClientId());
// tokenService.setTokenType(DefaultOAuth2AccessToken.REFRESH_TOKEN);
oAuth2Filter.setTokenServices(tokenService);
// oAuth2Filter.setTokenServices(defaultToken());
return oAuth2Filter;
}
I added google url param requriments , spring boot application.yml social authentication configuration.
I'm trying to add a new user using Spring Security programmatically using this answer. But unfortunately I get issue.
Code:
SecurityConfig:
private JdbcUserDetailsManager employeeDetailsManager;
#Bean(name = "employeeDetailsManager")
public JdbcUserDetailsManager getEmployeeDetailsManager() {
return employeeDetailsManager;
}
Application:
private static void createUser(ApplicationContext context){
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("supervisor"));
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
UserDetails user = new User("supervisor1", passwordEncoder.encode("supervisor1"), authorities);
JdbcUserDetailsManager userDetailsManager = (JdbcUserDetailsManager) context.getBean("employeeDetailsManager");
userDetailsManager.createUser(user);//Exception!
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
On the line userDetailsManager.createUser(user) I get exception:
java.sql.SQLSyntaxErrorException: user lacks privilege or object not
found: USERS
And I understand why I get it: I really don't have a table Users. Instead of this I have table Employees, so I need to save new user in this table.
So how can I fix this error and create and save user to the table Employees?
There are fields constant storing the query.
for example, you have to
setCreateUserSql(String createUserSql)
before using createUser method.
http://docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/apidocs/org/springframework/security/provisioning/JdbcUserDetailsManager.html
Please refer to the constant and make sure you filled in correctly.