How to check if user still logged in via azure sso (oAuth2), while using my own webapp? - spring-boot

I'm developing an web application with Spring Boot using Azure AD and OAuth2.0 for authentication to secure up the backend.
If I log-out via for example the Outlook Web App, my web application should register this process and logout as well (at least if I reload or reopen the page). How do i implement that? Now the Web-Application seems as still logged in. Unfortunately I did not find an approach to implement this behavior consistently. Only if I use the self-implemented log-out button, it shows the desired effect and the HttpSession gets invalidated and cookies where deleted.
I have already implemented a login and logout via Azure AD in my web application (see code). As soon as I log-out via the button of my own application, I am automatically logged out of other Azure applications (e.g. Outlook Web App) that require Azure SSO.
I already tried the #PreAuthorize Annotation discribed here Spring MVC - Checking if User is already logged in via Spring Security? but this seems not to be the solution.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.clearAuthentication(true)
.logoutSuccessUrl("https://login.microsoftonline.com/common/oauth2/logout");
}
Redirect to main page:
#GetMapping("login/oauth2/code/azure")
public ModelAndView redirectToRoot(ModelMap modelMap) {
return new ModelAndView("redirect:/", modelMap);
}

I have never implemented this myself, but if I remember right, all OAuth2 providers have some kind of a SingleSignOut endpoint, if you call this in your logout method, it will log the user out from every app that is connected to this provider.
After refreshing the page of your webapp, the security should recognize that the user is then no longer logged in and redirect him to the login page.
Hope I could help you a bit. :)
Edit: I found this after a quick search: https://github.com/juanzero000/spring-boot-oauth2-sso .

Related

Swagger-ui Spring Security and OAuth2 project Logout button doesn't clean Session cookies

I am working for microservice (rest API) with Spring boot, security and OAuth2 project with swagger 2.
Although it's a Rest service project but it creates a session cookie when a user login and after logout cookie should be removed and when any user tries to access the APIs need to put an access token again but the cookie remains the same.
So the same users can access API again without login after refreshing.
Solution:
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
add this line on WebSecurityConfigurerAdapter bean It's work for me.
OR
For MVC:
.and()
.logout().clearAuthentication(true)
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID","remember-me")
.invalidateHttpSession(true)
.and()

Spring security user manual login session creation without password by admin

I am building a web application with spring, hibernated for backend and I am using html,css, javascript jquery forfrontend . I have created signup page, login page and home page. The flow is, User creates account and logins with username and password and if he is authenticated then he is redirected to home page. We do not store password in plaintext form for security reasons. Now I am the administrator and creator of the web application and sometimes a need arises for admin to change data for user or demonstrate what user can do in the interface. What I need to do is create a login session of the user and make changes in his account and/or demonstrate how user can do things on the website(by sharing screen). I want to create a user's session manually, as password is stored in plaintext form I can not login with username and password. Is there a way I can create browser login session without password. I am sharing screenshots of my web applications login page and home page. I am also sharing spring security configuration class. Is there a way I can just specify a username and spring can create a login session for me and I can access user's account just like a normal user session.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// add a reference to our security data source
#Autowired
private DataSource myDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(myDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/signup_page","/forgot_password","/signup","/reset_password").permitAll()
.antMatchers("/resources/**").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login_page")
.loginProcessingUrl("/authenticateTheUser").permitAll()
.defaultSuccessUrl("/home_page")
.and()
.logout()
.logoutSuccessUrl("/login_page?logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.permitAll()
.and()
.sessionManagement()
.sessionFixation()
.migrateSession()
.invalidSessionUrl("/login_page")
.maximumSessions(3)
.expiredUrl("/login_page?logout");
}
}
below are the images of my web application.
Two concepts that you may want to look into are:
Pre-Authentication, normally for cases where you are behind a gateway that performs authentication prior to your application (see RequestHeaderAuthenticationFilter)
Switch User for cases where an ADMIN needs to impersonate a USER (see SwitchUserFilter)
Both of these require careful consideration and proper use so as not to accidentally open you up to bypassing authentication entirely. If you're just doing this in a development environment, enabling pre-authentication by adding a RequestHeaderAuthenticationFilter could work for you.

Spring Security: how to recognize, in a public page, that a user is authenticated

I have a simple Spring Boot web application consisting of 2 pages:
a Home Page (freely accessible) at the url https://example.com/
a secured page (requires login for being accessed) at the url https://example.com/secure/page.html
In the Home Page I'm printing the First Name of the visiting user (if he/she is already authenticated) or a sentence saying that the page is visited by an anonymous user.
I'm using Keycloak as far as authentication is concerned.
Here the Spring Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secure/**")
.authenticated()
.and()
.csrf().requireCsrfProtectionMatcher(keycloakCsrfRequestMatcher())
.and()
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/");
}
If the user is already authenticated, the problem is that the Home Page says he is anonymous because the Principal is always null.
But if the user enters the secured page (and Keycloak lets him in because he's already authenticated) when he comes back to the Home, the page contains - correctly - his First Name.
Where is my configuration wrong?
It seems that Spring Security doesn't check the authentication on non secured pages. Is there a way to tell Spring Security to check every page (both secured and non-secured)?
Thanks in advance for your support.
The solution to this problem is to add /** to security context/handling (with permitAll()).
The art is to do it correctly:
Multiple antMatchers in Spring security (First ant matcher wins!!, https://www.google.com/search?q=spring+security+permitall+not+working)
http://blog.florian-hopf.de/2017/08/spring-security.html
So in this case:
http
.authorizeRequests()
.antMatchers("/secure/**").authenticated()
.antMatchers("/**").pernmitAll()
.and()...
...should fill the (anonymous) Principal also in "permitted area" (i.e. /**(!) ...and leave secure/** restricted!;).
To the core/title question (once Principal is filled), i think the answer is already given:
here (verbally): https://stackoverflow.com/a/26117007/592355
and here(with code): https://stackoverflow.com/a/57054816/592355
..if you use Spring Security (JSP) Taglibs isAnonymous() is handy, and otherwise (in default) you just need to check for hasRole('ROLE_ANONYMOUS') (Ref)

How do I modify spring boot security redirect on login to not redirect back to login?

We have a spring boot application configured as an oauth2 client. Occasionally, we have people where their browser sends a request like this to the application:
https://app/login?code=XXX&state=ZZZ
The code and state were cached from a previous authentication attempt and are invalid right now.
Spring security sees that this person is not authenticated, so it redirects them to /login which then does the whole oauth2 authentication but then after they are authenticated, spring security sends them back to /login?code=XXX&state=ZZZ because that was their original request. When that happens, it tries to validate the code and state but fails and sends them to an error page. This is a problem when supporting the app because the user is authentcated.
Is there a way to change the logic of the the storing of the initial request so that if it is /login we can replace that with /? There might be other solutions we haven't thought of. Any help would be appreciated.
Our app is currently using Spring boot 2 but I've tried this with the latest version of Spring boot 3 and it is still an issue. We have been unable to change the browser behavior so would like to solve this on the server if possible.
Here is our configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/info", "/static/**").permitAll()
.anyRequest().authenticated().and()
.csrf();
}
If I understand you correctly, you want to avoid redirect only sometimes (so SpringSecurity's defaultSuccessUrl is not an option).
If so, you can implement your own AuthenricationSuccessHandler like this:
...
.successHandler(
(request, response, authentication) -> {
if (request.getRequestURI().equals("/your/invalid/path"))
response.sendRedirect("/");
}
...

Spring OAuth2 + Http basic redirection with token

I'm using OAuth2 Spring setup from official spring guide - full blown authorization server part.
There are two apps - separate authorization server with user resource server embedded and a client application - using JWT OAuth.
By default if you want to navigate to protected resource of the client app you get redirected to authorization server app where you can choose which authentication provider you'd like to use for the session. The problem is I want to support also local login mechanisms.
I managed to introduce a simple login form which just gets from /user resource with Basic authentication which works fine except there is no redirection back to the resource which initiated the process in the first place and no JWT token is being issued.
Normally I would get redirected with JWT token but I guess basic authentication doesn't even have authentication success handlers not to mention SavedRequestAwareAuthenticationSuccessHandler which OAuth2ClientAuthenticationProcessingFilter seems to be using after successfully logged in.
Here's my initial idea:
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
http.antMatcher("/**")
.httpBasic()
.authenticationEntryPoint(LoginUrlAuthenticationEntryPoint("/"))
.and()
.authorizeRequests()
.antMatchers("/", "/login**", "/webjars/**").permitAll()
.antMatchers("/registration/**").permitAll()
.antMatchers(HttpMethod.POST, "/auth/account/password/reset*/**").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.and().logout()
.logoutSuccessUrl("/").permitAll()
// .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().csrf().disable() // todo consider how to enable this only for parts of the service which is exposed to the web browser
.addFilterBefore(createClientFilter(), BasicAuthenticationFilter::class.java)
}
And somewhere at the end of the chain
val oldRedirectUrl = HttpSessionRequestCache().getRequest(request, response).redirectUrl
DefaultRedirectStrategy().sendRedirect(request, response, oldRedirectUrl) // all those should be in authentication success handler for basic auth
The only problem is that once the user is authenticated at auth server (port 9998) and gets redirected to initial application (port 9999) he gets the following error:
The second time he does it (when he is already authenticated) it works fine. Read somewhere that the issue might be with apps stealing each others cookies so I renamed the cookies using
server:
session:
cookie:
name: AUTH_SESSION
Config option. The resulting cookies (after authentication) under localhost domain are:
What is interesting AUTH_SESSION cookie changes its value after signing in.
Additionally I have no idea where JSESSION_ID came from. Maybe that's the problem here?
By the way it works perfectly when using formLogin auth.

Resources