Is there any implementation of OIDC Session Management and Logout mechanism in Spring Authorization Server for implementing Single Sing On? - spring

I am trying to implement Single Sing On using Spring Authorization Server which is based on oAuth2 and OIDC for Authorization and Authentication respectively, but as per the Spring Authorization Server feature list OIDC support is not fully integrated. Mainly the session management and logout part, I couldn't find. Also if there's any workaround for implementing sso and logout?
Feature List URL: https://docs.spring.io/spring-authorization-server/docs/current/reference/html/overview.html

These features are still on the backlog but are not scheduled yet (as of the time of this writing). See #58 and #266 respectively to follow progress on these features.
Given that there are a number of pieces to the specifications for both of these features, I imagine it would be a bit of a hassle to attempt a fully spec-compliant implementation of them as extensions to SAS (though I'm sure it's possible). Instead, you might pick a minimal subset of the logout feature as a way to get started.
Using Spring Security configuration, you can configure a logout endpoint in a custom way. Here's some pseudocode to get you started:
#Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.logout((logout) -> logout
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/logout", "GET"),
new AntPathRequestMatcher("/logout", "POST")
))
.addLogoutHandler((request, response, authentication) -> {
// Logic to validate an id_token_hint, client_id, etc.
// Throw exception in case of invalid request
})
.logoutSuccessHandler((request, response, authentication) -> {
// Get state and calculate redirect for logout success back to client
// http://127.0.0.1:8080/logout?success&state=...
// new SimpleUrlLogoutSuccessHandler()...
})
);
return http.build();
}
This assumes validation of some kind is implemented to prevent CSRF, denial of service, etc. You might also choose to add a logout confirmation page as a separate endpoint of the auth server that redirects to the logout endpoint when the user clicks a "Confirm" button, etc.
To use this, you simply redirect from your client application to the logout confirmation page, which redirects to the logout endpoint on confirm, which then redirects back to the client (which can then log out of the client automatically).

Related

Allow user with admin role to consume all apis

I configured my API security side using spring security :
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests(authRequests -> {
// Allow all resource to admin
authRequests.antMatchers("*").hasRole(RoleType.Fields.ADMIN);
// Permissions OPTIONS
authRequests.mvcMatchers(HttpMethod.OPTIONS).permitAll();
// Management endpoints
authRequests.mvcMatchers("/management/**", "/docs/**", "/webjars/**").permitAll();
// Business APIs
authRequests.anyRequest().authenticated();
});
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
for other roles, I created a filter to get authoritie, it works fine, the problem is that I want to give the ROLE_ADMIN access to all resource, when I used antMatchers("*") , the api gives 403 code.
Try to disable csrf protection:
http.csrf().disable();
and use anyRequest() instead of antMatchers("*"):
authRequests.anyRequest().hasRole(RoleType.Fields.ADMIN)

Why does setting sessioncreation policy to stateless break my oauth2 app

#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Why does setting SessionCreationPolicy to STATELESS break oauth2 login?
After authenticating with facebook, the app goes on a never ending loop that eventually leads to "localhost redirected you too many times".
The loop goes like this:
Authenticate with facebook and redirect to:
Redirect-Uri - //login/oauth2/code/facebook?code=&state=
Back to facebook authorization-Uri - /oauth2/authorization/facebook
Repeat
This all happens with the SessionCreationPolicy being STATELESS. Can someone explain to my why this happens?
That's expected behavior. OAuth2 Clients need to store the tokens somehow for using them in later requests. By using a stateless session creation policy, every time you call the application, it won't find any token (i.e. it doesn't know you've already authenticated yourself in the previous request), so it will trigger again the authentication flow.
On the other hand, OAuth2 Resource Servers can be stateless, since they don't rely on any session state. Every request sent to an OAuth2 Resource Server from an OAuth2 Client provides an Access Token in the HTTP request header (which is possible because the client stores the tokens in the session).

Spring MVC based application is not landing to default success url while trying to access it using https url but working fine on http based url

While running the spring mvc based application on http, it is working fine but once the application is moved to https based configuration, we are not able to get successful landing url page. The url which are non authenticated is working fine but the authenticated urls are not reachable even after providing correct login credentials.
Please help me with the missing configurations/issues to resolve this problem.
The application is deployed over Jboss server.
Below are the configurations done as part of application.
protected void configure(HttpSecurity http) throws Exception {
System.out.println("I am configure");
http.csrf().disable();
// The pages does not require login
http.authorizeRequests().antMatchers("/login", "/logout", "/resetPassword", "/forgotPassword", "/change-password", "/confirm-account", "/registerPage", "/registerwithemail", "/resources/**", "/logoutSuccessful", "/login/**").permitAll();
http.authorizeRequests().antMatchers("/**").authenticated();
http.authorizeRequests().antMatchers("/home").authenticated();
http.authorizeRequests().antMatchers("/").authenticated();
// /userInfo page requires login as USER or ADMIN.
// If no login, it will redirect to /login page.
http.authorizeRequests().antMatchers("/userInfo").access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");
// For ADMIN only.
http.authorizeRequests().antMatchers("/admin").access("hasRole('ROLE_ADMIN')");
// When the user has logged in as XX.
// But access a page that requires role YY,
// AccessDeniedException will throw.
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
http.headers().frameOptions().sameOrigin();
// Config for Login Form
http.authorizeRequests().and().formLogin()//
// Submit URL of login page.
.loginPage("/login")//
.loginProcessingUrl("/j_spring_security_check") // Submit URL
.defaultSuccessUrl("/home", true)// userInfo (Changed to) home
.failureUrl("/login?error=true")//
.usernameParameter("username")//
.passwordParameter("password")
// For Remember-me
.and().rememberMe().rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository()).tokenValiditySeconds(86400)
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/logoutSuccessful").invalidateHttpSession(true).deleteCookies("JSESSIONID", "remember-me");
}
if there is any channel security for http/https is required, please let me know about the configurations as I'm also traversing internally to different module using ip and port based http url.

Spring Security loginPage Vs loginProcessingURL

what is the difference between loginPage vs loginProcessingURL.
.formLogin().loginPage("/login").usernameParameter("phone-number").passwordParameter("password")
Seems to be loginProcessingURL is like post method once user submits the data in the login page but when I remove also it is working fine. What is the significance of loginProcessingURL and how does it differ from loginPage?
The line loginPage("/login") instructs Spring Security
when authentication is required, redirect the browser to /login
we are in charge of rendering the login page when /login is requested
when authentication attempt fails, redirect the browser to
/login?error (since we have not specified otherwise)
we are in charge of rendering a failure page when /login?error is
requested
when we successfully logout, redirect the browser to /login?logout
(since we have not specified otherwise)
we are in charge of rendering a logout confirmation page when
/login?logout is requested
AND
.loginProcessingUrl("/login/process")
tells Spring Security to process the submitted credentials when sent the specified path and, by default, redirect user back to the page user came from. It will not pass the request to Spring MVC and your controller.
Refer documentation
Purpose of loginPage()
The loginPage() tells the framework where the user will be redirected when login is required. For example when you are not authorized to the page, you get redirected to this page. This page performs the login activity, for example when you implement a loginForm() or oauth2Login() like in my code using Google OAuth2,this page redirects to google login.
http.anonymous().and()
.authorizeRequests().antMatchers("/images**").permitAll().and()
.authorizeRequests().anyRequest().authenticated().and()
.oauth2Login()
.successHandler((request, response, authentication) -> {
request.authenticate(response);
})
.loginPage("/oauth2/authorization/google")
.loginProcessingUrl("/login")
Purpose of loginProcessingUrl()
The loginProcessingUrl() is the method that automatically set the rule antMatchers("/thisUrl").permitAll() to this URL so that when the response is returned (code, state, token, etc.) will be allowed to be GETed and this response is processed as you can see in the authenticate method of the request. Something more important is that this loginProcessingUrl() tells that the response should be processed to this URL. Without this the request.authenticate(response) will not be executed and authentication will not be returned or otherwise you implement another algorithm.
May the following code segment from spring security source code will help you:
loginPage the login page to redirect to if authentication is required
loginProcessingUrl the URL to validate username and password
DEFAULT_LOGIN_PAGE_URL = "/login"
/**
* Updates the default values for authentication.
*
* #throws Exception
*/
protected final void updateAuthenticationDefaults() {
if (loginProcessingUrl == null) {
loginProcessingUrl(loginPage);
}
//...
}

CSRF token expires during login

I'm working on Spring web application and I need to avoid problem with expire csrf token on login page, because if user is waiting too long and try to login only one way to resolve problem with csrf is to reload page and try to login again. But it's not user friendly and I want to avoid this situation.
First question: Is it possible in general(by spring security 3.2.4)? Without disable csrf.
I tried to use security="none" for login page and spring seciruty "login_check", but it's not working, i got infinity redirect or I got error that no mapping for url "myhost/login_check".
Second question: How can i do it?
Recommended solution
I would say that you should not disable csrf tokens on a production site. You may make session (and thus the csrf token) last longer (but it usually should not last longer than a day, especially for not-logged-in users as it is a DOS vector), but the real solution may be to automatically refresh the login page when the csrf token expires. You may use a
<META HTTP-EQUIV="REFRESH" CONTENT="csrf_timeout_in_seconds">
in your login page header. If the user lets the login page sit for hours, it should not bother him that the page got refreshed.
Second solution
A possible solution which does not require you to actually store sessions but allows for infinite timeout is that you can generate your csrf tokens with hashing from the session id and a server-side secret:
csrf = hash(sessionid+secret)
Note however that you need to really dig and override spring-security internal mechanisms, namely:
re-creating anonymous sessions on the fly if a request arrives and no such session exists
re-creating the csrf token on the fly from the session id
And choose a very secure hashing algorithm, preferably sha-512.
Third solution
You could have a small javascript that calls a no-op page on your server regularly (just before the session timeout), thus extending your session. This results in infinite session timeout only if the browser is on all the time, so the DOS aspect is mitigated.
Ok, one last solution
You can alter the CSRF token checking code, and disable it for the login page. This is actually synonymous with the second solution, but is specific for the login page, not generally for all anonymous sessions.
You can do this e.g. by setting a custom RequestMatcher in HttpSecurity:
http.csrf().requireCsrfProtectionMatcher(new MyCsrfRequestMatcher());
...
class MyCsrfRequestMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest request) {
return !request.getServletPath().equals("/login");
}
}
Another option would be set no timeout for the session by default and then, when the user is authenticated, change the timeout to whatever you want. You can see an example of how to do this here.
In one of the projects I worked on, I implemented the following:
Implement an exception handler which handles CsrfException (or AccessDeniedException in general in my case). Forward the request to a controller method.
#ExceptionHandler(AccessDeniedException.class)
#ResponseStatus(value = HttpStatus.FORBIDDEN)
public void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
request.getRequestDispatcher("/Access_Denied").forward(request, response);
}
In the controller method, check whether the original request is for the login page. If so, show an appropriate message within the login page.
if ("/login".equals(request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH))) {
model.addAttribute("error", "An invalid security token has been detected. Please try again.");
return "login.jsp";
} else {
return "accessDenied.jsp";
}
With this approach, user will be able to retry the login without the need to refresh.
You can also make your CSRF protection rely on cookies and NOT server side session state. Spring Security has full support for this.
CookieCsrfTokenRepository
You will only receive a timeout if your cookie expires. This scales well since it's basically stateless (from the server's perspective).
#EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
Andrew

Resources