How to single sign out on Spring mvc application enabled SSO using Spring OAuth2? - spring

I'm developing a spring web app with SSO architect by using Spring OAuth2
2 spring mvc web apps
1 authorization server is responsible for SSO and issue/check tokens
1 resources server serving /api/me
Everything works well when single signing on, but I don't know how to Single Sign Out (1 app logs out, other apps log out as well). Currently, it seems that every apps with mimimum config like code snippet below is using different session storage, and after successful authentication, it saves the authenticated session in its own session storage, and every subsequent requests is intercepted with the session in cookie. So when sso-server logging out it cannot invalidate related sessions on others app session storages. Anyone could suggest me some strategies for Single Sign Out?
Web client application.yml file:
security:
oauth2:
client:
clientId: metlife_monitor
clientSecret: password
accessTokenUri: http://localhost:8668/sso-server/oauth/token
userAuthorizationUri: http://localhost:8668/sso-server/oauth/authorize
tokenName: oauth_token
resource:
userInfoUri: http://localhost:8688/api/me
Web Application class:
#SpringBootApplication
#EnableOAuth2Sso
#EnableJdbcHttpSession
public class Application extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**", "/error**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and().logout()
.logoutUrl("/logout")
.logoutSuccessUrl("http://localhost:8668/sso-server/logout")
.invalidateHttpSession(true)
.deleteCookies("client-session", "JSESSIONID")
.permitAll()
.and().csrf().disable()
;
}
public static void main(String[] args) {
SpringApplication.run(App1Application.class, args);
}
}

There's difference between a Single-Sign-On(SSO) server and a Central-Authentication-Server(CAS). If the your auth server does not manage any sessions, it stops being an SSO server and just becomes a CAS.
In your case you can have your SSO server manage sessions and it can also distribute a session id that Resource Servers would verify with it before servicing the request. It does lead to a chatty system though. If you can live with a little latency post session-expiry, then you can make very short-lived tokens that are attached to the SSO and would not have to verify the token with every request, you'd only refresh a token is there's still a valid session.
If you can, consider using Keycloak. It has adapters for Java and Javascript applications as well

Related

Handling authentication of microservice at Gateway service in Spring boot

I am learning authentication at gateway service in a microservices architecture. What i understood is
When the client makes a request, it needs to have a valid access token
The authentication of the requests is happening at the gateway level, the authorization at the microservice application level
Request is processed if authentication is success (Jwt token is valid)
My Questions are :
Is it really needed to have authentication at gateway service as well as individual microservice ? Because its redundant to have the same logic at both places (JWT Validation)
If not (only at gateway service), how can the individual microservice can be protected if the call is not via gateway ?
Assuming that the Microservices are accessed only via the gateway, the authentication can be delegated to the Gateway that then send the informations relative to the caller to the recipient microservice(via an header for instance).
It is important to restrict the access to the microservices to the Gateway only.
This can be done at network level if there is a firewall or a router in between or via code configuring the microservice with soething like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/foos/**").hasIpAddress("11.11.11.11")
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.csrf().disable();
}
// ...
}
This is the whitelisting part. Then you need to verify how to pass the information of the logged in user to the microservices from the Gateway. If you need more infow about the white listing check this article: https://www.baeldung.com/spring-security-whitelist-ip-range

Google drive API access for aplication testing

I am working on an application which uses Google Oauth2 token and the purpose of application is to download specific file from logged user's Google Drive. I am wondering if there is any chance to test that before going to production environment. I've enabled Google Drive API in Google Cloud Platform created Oauth 2.0 Client ID.
I've got web application created using Java and Spring and authorisation works good. I do receive Oauth 2.0 token from Google Sign In but when I add a scope for Google drive which is the same one added in GCP I do receive following error:
Below is spring security google scope:
spring.security.oauth2.client.registration.google.scope=https://www.googleapis.com/auth/drive.readonly
And the spring security config:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/styles/**", "/webjars/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/goodbye");
}
}
I found some information that if the registered application in GCP has sensitive scope it should goes through validation process but I want to test it first before going to production that's why I've left that in Testing Oauth consent and added testing user but it doesn't work.
Any ideas what might be wrong?
Reproducible example:
Register app in GCP.
Enable Google Drive API and generate Oauth2 credential.
Add auth/drive.readonly scope.
Set consent status to testing and add testing user.
Create Spring Boot app with Spring Security.
Add scope:
spring.security.oauth2.client.registration.google.scope=https://www.googleapis.com/auth/drive.readonly
Run app and authenticate with Google Sign in.

Multiple Authentication Provider in SpringBoot

In my SpringBoot application, I am trying to implement two different authorizations for two different areas.
Area 1 [API]:
/api/**
Area 2 [Admin]:
/admin/**
The Area 1 [API] is the API part of my application where I have implemented JWT Authentication. Every request that starts with /api will require an Authorization header containing jwt token.
The Area 2 [Admin]: is the admin area. Where I would like to log in with an URL from the browser, For example (/admin/login). I would like to have my username and password saved in the application.properties and for any URL that starts with /admin, I want the user to be authenticated (Session-based). I want to apply in-memory authentication in that case. I am looking for ideas to implement these two different authentications for two different areas.
You can use .authorizeRequests() method to configure endpoints for that security configuration file.
In void configure(HttpSecurity http) method in your WebSecurityConfigurerAdapter or ResourceServerConfigurerAdapter file you can use like
http
.antMatcher("/api/**")
.authorizeRequests()
...
Then spring security will start authorizing requests starting with /api path.
If added it in ResourceServerConfigurerAdapter spring oauth2 will start authorizing from there. To configure web security for an endpoint like /admin, in your WebSecurityConfigurerAdapter
http
.antMatcher("/admin/**")
.authorizeRequests()
...

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 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