OAuth2 authetication server and resource server using Spring boot - spring-boot

I have a Spring Boot application.I am trying to implement OAuth2 authorization for that application. I have followed this tutorial https://spring.io/guides/tutorials/spring-boot-oauth2/ Enabling the Authorization Server part. Although I am successfully able to get access tokens from the auth-server, when I am trying to send those tokens to request my resource server, it errors Unauthorized access in the console.
org.springframework.security.access.AccessDeniedException: Access is denied
Though I will separate both of the authorization server & resource server later, for initial purpose, single application for both will work.
#Configuration
#EnableAuthorizationServer
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(Application.baseURL + "/user/register");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
.and().csrf().disable();
}
}
and for user authetication
#Configuration
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Loggable
private static Logger logger;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Session session = Hibernate.sessionFactory.openSession();
try {
UserPasswordDTO userPasswordDTO = new UserPasswordModel().getByEmailId(session, username);
return new SimsmisUser(username, userPasswordDTO.hashedPassword, true, true, true, true,
AuthorityUtils.createAuthorityList("USER"), userPasswordDTO.userId);
}
catch (InvalidIdException e) {
throw new UsernameNotFoundException(e.getMessage());
}
finally {
if (session != null) {
try {
session.close();
}
catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
};
}
}
How to communicate with the resource server with the access token?
Any example will help.

You haven't posted any Resource Server configuration.
Try this tutorial as well for additional insights:
https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v

Related

Spring boot + LDAP form login, logout and validating the token after login

I am integrating spring boot with LDAP to Authenticate a user. So that only authenticated users can only access the API.
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
LdapAuthenticationProvider authenticationProvider(final LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
#Bean
BindAuthenticator authenticator(final BaseLdapPathContextSource contextSource) {
final BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] {
"xx" });
return authenticator;
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter("(sAMAccountName={0})")
.contextSource(contextSource());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin().and()
.authorizeRequests()
.anyRequest().fullyAuthenticated().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.debug(true);
}
#Bean
LdapContextSource contextSource() {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("xx);
contextSource.setBase("xx");
contextSource.setUserDn("xx");
contextSource.setPassword("xx");
contextSource.setPooled(true);
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
final LdapTemplate ldapTemplate = new LdapTemplate(
contextSource());
ldapTemplate.setIgnorePartialResultException(true);
return ldapTemplate;
}
}
I am using the inbuild form login.
Question
Who (which class) is responsible to create a success token and where is it stored and in successive calls how is it validated?
Now I am only redirecting the unauthenticated calls to the login page due to this it giving 200 success responses, How to override this and send 401
Edited:
I have one specific question
If there is no token, the user is stored in the session -> how subsequent requests are validated. Which all classes are used

trouble connecting Angular 10 with Spring security to use custom login page

I have been working on a web application using Spring boot and spring security with frontend controlled by angular 10. I have implemented backend for security and created a login page also. But, on running on local host it is throwing an error
blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have been banging my head all day long to resolve this error but could not find the solution.
I have attached my code below for reference
Controller
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
AuthenticationManager authenticationManager;
#PostMapping("/login")
public boolean login(#RequestBody loginDetails data) {
try {
String username = data.getUsername();
System.out.println("Checking...");
System.out.println(data.getUsername());
System.out.println(data.getPassword());
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
// String token = jwtTokenProvider.createToken(username,
// this.users.findByEmail(username).getRoles());
System.out.println("abcdefg");
Map<Object, Object> model = new HashMap<>();
model.put("username", username);
// model.put("token", token);
/* return true; */
} catch (AuthenticationException e) {
/*
* throw new BadCredentialsException("Invalid email/password supplied");
*/
return false;
}
return true;
}
WebSecurityConfiguration
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("userDetailsService")
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/user/save","/user/login",
"/admin/**").permitAll().anyRequest().authenticated().and().csrf()
.disable().formLogin().permitAll().and().logout().permitAll();
http.cors();
}
#Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
AngularRequestcode
public doLogin(){
this.userLogin.username=this.loginForm.get("username").value;
this.userLogin.password=this.loginForm.get("password").value;
console.log(this.userLogin);
return this.http.post<any>("http://localhost:8080/user/login",this.userLogin).subscribe((response) => {
if (response.status === 200) {
console.log('login successfully');
} else {
console.log('galat');
}
}
);
}
First of all, try change to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.authorizeRequests().antMatchers("/user/save", "/user/login",
"/admin/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and().logout().permitAll();
}
CORS it's browser check if you have response with: Access-Control-Allow-Origin: http://localhost:4200 or no: No 'Access-Control-Allow-Origin' header is present on the requested resource.;
Change http://localhost:4200 to your front-end url;
And add to your WebSecurityConfig:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*");
}
and: implements WebMvcConfigurer
Response without error:
Response with error. No Access-Control-Allow-Origin:

Oauth Spring Security not unauthorized to microservice oauth, registerController

I am beginner to Spring. I've been with this problem for several days and I wouldn't know how to solve it.
I have two microservices:
Microservice authentication : This allows return a JWT token to user when it access to path: /oauth/token
Microservice Account user : This microservice will have a lot of functions but the problem is it:
The user must be register in platform and this microservice call to server oauth to save the new created user.
So, I create the controller to microservice Oauth:
#PostMapping
#PreAuthorize("#oauth2.hasScope('server')")
public UserDto createUser(#Valid #RequestBody UserDto userDto) {
Usuario savedUser = new Usuario();
try {
savedUser = userService.create(this.toUsuario(userDto));
} catch (ArendheException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return toDto(savedUser);
}
The WebSecurityConfigureAdapter is:
#Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService usuarioService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.usuarioService).passwordEncoder(passwordEncoder());
}
#Bean("authenticationManager")
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST,"/oauth/**").permitAll()
.antMatchers(HttpMethod.POST, "/user/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
As you can see, I write two antMatchers, the second to create a new user.
The problem is when I test it with Postman (path localhost:8005/user with data JSON parsed to POST method). The output is:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I dont understand it because I have a permitAll to /user/** path.
Thanks.

Spring security : Custom authentication provider not working

I am using custom authentication provider which implements "AuthenticationProvider". In SecurityConfig i am using following configs.
http.csrf().disable().authorizeRequests()
.antMatchers("/login/authenticateUser").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated();
The above configuration does not call custom authentication provider for login API but for other API's the custom authetication provider is not being called which is throwing forbidden error.
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/login/authenticateUser").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated();
}
Custom authentication provider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
static Map<String, UserDetails> userSessionList = new HashMap<String, UserDetails>();
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String password = authentication.getCredentials().toString().split(";")[0];
if (checkUserNameAndPassword(userName, password)) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(() -> {
return "AUTH_USER";
});
Authentication auth = new UsernamePasswordAuthenticationToken(userName, password, grantedAuths);
return auth;
} else {
throw new AuthenticationCredentialsNotFoundException("Invalid Credentials!");
}
}
#Override
public boolean supports(Class<?> authentication) {
//return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
Please help me to solve this issue. Custom authentication provider should be called for all the requests except login controller.

WebSocket, Spring Security integration

I'm having small REST API application that is running on Spring boot. For security I'm using external provider (Auth0 in this case), and frontend Angular application provide token for each API call. This works great with minimal configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
JwtWebSecurityConfigurer
.forRS256(apiAudience, issuer)
.configure(http)
.authorizeRequests()
.antMatchers("/websocket/**").permitAll()
.anyRequest().authenticated();
}
}
Now I'm trying to add some websocket support in it for notify users on some events. Some basic things:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
Connection is working and I can introduce some HandshakeInterceptor to validate user's token that is sent throw url on connect:
public class HttpSessionHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
String token = httpServletRequest.getParameter("token");
AuthAPI auth = new AuthAPI("account url", "user id", "secret");
Request<UserInfo> authRequest = auth.userInfo(token);
try {
UserInfo info = authRequest.execute();
if (info.getValues().get("name") != null) {
return true;
}
} catch (APIException exception) {
} catch (Auth0Exception exception) {
}
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return false;
}
}
I'm having problem with current API calls that I want to be available also on sockets:
#Controller
public class TestController {
#PreAuthorize("hasAuthority('read:photos')")
#RequestMapping(value = "/photos", method = RequestMethod.GET)
#MessageMapping("/photos")
#ResponseBody
public String getPhotos() {
return "All good. You can see this because you are Authenticated with a Token granted the 'read:photos' scope";
}
}
Calling this from socket throws that, An Authentication object was not found in the SecurityContext exception. Is their any why to provide SecurityContext on socket calls? Maybe throw ChannelInterceptorAdapter.preSend? I found a lot of questions about this, but no answers have given (example). Auth0 team also not provide any working example of this.
I also tried to use WebSocket Security, but cant rewire it to Auth0.
Do anyone have any working solution with this more granular approach? Small note, on frontend using SockJS and Stomp. Can send token throw headers or throw url.

Resources