I'm developing an application (spring boot for backend and react for frontend) with authentication with Office 365. But I want to use my own group and permissions for Users. For instance, when a User access to /api/auth for the first time, I want to retrieve informations from microsoft graph and save it to my DB and then protect my endpoint with my own roles/permissions.
So far I managed to do this :
- When i go to localhost:8080 (back) i'm redirect to Azure portal to authenticate. Then, I can access to my endpoints
- I can protect my endpoints with my own roles
- When i go to localhost:3000 (react app) I have a button that redirect me to the portal and give me an azure access token (thanks to MSAL.js)
So my problem is that I can't validate this azure token in my backend and send a new token from the back to the front to send request (like GET /api/users or POST /api/todos). I think my backend configuration and implementation is wrong but I didn't find a way to validate tokens and return a token for my backend...
I hope I was clear, english isn't my native language
Here is my WebSecurityConfig
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/**")
.authenticated()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.oauth2Login();
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
And here's how I protect my endpoints :
@PreAuthorize("hasPermission(#foo, 'write')")
The conf :
@Configuration
public class AclPermissionEvaluator implements PermissionEvaluator {
@Autowired
private RoleRepository roleRepository;
@Autowired
private SecurityService securityService;
@Override
public boolean hasPermission(final Authentication authentication, final Object privilegeName, final Object privilegeType) {
DefaultOidcUser principal = (DefaultOidcUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Map<String, Object> userAttributes = principal.getAttributes();
UserInfo userInfo = securityService.getUserInfoByLogin((String) userAttributes.get("unique_name"));
if (userInfo == null || StringUtils.isBlank((CharSequence) privilegeName) || StringUtils.isBlank((CharSequence) privilegeType)) {
return false;
}
for (Permission permission : roleRepository.getByName(userInfo.getRoleName()).getPermissions()) {
if (permission.getPermission().startsWith((String) privilegeName) || permission.getPermission().equals("*")) {
return true;
}
}
return false;
}
//We don't need an implementation of this function for now
@Override
public boolean hasPermission(final Authentication authentication, final Serializable serializable, final String s, final Object o) {
return false;
}
}
Security Service :
@Service
@Transactional
public class SecurityServiceImpl implements SecurityService {
@Autowired private UserRepository userRepo;
@Autowired
private RoleRepository roleRepository;
@Override
public UserInfo getUserInfoByLogin(String username) {
User user = userRepo.getUserByUsername(username);
ModelMapper modelMapper = new ModelMapper();
return modelMapper.map(user, UserInfo.class);
}
}
RoleRepository :
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
public Role getByName(String name);
}
And on my application.yml :
spring:
security:
oauth2:
client:
registration:
azure:
clientId: <my-clientId>
clientSecret: <my-secret>
azure:
activedirectory:
tenant-id: <my-tenant-id>
user-group:
allowed-groups: all
active-directory-groups: all