How to setup ForwardedHeaderFilter for login using Spring Security without Spring Boot? - spring

I am looking to setup the ForwardedHeaderFilter in spring security so I can let spring know which protocol to use after login. I have several app servers behind a load-balancer (using ssl termination) and spring security is redirecting the user using http (instead of https). Because of this, my users are now getting a obtrusive warning message. The only examples I can find online are with spring boot which I do not implement.
I thought of using "addFilterBefore()" method to my security configuration, but the filter is never called.
Any ideas?
// Apply sameOrigin policy for iframe embeddings
http.headers().frameOptions().sameOrigin();
// ********* Add filter here? *******
http.addFilterBefore(new ForwardedHeaderFilter(), ChannelProcessingFilter.class);
// Authorization filters
http.authorizeRequests().antMatchers("/sysAdmin/**", "/monitoring/**").access("isFullyAuthenticated() and hasRole('GOD')");
http.authorizeRequests().antMatchers("/app/**").authenticated();
http.authorizeRequests().antMatchers("/**").permitAll();
http.formLogin()
.loginPage("/public/login.jsp")
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/app/Dashboard.action", false)
.failureHandler(customAuthenticationFailureHandler());
// Disable so that logout "get" url works (otherwise you have to do a html form)
http.csrf().disable();
http.logout().logoutSuccessUrl("/public/login.jsp");
http.sessionManagement()
.invalidSessionUrl("/public/expiredSession.jsp?expiredId=2")
.maximumSessions(2)
.sessionRegistry(sessionRegistry())
.expiredUrl("/public/expiredSession.jsp?expiredId=3");

I ended up adding the filter like this and everything seemed to work
// Added for load balancer headers (X-Forwarded-For, X-Forwarded-Proto, etc)
http.addFilterBefore(new ForwardedHeaderFilter(), WebAsyncManagerIntegrationFilter.class);

Related

Spring SAML SSO with OKTA - InResponseTo when changing web app context

We're having a lot of trouble with OKTA SAML SSO integration with Spring Security. We're using the saml-dsl extension to Spring Security to configure the auth, and everything works fine on HTTP, however when we try to use HTTPS the authentication only works when the app is deployed on root (/) context. When we change the context to anything else, it stops working and starts throwing InResponseTo field errors and sometimes with different configurations it comes to a redirect loop.
Here's the configuration we're using:
http
.csrf()
.disable();
http
.sessionManagement().sessionFixation().none();
http
.logout()
.logoutSuccessUrl("/");
http
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(samlConfigurer())
.serviceProvider()
.keyStore()
.storeFilePath(config.getKeystorePath())
.password(config.getKeystorePassword())
.keyname(config.getKeyAlias())
.keyPassword(config.getKeystorePassword())
.and()
.protocol("https")
.hostname(String.format("%s:%s", serverURL, config.getServerPort()))
.basePath("/"+contextRoot)
.and()
.identityProvider()
.metadataFilePath(config.getMetadataUrl());
And we should have our OKTA setup properly as well ( https://localhost:8443/ourappcontext/saml/SSO for testing, all the other stuff too )
We've tried most of the solutions proposed on here and the Spring documentation ( empty factory, spring session management and session fixation and so on ) and nothing seems to work. Are we doing something wrong? We're currently not generation any SP metadata, could it be that this is at fault and the request is somehow redirected to the wrong place or something? Really confused as of right now, first time using SAML and I'm not sure if it's the extension, the OKTA config or the Spring config...
We're deploying on Wildfly and you set the application context on there through a separate jboss-web.xml, if that matters at all.
By default the HttpSessionStorageFactory is used and this provides HttpSessionStorage SAML message store.
--> The HTTP session cookie is the browser side key to the server side SAML message store.
When the HTTP session cookie is not sent by the browser when the SAML response is delivered to Spring Security SAML SP, it can not find the related SAML AuthNRequest and fails to compare 'InResponseTo' value with 'ID' value of matching AuthNRequest.
If you can not assure HTTP session cookie is delivered you may implement your own SAMLMessageStorageFactory and SAMLMessageStorage (as I did).

Configure communication between multiple OAuth2 authorization servers and a single resource server

I'm currently setting up a single resource server that will be validating access tokens from various authorization servers.
Spring security (using the Okta security starter with this as well) seems to only allow me to set a single issuer URI.
I managed to find a solution that works but I'm unsure if this is the best practice/standard way of doing it. In the code snippet below I've explicitly setup the resources with Spring's Java Config for simplicity.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers("/api/protected/by/authserver1")
.and()
.oauth2ResourceServer()
.jwt()
.jwtDecoder(ReactiveJwtDecoders.fromOidcIssuerLocation("https://authserver1")
.and()
.and()
.authorizeExchange()
.pathMatchers("/api/protected/by/authserver2")
.and()
.oauth2ResourceServer()
.jwt()
.jwtDecoder(ReactiveJwtDecoders.fromOidcIssuerLocation("https://authserver2");
return http.build()
}
This seems to work exactly as intended, tokens minted from one auth server and used on the endpoint validating the other receive 401. When the minted tokens are used on their respective endpoint, they are successfully validated.
It looks a little funny having .and() calls back to back, I'm under the impression that these chained calls are just creating multiple web filters under the hood? Either way, is this the standard way of enabling this functionality in a Spring application with Spring Security and WebFlux?
Additionally, I came across this SO question but I don't know that I'll be able to setup a 'federation provider' within the context of this project. However, If that approach is the best practice I'd like to know. However, I think that's happening to some extent at the Okta level with the federation broker mode on the auth server access policies...?
Either way, is this the standard way of enabling this functionality in a Spring application with Spring Security and WebFlux?
No. What's more the example you've provided won't work. You can investigate the ServerHttpSecurity implementation and see why. Actually when you call oauth2ResourceServer() it sets new OAuth2ResourceServerSpec or returns the old one which can be modified. So in your case only the second JwtDecoder will be applied, because it overrides the first one. If you want to configure oauth2ResourceServer per path you'll have to define multiple SecurityWebFilterChain as posted here https://stackoverflow.com/a/54792674/1646298 .

Receive Authorization header on anonymous url using Spring Boot

How can an Authorization header be accessed on anonymous urls?
My security configuration looks like:
http
.authorizeRequests()
.antMatchers("/login", "/legacy-login").anonymous()
.antMatchers("/things/*").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
Authentication in general works fine. However, on /legacy-login I need to do some migration and need to access the authorization header without spring boot managing the authorization. Although /legacy-login is marked anonymous as soon as there are requests, spring intercepts the request and tries to authorize itself (what then results into 401).
How can I make Spring let the auth header through on that single url?
I foudn one solution myself. Instead of fiddleing around with .anonymous() and .permitAll() I added /legacy-login as ignore rule:
override fun configure(web: WebSecurity) {
super.configure(web)
web.ignoring()
.antMatchers("/legacy-login")
}

Using SAML with Spring Boot behind an ELB redirects to http instead of https

I'm trying to use Okta to authenticate users from a SpringBoot application.
I've setup the app following the Okta Tutorial from : https://developer.okta.com/blog/2017/03/16/spring-boot-saml
However my app is behind an ELB, and as such the TLS is being terminated at the LB. So I've modified the configuration from the tutorial to suit my needs.
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.serviceProvider()
.keyStore()
.storeFilePath(this.keyStoreFilePath)
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
.hostname(String.format("%s", serverName))
.basePath("/")
.and()
.identityProvider()
.metadataFilePath(this.metadataUrl);
}
This does the trick but there is a problem. After the user is authenticated by Okta, the user is finally redirected to a http URL instead of a https URL. I am thinking the reason for this is that the TLS is being terminated at the LB and my app is actually receiving the request with http which is being sent in the RelayState.
This is something I found : spring-boot-security-saml-config-options.md.
It contains a list of SAML properties for spring boot security. I added the following to the application.properties file
saml.sso.context-provider.lb.enabled = true
saml.sso.context-provider.lb.scheme=https
saml.sso.profile-options.relay-state=<https://my.website.com>
It doesn't change the http redirection. Is there something I am doing wrong?
When a SAML 2.0 IdP like Okta redirects back to you application the endpoint url is either based on the SAML 2.0 metadata you application expose or the configuration in the IdP.
Furthermore, it is optional to add a Destination property in SAML 2.0 AuthnRequest:
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"
Destination="https://my.website.com" IssueInstant="2018-11-22T09:23:08.844Z" Version="2.0" ID="id-f8ee3ab1-6745-42d5-b00f-7845b97fe953">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion"> ... </Issuer>
...
</samlp:AuthnRequest>

Spring Security returns 401 for unsecured URL

Using Spring Security 4.0.3 from a Spring Boot 1.3.3 application.
The application has two types of HTTP contents : "API" a REST API and "UI" a web based used interface (Thymeleaf + Spring Web MVC).
Most endpoints of the REST API of the application are secured, using Basic, but some are not and should always be available.
The simplified configuration looks like:
// In one method, the security config for the "API"
httpSecurity
.antMatcher("/api/**")
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.authorizeRequests()
.antMatchers("/api/ping").permitAll()
.antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic();
// In another method, the security config for the "UI"
httpSecurity
.authorizeRequests()
.antMatchers("/ui/", "/ui/index.html", "/ui/css/*", "/ui/js/*", "/ui/img/*").permitAll()
.antMatchers("/ui/user/**").hasRole("USER")
.antMatchers("/ui/**").denyAll()
.and()
.formLogin().loginPage("/ui/login.html").permitAll().failureUrl("/ui/login.html").defaultSuccessUrl("/ui/user/main.html")
.and()
.logout().logoutUrl("/ui/logout").permitAll().logoutSuccessUrl("/ui/login.html")
.and()
.httpBasic();
Accesses to secured endpoints work as expected.
But accesses to public endpoints such as ".../api/ping" fail with a 401 when the user provided an invalid Basic authentication. Of course such endpoints works fine when no or valid Basic authentication is provided.
This 401 from Spring Security is surprising. How can we implement a Spring Security configuration that never returns any 401 or 403 for selected endpoints?
Thank you for your time.
Update 1 : added info about the "UI" existence and security config
Order is important. Most specific rule (path) first:
httpSecurity
.antMatchers("/api/ping").permitAll()
// and then the rest
This because if there is a match like on antMatcher("/api/**"), Spring Security will not evaluate later rules.

Resources