Spring Boot Google Oauth2 all requests return a 401 Unauthorized - spring

I have a very simple Spring Boot app where I want all pages to be authenticated thtough Google Oauth2. I followed the Spring Oauth2 tuotrial and looked at the code under the /simple implementation. (My application.yml file is setup for Google instead of FB)
Any request to my app returns a 401 Unauthorized response, and goes to localhost:8080/login... (The Spring security auto generated login page, which is set as the Redirect URI in Google developer console)
I have looked at all the other questions that try to answer this issue, but none have been of help.
My Application class:
#SpringBootApplication
#EnableOAuth2Sso
#RestController
public class ControlApplication extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(ControlApplication.class, args);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated().and().formLogin().defaultSuccessUrl("/", false);
}
}
And my application.yml:
security:
oauth2:
client:
clientId: [MyClientID]
clientSecret: [MyClientSecret]
accessTokenUri: https://accounts.google.com/o/auth2/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/auth?hd=xyz.com
redirectUri: http://localhost:8080
clientAuthenticationScheme: form
tokenName: oauth_token
authenticationScheme: query
scope:
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true

Solved. (Finally!) The issue appears to be wrong config for acessTokenUri in calling the Google Auth API. Working config:
accessTokenUri: https://www.googleapis.com/oauth2/v4/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth?hd=xyz.com
clientAuthenticationScheme: form
scope:
- openid
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true

Related

Spring Boot with oauth2 using keycloak

I use spring boot as backend in addition to thymeleaf as the template engine. Atm, I'm trying to implement oauth2 (with keycloak) into my project.
I created a new realm, added a redirect-uri ("http://172.31.52.123:8000/*") + created users and put the id, secret, etc. in my application.properties file.
When I call http://172.31.52.123:8000/ I get the "hi" message from the ViewController below.
When I call http://172.31.52.123:8000/greeting, I will be redirected to http://172.31.52.123:8080/oauth2/authorization/appliance and then to the keycloak login. From there, I get these parameters:
response_type: code
client_id: myClientId
state: hpcfsknjW6QCfMSQWS-k...
redirect_uri: http://172.31.52.123:8080/*
and then these from keycloak again:
state: hpcfsknjW6QCfMSQWS-k...
session_state: f6ca95e5-a117-...
code: 298f32f-f283f ...
After the login, I end up with this:
172.31.52.123 hat Sie zu oft weitergeleitet. -> ERR_TOO_MANY_REDIRECTS
There is nothing in the console. What am I doing wrong? Originally, it should redirect to http://172.31.52.123:8000/*.
application.properties
appliance-base-url: https://authServerBlaBla/auth/realms/myRealmName
spring:
security:
oauth2:
client:
registration:
appliance:
authorizationGrantType: authorization_code
redirectUri: http://172.31.52.123:8080/*
clientId: myClientId
clientSecret: myClientSecret
provider:
appliance:
authorization-uri: ${appliance-base-url}/protocol/openid-connect/auth
token-uri: ${appliance-base-url}/protocol/openid-connect/token
user-info-uri: ${appliance-base-url}/protocol/openid-connect/userinfo
SecurityConfig
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.mvcMatchers("/").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
}
ViewController
#Controller
public class ViewController {
#GetMapping(value = {"/"})
#ResponseBody
public String index() {
return "hi";
}
#GetMapping("/greeting")
#ResponseBody
public String greet() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
return "Welcome, " + username;
}
}
You return #ResponseBody => your app should be configured as resource-server (not client).
If your app also serves UI elements (with Thymeleaf, JSF or whatever server-side rendering framework), you'll have to provide two different filter-chains, with securityMatcher patterns to specify where to apply resource-server security and where to apply client one.
I already detailed that in this answer: Use Keycloak Spring Adapter with Spring Boot 3
Details for configuring a Spring backend as both a resource-server (REST API served with #RestController) and a client (server-side rendered HTML with a regular WebMvc #Controller with Thymeleaf, JSF or whatever) in this one of my tutorials.
After having a look in the book:
Keycloak - Identity and Access Management for Modern Applications: Harness the Power of Keycloak, OpenID Connect, and OAuth 2.0 Protocols to Secure Applications
I figured that using Spring Boot + Thymeleaf requires keycloak to identify the application as a client. This might be the case in addition to the authorization code grant type.
All I had to do was the following:
change paths into relative paths (requests url)
remove/add proxies for the communication to work
add the following into your pom:
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>3.0.0</version>
</dependency>
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.0.0</version>
</dependency>
create an application.properties/yaml file with your configuration:
appliance-base-url: https://yourKeycloakInstance/auth/realms/p4udemo
spring:
security:
oauth2:
client:
registration:
democlient:
provider: keycloak
client-id: democlient
client-secret: 73... 324
authorization-grant-type: authorization_code
redirect-uri: "http://yourwebsite:port/login/oauth2/code/"
scope: openid
provider:
keycloak:
authorization-uri: ${appliance-base-url}/protocol/openid-connect/auth
token-uri: ${appliance-base-url}/protocol/openid-connect/token
jwk-set-uri: ${appliance-base-url}/protocol/openid-connect/certs
Add securityConfiguration.java (can be renamed differently):
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
}

OAuth2 with Google and Spring Boot - I can't log out

I've been trying to get a successful Oauth2 login with Google and Spring Boot for a while now. This only works partially. Why partly - because I can't manage the logout or when I pressed the logout button I see an empty, white browser page with my URL (http://localhost:8181/ben/"). After a refresh of the page I get error from google, but if I open a new tab, enter my url, I'm still logged in to google, because I can see my user, which I'm outputting to my react application.
#SpringBootApplication
#EnableOAuth2Sso
#RestController
#CrossOrigin
public class SocialApplication extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
#RequestMapping("/logout")
public String fetchSignoutSite(HttpServletRequest request, HttpServletResponse response) {
Cookie rememberMeCookie = new Cookie("JSESSIONID", "");
rememberMeCookie.setMaxAge(0);
response.addCookie(rememberMeCookie);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
auth.getPrincipal();
return "redirect:/ben/login";
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().antMatchers("/ben/*").permitAll().anyRequest().authenticated().and()
.logout().logoutSuccessUrl("http://localhost:8181/ben/login").invalidateHttpSession(true)
.clearAuthentication(true).deleteCookies("JSESSIONID");
}
My application.yml file looks like this:
# Spring Boot configuration
spring:
profiles:
active: google
# Spring Security configuration
security:
oauth2:
client:
clientId: 415772070383-3sapp4flauo6iqsq8eag7knpcii50v9k.apps.googleusercontent.com
clientSecret: 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
accessTokenUri: https://www.googleapis.com/oauth2/v4/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth
clientAuthenticationScheme: form
scope:
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true
# Server configuration
server:
port: 8181
servlet:
context-path: /ben
That fetchSignoutSite only emptying the JsessionId and logging out from Spring Security context. So you would still need to add part where you go to google and sign out from there which I have no experience on implementation.

How can I retrieve the azure AD JWT access token from Spring?

I'm trying to retrieve the azure JWT access token from my Spring Boot application from another application by querying a /token endpoint, but the token I receive is seemingly incorrect.
The project has a Spring Boot backend and an Eclipse rcp frontend. I'm attempting to retrieve the access token from the eclipse frontend. For this, I have the controller below:
#Autowired
private OAuth2AuthorizedClientService authorizedClientService;
#GetMapping("/token")
public String user(OAuth2AuthenticationToken authentication) {
OAuth2AuthorizedClient authorizedClient = this.authorizedClientService
.loadAuthorizedClient(authentication.getAuthorizedClientRegistrationId(), authentication.getName());
return authorizedClient.getAccessToken().getTokenValue();
}
Which returns a token with the following format:
PAQABAAAAAABeAFzDwllzTYGDLh_qYbH8hgtbYMB8x7YLamQyQPk_MEXyd9Ckc5epDFQMv3RxjmMie0JDr5uN82U4RFLgU3fnDBxGolo4XVwzLEsTZDmUK_r0YG6ZwLbbQI_ch_Xn8xCxhsFq-AoRbEESDqK3GmK4eXwCYoT0G8_XfZjHTvCNTOMqUb2Q-CD2EalIKf0zSZ5184qrvlXfdNeT_BJdH_tqaodn80Bp2UL2hdnOCDZuWRqKl_2fi4v-eOOKJCcjOqY6SreVEeoKkIvVdayGE8F6qCxFehmlA0sX9sVW34FIVYVo4lDRsTkm-WN2KJwxJmalNcxg0k2ObDnIeC1ulPPpiPq-O_LK9bVA4HEZ63cJi9ZwQHwLPUhOO6TquoCOroHSy5KPoFkX3N796hM1i0NpaaY4MeAx17CSYeZ9P06jvYD7UMTV3OwWt-OVrDm5z_AvbOvyHRf9wjh31H6oLoc-iu_NCspT6NzC2UZQSHBtKdydEcP6sNkRp073jrZEg8UtcVT6HzddIBk2P0tVeIiSyU3SfLETbzJE67xtJVip3ai9aLN28c0qt3rDBaVGDAXjXhqrh5D3NiXdQjS6YTAKy0bVmNk9Yr9o2CGBA2wFjE8OZ6_Hb3k8_13KMJHafx0gAA
Dependencies from pom.xml
Built using spring boot with the following relevant dependencies:
spring-boot-starter-web v2.2.4
azure-active-directory-spring-boot-starter v2.2.1
spring-security-oauth2-client v5.2.1
spring-security-oauth2-jose v5.2.1
spring-security-oauth2-resource-server v5.2.1
Config from application.yml
We support multiple authorization servers, here is the fully configured azure client:
spring:
security:
oauth2:
client:
azure:
client-id: XXX
client-secret: XXX
client-name: Microsoft
scope: openid, https://graph.microsoft.com/user.read, profile
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/azure
client-authentication-method: basic
authentication-method: post
provider:
authorization-uri: https://login.microsoftonline.com/XXX/oauth2/authorize
token-uri: https://login.microsoftonline.com/XXX/oauth2/token
user-info-uri: https://login.microsoftonline.com/XXX/openid/userinfo
jwt-set-uri: https://login.microsoftonline.com/dXXX/discovery/keys
azure:
activedirectory:
tenant-id: XXX
active-directory-groups: XXX
allow-telemetry: false
websecurityconfig.java
#Configuration
#EnableConfigurationProperties
#EnableWebSecurity
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
[...]
.anyRequest().authenticated();
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
http.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService)
.and()
.authorizationEndpoint();
}
[...]
}
This is how I ended up obtaining the open id token from Azure
#GetMapping("/token")
public String user(OAuth2AuthenticationToken authentication) {
DefaultOidcUser user = (DefaultOidcUser) authentication.getPrincipal();
return user.getIdToken().getTokenValue();
}

Spring boot + oauth2 + HttpClientErrorException 401 Unauthorized

I'm using the spring boot tutorial as a base (https://spring.io/guides/tutorials/spring-boot-oauth2/)
to test Oauth2.
However, my auth server isn't facebook, it's Netiq Access Manager (NAM).
I managed to be redirected to NAM login page, but after logging in, i get the following error:
The log shows:
o.s.b.a.s.o.r.UserInfoTokenServices : Could not fetch user details: class org.springframework.web.client.HttpClientErrorException, 401 Unauthorized
This is the project:
The app code:
package com.example.springoauthdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
#SpringBootApplication
#EnableOAuth2Sso
public class SocialApplication {
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
}
The application.yml
security:
oauth2:
client:
clientId: 55bb61f1-4384-4939-9cd0-fa7d76af9a0c
clientSecret: fdUjegFlCJjnD778RUSuS4SqMRey4IKVDOkadi4hjN6YbhC1xCInxoxobf-a-p-po8rt1wfZM2BPqJHpcZ-FGs
accessTokenUri: https://nam.example.com/nidp/oauth/nam/token
userAuthorizationUri: https://nam.example.com/nidp/oauth/nam/authz
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://localhost:8443/index.html
#userInfoUri: https://nam.example.com/nidp/oauth/nam/userinfo
server:
port: 8443
ssl:
enabled: true
key-alias: tomcat-localhost
key-password: changeit
key-store: classpath:keystore.jks
key-store-provider: SUN
key-store-type: JKS
key-store-password: changeit
As far i know, using this Oauth2 flow as example, step 1, 2 and 3 seems to be ok, so the problem is trying to get the access token?
Any ideas?
Thanks in advance!
When you are authenticated and you have a user, you can validate it against the userInfoUri, which returns a Principal object of the oauth.
You are setting this value against an html:
resource:
userInfoUri: https://localhost:8443/index.html
It should be something like:
resource:
userInfoUri: https://localhost:8443/userinfo
And that service response would have to return something like:
#RequestMapping("/userinfo")
Principal getUser(Principal user) {
return user;
}

Spring security oauth 2 client app can't check token validity

here is what I'm trying to achieve :
I have an enterprise oauth 2 provider, I want to use their login form and get code, access token and so on from this provider
here is my conf
security:
oauth2:
client:
clientId: MY_CLIENT_ID
clientSecret: asecret
accessTokenUri: https://blabla/oauth-server/oauth/token
userAuthorizationUri: https://blabla/oauth-server/oauth/authorize
tokenName: access_token
scope : read
userInfoUri: https://localhost/user
I can get my code which is changed for an access token, everything is fine, it's calling my local endpoint to get user information (roles for instance)
but when I debug the code I can't see any expires_in value anywhere and my token doesn't expire at all.
here's my resource server conf
#EnableResourceServer
#EnableOAuth2Sso
#RestController
public class SecurityController extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(true);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().anyRequest().and().authorizeRequests();
http.
anonymous().disable()
.requestMatchers().antMatchers("/**/*")
.and().authorizeRequests()
.antMatchers("/**/*").access("#oauth2.hasScope('read')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
I can't fidn out how to revoke the token
any idea is welcome, i've lost a bunch of hour reading tutorials ...
Add access-token-validity-seconds to your yaml
security:
oauth2:
client:
clientId: MY_CLIENT_ID
clientSecret: asecret
accessTokenUri: https://blabla/oauth-server/oauth/token
userAuthorizationUri: https://blabla/oauth-server/oauth/authorize
tokenName: access_token
scope : read
userInfoUri: https://localhost/user
access-token-validity-seconds: 30 //Adds 30 seconds of token validity

Resources