I have added to my Spring Boot MVC Web Application Social login feature. It allows users to login to my application with GitHub, Facebook, or Google account. But I am struggling to get the /logout feature work. Even though the /logout is called and the logoutSuccessUrl is loaded, if user clicks on the login link again, the user is not being asked to provide their username or password again. It looks like the user is still authenticated.
How do you guys implement /logout using the new Spring Security 5 OAuth 2 client support?
I have used the new OAuth 2 Client support.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
spring.security.oauth2.client.registration.facebook.client-id =
spring.security.oauth2.client.registration.facebook.client-secret =
And here is how my HTTPSecurity configuration looks like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.logout().logoutSuccessUrl("/");
}
I have tried this way as well:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers(HttpMethod.POST,"/logout").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSIONID")
.logoutSuccessUrl("/").permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
How do you guys log out users who are authenticated using one of the Social OAuth 2 Login Providers using the new Spring Security OAuth 2 client support?
I am currently logged into google on my chrome browser and can view my gmail etc, so I have an active session with google.
If I was to access your spring app, and use google sign-in, your spring app will redirect me to googles auth server which detects that I am already logged into google so it knows who I am, hence it just needs to ask me to consent the scopes your application is requesting and if I agree to issue your application the access token.
Now if I want to log out of your app, spring security will invalidate the session on your application, but it has no control over the session I have open with google, in fact I don't want to also be logged out of google.
Hence if you want the login screen of the 3rd party again, you need to go to their page and logout.
Related
I watched a training video on authorization and authentication by token, and there is an entry http.csrf().disable() in the method that connects the filters. Why do we disable csrf protection, the person did not explain? Here's the full class for a better idea of what I'm talking about:
#Configuration
#RequiredArgsConstructor
public class SecurityConfig{
private final JwtTokenProvider jwtTokenProvider;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(authz ->
{
try {
authz
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/sensors/**").hasAuthority("ROLE_ADMIN")
.requestMatchers("/api/sensors/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
);
return http.build();
}
}
CSRF is a vulnerability where an attacker exploits the way browsers work. In more traditional web applications, your session token (which allows the app to authenticate requests) is sent in a cookie. Cookies are sent by browsers with requests based on where the request goes to, and regardless of the origin the request was made from. So if you are logged in to such an application, and visit a malicious site, that malicious site can have you make inadvertent requests to the victim site, and your session cookie will be sent automatically. So to prevent an attacker from setting up such a malicious site and have you make requests to the victim one potentially performing things you didn't want to, the victim site needs to implement protection against CSRF. There are multiple techniques to achieve this, for example the website can create a random CSRF token, include it in forms and store it server-side, expecting it back with every form submission. This works, because the attacker, when making a request from the malicious site will have no way to include the correct token. (Also the token belongs to an authenticated user, so even if the attacker does download a page to get a CSRF token, that will not be valid for another logged on user.)
Notice that all of the above relied on the fact that a cookie will be sent automatically. If, as in your example, the authentication token is sent as something lese (like as an Authorization header), that will not be automatically included by the browser. The attacker on a malicious website can still have you make a request to the victim site, however, you auth info cannot be included, so it will not work anyway - whatever such a request can do, the attacker could do by himself anyway (except some very special cases).
For this reason, CSRF protection can be disabled, if authencation is based on something that's not sent automatically.
I am trying to integrate Authorization Server into my app with form login. I'm using my own login page.
Samples suggest to use the following configuration:
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
//...
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) ->
exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
//...
)
Now when I try to authorize (/oauth2/authorize?...) I got redirection to my login page. If I logged in before I see OAuth consent page and able to submit consent. But 99% of times I see my /login page, able to log in and stuck here. How to continue to consent page there? Should I write my own logic for that?
Solved issue myself by removing custom .successHandler(...) from my custom form login configuration. Default SavedRequestAwareAuthenticationSuccessHandler correctly handle all redirects as expected.
I'm trying to get Okta OAuth setup with my spring boot app. I can get it to redirect to the authorization page, but after authenticating with okta I always just get bumped back to the login page. It seems as if the auth data from okta isn't being properly stored in the application.
You can see the full app here. I've omitted the sensitive data like clientid/secret for my okta dev instance and the keystore stuff.
This is my security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth_login")
.permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.successHandler(successHandler)
.loginPage("/oauth_login")
.clientRegistrationRepository(clientRegRepo)
.authorizedClientService(clientService)
.tokenEndpoint()
.accessTokenResponseClient(tokenClient);
}
I am trying to add handler on user login. In basic spring security it could be implemented by this code:
protected void configure(HttpSecurity http) throws Exception {
http.
//some configurations
.formLogin().successHandler(authenticationSuccessHandler);
}
Unfortunalty, this way do not work with oauth2 password flow because user do not authenticate using login form(there is direct request to oauth/token endpoint).
I have found this answer, and wonder is there cleaner way?
UPD: If I use AuthenticationSuccessEvent - I receive this event on every request, Perhaps because I use JWT authentication.
I have configured a form-base authentication using Spring security. It works fine when I log in using the login form in my web application.
It also works with cURL:
curl --data "j_username=myname&j_password=mypassword" http://localhost:8080/test/j_spring_security_check --verbose
However, I cannot make it works using Postman:
What is missing? I need to be authenticated in order to test other services.
Finally I managed making Postman aware of authentication by using the Postman Interceptor chrome extension
With the extension enabled (by clicking the satellite icon within the Postman App), you just need to log in using the login form of your web and then you can start using services that require authentication in Postman.
You can achieve authentication/authorization in postman through various authorization types given in postman dropdown under Authorization tab.
Below is the step to use Basic Auth which by default spring security provides.
In spring security you can customize your credentials in application.properties file as given below.
spring.security.user.name=yer
spring.security.user.password=galem
Using http.httpBasic worked for me.
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/home")
.hasRole("ADMIN")
.antMatchers("/api/product/*")
.hasRole("ADMIN")
.and()
.formLogin();
When using basic auth on postman, you will set the credentials on the authorization tab.
Click on Authorization, choose the type as Basic Auth, the credentials section will be displayed for you to key in the username and password.
For me the Postman Interceptor was not working, So I did the following and now I can login to the server.
Select POST request from dropdown and type login URL in request URL section.
Select Body from tabs
Enter username and password keys and values as shown in picture.
And click send this will create a temp cookie in postman and be there for a session.
You can also do GET request for logout URL to logout from session. Or delete the cookie from Cookies below Send button.
http.httpBasic is working for Testing spring security with Postman.
For example, Added the override configure method from the configuration class MyConfiguration.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").permitAll().anyRequest().authenticated()
.and().formLogin().permitAll().and().logout().permitAll().and().httpBasic();
http.cors().disable().csrf().disable();
}
Note: To access the POST, PUT, and DELETE request you need to disable the cors and csrf as per code above.