Combine Saml2 and oAuth2 in the same authentication module - spring-boot

I have developed an authentication module in spring-boot based on spring-security that allowed user authentication via oAuth2 to external systems such as AAD, ADFS ...
Everything works correctly but a new client requests the use of Saml2 as an integration protocol.
Currently the module consists of the following parts
SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
// only allow access to specified URIs
.authorizeRequests()
.antMatchers("/auth/**", "/oauth2/**", "/public/**")
.permitAll()
// only allow access with fully authenticated requests
.anyRequest()
.fullyAuthenticated()
.and()
// configure OAuth2 login
.oauth2Login()
// configure token endpoint for hack
.tokenEndpoint()
.accessTokenResponseClient(getAccessTokenResponseClient())
.and()
// endpoint for authorization (the endpoint we expose and knows the third party to go to)
.authorizationEndpoint()
.baseUri(OAUTH2_AUTHORIZE_BASE_URI)
.authorizationRequestResolver(oauth2AuthorizationRequestResolver)
.authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
.and()
// endpoint for callback (where the third party service calls back after authenticating a user)
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
// the service to use
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
Application.yaml
spring:
security:
oauth2:
client:
registration:
example1:
clientId: -----------------
clientSecret: -----------------
redirectUriTemplate: -----------------
grant-type: authorization_code
authorizationGrantType: authorization_code
tokenName: code
authenticationScheme: query
clientAuthenticationScheme: form
example2:
clientId: -----------------
clientSecret: -----------------
tenant-id: -----------------
active-directory-groups: -----------------
redirectUriTemplate: -----------------
grant-type: authorization_code
authorizationGrantType: authorization_code
tokenName: code
authenticationScheme: query
clientAuthenticationScheme: form
My doubts regarding the integration with Saml2 are the following:
It is possible to combine both authentications in the same application, could you have something like this in Application.yaml?
spring:
security:
saml2:
relyingparty:
registration:
aad:
identityprovider:
entity-id: -----------------
verification.credentials:
- certificate-location: "classpath:certs/aad.cert"
singlesignon.url: -----------------
singlesignon.sign-request: false
okta:
identityprovider:
entity-id: -----------------
verification.credentials:
- certificate-location: "classpath:certs/okta.cert"
singlesignon.url: -----------------
singlesignon.sign-request: false
oauth2:
client:
registration:
example1:
clientId: -----------------
clientSecret: -----------------
redirectUriTemplate: -----------------
grant-type: authorization_code
authorizationGrantType: authorization_code
tokenName: code
authenticationScheme: query
clientAuthenticationScheme: form
example2:
clientId: -----------------
clientSecret: -----------------
tenant-id: -----------------
active-directory-groups: -----------------
redirectUriTemplate: -----------------
grant-type: authorization_code
authorizationGrantType: authorization_code
tokenName: code
authenticationScheme: query
clientAuthenticationScheme: form
If the previous configuration is possible, how would it be represented in "SecurityConfig.java -> configure (HttpSecurity http)"? Is it possible to enter saml2Login in the current configuration?
I have seen some incomplete examples where they talk about the use of "authenticationProvider" to implement this type of case. Does anyone know if this is effective?
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(oauth2AuthenticationProvider());
auth.authenticationProvider(saml2AuthenticationProvider());
auth.authenticationProvider(DDBBAuthenticationProvider());
}
Thanks for your help!

A key thing to a for is that your apps shouldn't need to know anything about SAML, as an old technology. You will want your UIs and APIs to be modern and use OAuth tokens.
Can't answer this one.
Sounds like you should be using an Authorization Server (AS) rather than doing too much in your own authentication module. Third party systems have built in support for many providers and have taken years to develop.
As an example see all of these options supported by the Curity product, which has a free community edition that you can download.
In terms of the general pattern:
Your apps speak OAuth and OpenID Connect and interact with the AS
If a business partner wants to use SAML logins against their own Identity Provider, you only need to make SAML config changes in the AS - with zero code changes to your apps

Related

Spring Oauth2 Login not working after migrating to Spring Boot 3

After migration to Spring Boot 3 from 2.7.5, when trying to login and get into infinity loop in the login screen.
After debugging we found this exception:
org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_request] client_secret is must in DefaultAuthorizationCodeTokenResponseClient.getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest)
You can check our how is defined our SecurityFilter chain.
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(new CsrfIgnoreRequestMatcher())
)
.headers(headers -> headers
.cacheControl().disable()
.frameOptions().disable()
)
//Access configuration
.authorizeHttpRequests(authorizeRequest -> authorizeRequest
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers(
LOGIN,
LOGOUT).permitAll()
)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new Http401UnauthorizedEntryPoint())
)
//######## OAUTH2-Login configuration ########
.oauth2Login(oAuth2Login -> oAuth2Login
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint
.baseUri(LOGIN)
.authorizationRequestResolver(customOAuth2AuthorizationRequestResolver)
)
.loginProcessingUrl(LOGIN)
.userInfoEndpoint(userInfo -> userInfo.userAuthoritiesMapper(new RoleMapper()))
)
.logout(logout -> logout
.logoutUrl(LOGOUT)
.invalidateHttpSession(true)
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
);
return http.build();
}
Here are our application.yaml properties for the security:
spring:
security:
oauth2:
client:
provider:
customIdp:
authorization-uri: https://sso.company/app/login
jwk-set-uri: https://sso.company/oauth/nam/keys
token-uri: https://sso.company/oauth/nam/token?resourceServer=IdentityProviderRSUE&
user-info-uri: https://sso.company/oauth/nam/userinfo
user-name-attribute: cn
customIdpSso:
authorization-uri: https://sso.company/app/login
token-uri: ${spring.security.oauth2.client.provider.customIdp.tokenUri}
user-info-uri: ${spring.security.oauth2.client.provider.customIdp.userInfoUri}
user-name-attribute: ${spring.security.oauth2.client.provider.customIdp.userNameAttribute}
registration:
customIdp:
authorizationGrantType: authorization_code
clientAuthenticationMethod: basic
client-id: custom-client-id
clientName: Custom
client-secret: custom-client-secret
provider: customIdp
redirect-uri: "{baseUrl}/api/login"
scope: portal
customIdpSso:
authorizationGrantType: ${spring.security.oauth2.client.registration.customIdp.authorizationGrantType}
clientAuthenticationMethod: ${spring.security.oauth2.client.registration.customIdp.clientAuthenticationMethod}
clientId: ${spring.security.oauth2.client.registration.customIdp.clientId}
clientName: ${spring.security.oauth2.client.registration.customIdp.clientName}
client-secret: ${spring.security.oauth2.client.registration.customIdp.clientSecret}
provider: customIdpnosso
redirect-uri: ${spring.security.oauth2.client.registration.customIdp.redirect-uri}
scope: ${spring.security.oauth2.client.registration.customIdp.scope}
We migrated to new Spring Boot version and stoped using WebSecurityConfigurerAdapter.
If you need more information please tell us.
Due to https://docs.spring.io/spring-security/reference/5.8/migration/servlet/oauth2.html#_clientauthenticationmethod, the value for clientAuthenticationMethod should now be:
clientAuthenticationMethod: client_secret_basic
UPDATE: I've created https://github.com/spring-projects/spring-security/issues/12585 to look into making this clearer.

Spring Boot, OAuth2 with azure and another provider (e.g. google)

I have an application that is using Azure Active directory to authenticate and I need to add another provider, for example google.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends AadWebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.anyRequest().authenticated();
}
}
spring:
cloud:
azure:
active-directory:
enabled: true
profile:
tenant-id:
# credential:
client-id:
client-secret:
security:
oauth2:
client:
registration:
google:
client-id:
client-secret:
Using above code will force azure login.
How can I adapt the code to have both options azure and google?
I did not manage to make it work with spring-cloud-azure-starter-active-directory, so I removed this plugin and used:
spring:
security:
oauth2:
client:
registration:
google:
client-id:
client-secret:
azure:
client-id:
client-secret:
scope:
- openid
- profile
- email
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/azure
provider: azure-active-directory
provider:
azure-active-directory:
issuer-uri: https://login.microsoftonline.com/{tenant-id}/v2.0
This example helped me https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_v4.4.1/aad/spring-security/servlet/oauth2/login/src/main/resources/application.yml

spring boot Oauth Okta 404 on redirect

My Spring Boot app giving me an error as 404 after redirecting from the Okta server.
May I have some help to solve this. My configurations are as below.
This is my Okta server redirect
https://dev-40483106.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oa3p7a86ycJ7olpP5d7&scope=openid%20email&state=xVpkZIPGaGqAQCZIMXZgTrbmKmHJbcKbtM95KO9RwVU%3D&redirect_uri=http://localhost:8082/mms-sso/auth&nonce=cPdNAYT4tQ9mAjhj4HUiynBEQVAnJHnBTIezqquOpJM
This is my Redirect URI where I am getting a 404 error
http://localhost:8082/mms-sso/auth?code=rOh9u2fwBTB7gasIMUgtjIDMs5_Sydqz-0O_jG5Qhj0&state=xVpkZIPGaGqAQCZIMXZgTrbmKmHJbcKbtM95KO9RwVU%3D
Below is the screenshot
[![error-404][1]][1]
spring:
security:
oauth2:
client:
registration:
okta:
client-id: clientId
client-secret: secret
scope: openid, email
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8082/mms-sso/auth
provider:
okta:
issuer-uri: https://dev-40483106.okta.com/oauth2/default
authorization-uri: https://dev-40483106.okta.com/oauth2/default/v1/authorize
resource:
server: http://localhost:8081
server:
port: 8082
servlet:
context-path: /ui-one
security config
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login**", "/auth/**",
"/auth?**").permitAll().anyRequest().authenticated().and()
.oauth2Login();
Okta config
Sign-in redirect URIs - http://localhost:8082/mms-sso/auth
Sign out redirect URI - http://localhost:8082/mms-sso/, http://localhost:8082/mms-sso/logout
Controller for redirect
#GetMapping("/auth")
String home(#AuthenticationPrincipal OidcUser user) {
return "Welcome to MMS SSO";
}
Your controller mapping is for /auth but in redirect you specify it as /mmo-sso/auth.

Spring Boot OAuth2 login endpoint not working. Getting 404 when hitting default path

I want to implement OAuth2 in my Spring Boot App.
I added
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>2.3.7.RELEASE</version>
</dependency>
Basic config for GitHub (for example):
spring:
security:
oauth2:
client:
registration:
github:
client-id: xxx
client-secret: xxx
And basic WebSecurity config allowing all endpoints:
#Configuration
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.antMatcher("/**").authorizeRequests()
.antMatchers("/**").permitAll()
.and()
.oauth2Login();
}
}
When client-id/secret is not provided, an exception occurs so the config is picked up properly.
But I can't access the oauth login page which should be http://localhost:8080/oauth2/authorization/github, I have an 404 instead.

Spring Boot with OAuth2 behind reverse proxy

I'm new with Spring Security and trying to develop Spring Boot app with Google login using OAuth2 which runs under hostname:8080. This app is behind Apache reverse proxy server https://url.com.
Spring Boot version 2.1.0
Spring Security version 5.1.1
build.gradle:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security:spring-security-oauth2-client")
implementation("org.springframework.security:spring-security-oauth2-jose")
}
application.yml:
oauth2:
client:
registration:
google:
clientId: <clientId>
clientSecret: <clientSecret>
scope: profile, email, openid
server:
use-forward-headers: true
servlet:
session:
cookie:
http-only: false
Spring Security config:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login();
}
}
I request https://url.com
Get redirected to https://accounts.google.com/signin/oauth/
When authenticated get redirected back to
https://url.com/login/oauth2/code/google?state={state}&code={code}&scope=openid+email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me&authuser=0&session_state={session_state}&prompt=none which timed out with error:
[invalid_token_response] An error occurred while attempting to
retrieve the OAuth 2.0 Access Token Response: I/O error on POST
request for "https://www.googleapis.com/oauth2/v4/token": Connection
timed out (Connection timed out); nested exception is
java.net.ConnectException: Connection timed out (Connection timed out)
Is this error caused by the proxy server settings or boot app? Thanks for help.
Solved. I had to set the JVM parameters:
https.proxyHost=[host]
https.proxyPort=[port]
http.proxyHost=[host]
http.proxyPort=[port]

Resources