#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).
Related
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).
For our application with spring-boot (2.1.4) it is important to us that a user can only be logged in once.
This was relatively easy to configure and works great in relation with REST requests.
http
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry());
Much of the communication runs through websockets, and getting here starts our problem.
The view of our browser app changes when the websocket connection is disconnected. On the server side the websocket handshake is disconnected if the http session is invalid.
As I understand it, if a user logs in again with an already logged in user, the http session remains valid until the next REST request. I would like the http session to terminate independently from requests.
I have a workaround where I check every AuthenticationSuccessEvent if there are already sessions of this user and terminate them. This approach works, but I don't like it.
Thanks a lot for your help!
A relatively blind guess, but have you tried changing the sessionFixation? It should create a completely new session, without copying over old attributes, on an authentication attempt.
http.sessionManagement()
.sessionFixation().newSession()
;
I have stumbled upon a problem with Spring Security and Angular.
On my BE (Spring Boot application), there are defined OAuth2 providers, such as Google, GitHub and Facebook.
My BE works fine with this providers, since I can authenticate on the desired providers.
The problem is when I try to send the principal object to the FE (Angular 6 application).
I get undefined value when i try to subscribe the value from the rest endpoint.
I assume this is due to the Spring Servlet creating a new thread for the login request.
I am doing my login request from the Angular app.
I did watch dozens of tutorials and rad so many articles, but I just can't find the answer. If it's possible for you to share some code on how it is done, or give me a link, since for sure I am making a silly mistake and can't seem to find the answer here.
Thanks for understanding, have a good day.
:)
I am assuming that you are using the Authorization Code flow from your BE to authenticate the user that interacts with your FE Angular application (you in your example). Otherwise, you would be trying to authenticate the BE Client with the Client flow and you wouldn't need to return the "principal object" to the FE application. If my assumptions are correct... read on.
The Authorization Code flow goes as follows:
1) The user somehow selects an Authentication provider (ex: Google) and that selection is returned to some endpoint in the BE as a non-authenticated request..
2) The BE Client receives this request, preferably intercepted by a filter and, since the request is not authenticatedd, redirects the browser to the selected auth provider's authorization endpoint.
3) The user then proceed to authenticate against that provider which, upon succesfull authentication, returns a response that redirects the browser to a BE Client endpoint. That redirect holds a parameter that provides a code that the BE Client will use to get an idToken representing the user. At this point, it is important to note that the browser has not been returned any response for this redirect.
4) The BE Client then proceeds to send a regular HTTP request to the provider's token endpoint along with the received authorization code. The provider then returns the idToken an HTTP response directly to the BE Client. All this is happening while the browser is still waiting for the response to the last redirect.
5) The BE Client then process the idToken (verification, validation, user details, session etc) and only then, will finally send the response to the browser patiently waiting since the code redirect. That response may provide a header or a cookie with a sessionId or token (your choice) that the FE application will be able to read or use for the given purpose.
This flow is relatively easy to implement and requires minimal SS configuration. You must keep the BE Client auth endpoint with permitAll() otherwise, you would not be able to trigger this flow. Also, make sure that, once the FE app. has received the header/cookie, all subsequent calls shall be processed as "authenticated calls". Finally, make sure to document yourself on the perils of stateless sessions as well as cookie security and always use HTTPS.
Jake.
Sorry for such novice question.
I am fairly new to web security.
Can someone please explain to me, why do we need JWT token authentication for web api (REST) when I could include { username | email } / password for every single API request?
Mostly, it's a separation of concerns thing. JWTs are a way to authorize a request, whereas username/password is a way to authenticate. The key difference is that authentication is something you should ideally only have to do once, and it should be done by a dedicated endpoint responsible for that. For every other request, you're simply confirming the authorization you received from that initial authentication.
If you were to send username and password with every request, every endpoint then would have to handle authentication logic, which would be a nightmare. Using a JWT, the endpoint can simply verify that it's valid and move on to what it's actually responsible for.
JWTs are just one method of authorization. In a traditional website-style application, this would be handled by a cookie. This then enables the user to login once, and then proceed to browse protected areas of the site without having to login again. The equivalent of what you're suggesting would be essentially like forcing the user to login again everytime they clicked a link, just to view that next page.
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