Sorry, I can't speak English.
But I had some problems
Currently logging in with Microsoft Azure AD
But encounter the following problem
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' in your configuration.
application.yml
server:
port: 8080
forward-headers-strategy: native
spring:
security:
oauth2:
client:
provider:
azure-ad:
authorization-uri: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
token-uri: https://login.microsoftonline.com/common/oauth2/v2.0/token
jwk-set-uri: https://login.microsoftonline.com/common/discovery/v2.0/keys
registration:
azure-client:
provider: azure-ad
client-id: 'xxxxxxxxxx'
client-secret: 'xxxxxxxxxxxxxxxx'
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/'
scope: openid,profile
SecurityConfig.java
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AzureADSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure( HttpSecurity http ) throws Exception {
http.authorizeRequests()
.antMatchers("/","/login").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login();
//.defaultSuccessUrl("/monitor");
}
}
Controller.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class AzureADController {
#GetMapping("/Admin")
public String Admin() {
System.out.println("================== Azure ad ==================");
return "Admin message";
}
}
Any one has any ideas, please? Thanks very much.
There seems to be an extra indent for client registration. Registration and provider should be on the same level under spring.security.oauth2.client
spring.security.oauth2.client.provider.azure-ad
spring.security.oauth2.client.registration.azure-client
See: https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/oauth2.html
Related
I want the root only to be publicly accessible, this is how I'm doing it, but it doesn't seem to work as every URL including the root requires me to pass by the login interface
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http)throws Exception{
http.authorizeHttpRequests()
.requestMatchers("/")
.permitAll()
.anyRequest().authenticated()
.and().httpBasic();
return http.build();
}
}
.requestMatchers("/**") should fix it.
More details:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class SecurityConfig {
Add the EnableMethodSecurity another to allow some PreAuths on API pathing via your controllers, I like to batch do this via Interfaces as Spring Boot now allows it.
#PostMapping("/URL")
#PreAuthorize("hasRole('ROLE')")
public ResponseEntity<T> method(
The PreAuthorize annotation is basically short way of doing hasRole in your security filter chain.
Spring Boot rest api in the back and angular in the front.
Hi all, I have a problem after successful oauth2 authentication with google.
In srping boot debug I can read the following:
o.s.web.cors.DefaultCorsProcessor : Skip: response already contains "Access-Control-Allow-Origin".
Then a 401 is sent to angular with full authentication required to access /api/user/ resource which is the root to access user details in the backend side.
WebConfig.java
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
#Configuration
public class WebConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("*")
.allowedMethods(
"HEAD",
"OPTIONS",
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
)
.maxAge(MAX_AGE_SECS);
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
final CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return cookieLocaleResolver;
}
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
SecurityConfig.java
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;
import com.springboot.dashboard.security.jwt.TokenAuthenticationFilter;
import com.springboot.dashboard.security.oauth2.DashBoardOAuth2UserService;
import com.springboot.dashboard.security.oauth2.DashBoardOidcUserService;
import com.springboot.dashboard.security.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
import com.springboot.dashboard.security.oauth2.OAuth2AccessTokenResponseConverterWithDefaults;
import com.springboot.dashboard.security.oauth2.OAuth2AuthenticationFailureHandler;
import com.springboot.dashboard.security.oauth2.OAuth2AuthenticationSuccessHandler;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private DashBoardOAuth2UserService dashBoardOAuth2UserService;
#Autowired
private DashBoardOidcUserService dashBoardOidcUserService;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#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()
.authorizeRequests()
.antMatchers("/", "/error", "/api/all", "/api/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.and()
.userInfoEndpoint()
.oidcUserService(dashBoardOidcUserService)
.userService(dashBoardOAuth2UserService)
.and()
.tokenEndpoint()
.accessTokenResponseClient(authorizationCodeTokenResponseClient())
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(
tokenAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
);
}
#Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
* By default, Spring OAuth2 uses
* HttpSessionOAuth2AuthorizationRequestRepository to save the authorization
* request. But, since our service is stateless, we can't save it in the
* session. We'll save the request in a Base64 encoded cookie instead.
*/
#Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
// This bean is load the user specific data when form login is used.
#Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(
new OAuth2AccessTokenResponseConverterWithDefaults()
);
RestTemplate restTemplate = new RestTemplate(
Arrays.asList(
new FormHttpMessageConverter(),
tokenResponseHttpMessageConverter
)
);
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}
}
Thanks in advance for your help.
oauth2 login successful cause user data is successfully to database, but can access to full authentication resource.
WebSecurityConfigurerAdapter is deprecated, don't use it (it is not even there any more in spring-boot 3). Expose a SecurityFilterChain bean instead:
#Bean
SecurityFilterChain filterChain(HttpSecurity http) {
// http configuration
return http.build();
}
Resource-server (REST API)
Instead of writing all of spring-boot-starter-oauth2-resource-server security configuration in java #Configuration (CSRF, CORS, JWT decoder or token introspector, authorities mapping, public routes), you can use one of the spring-boot starters here:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" if your app is reactive -->
<!-- replace "jwt" with "introspecting" to use token introspection instead of JWT decoding -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<!-- this version is to be used with spring-boot 3.0.0-RC2, use 5.x for spring-boot 2.6.x or before -->
<version>6.0.5</version>
</dependency>
#EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,ressource_access.client-id.roles
com.c4-soft.springaddons.security.cors[0].path=/**
Client (Angular app)
Use an OAuth2 client library. My favorite for Angular is angular-auth-oidc-client. It will save you tones of efforts to:
redirect users to authorisation-server for login
handle redirect back from authorization-server with authorization-code
exchange authorization-code for tokens (access-token of course, but also refresh and ID tokens if you requested offline_access and openid scopes)
auto-refresh access tokens before it expires (if you got a refresh-token)
automatically authorize request matching configured patterns (add Authorization Bearer header with access-token)
provide with Angular route guards
...
Authorization-server
Unless you deploy your resource-server to Google cloud, it is likely it can't use Google authorization-server directly. You might use an other authorization-server capable of identity federation in front of it. Keycloak does it pretty well:
run a Keycloak instance and configure it with Google as identity provider
configure your resource-server to use Keycloak as authorization-server (as done in code above)
configure Angular to use Keycloak as authorization-server too
I refer to Keycloak here, but most serious OIDC providers (either on premize or SaaS like Auth0, Okta, etc.) support "social" login and will allow Google users to login (as well as Facebook, Github, Tweeter, etc.)
I'm developing microservices some of them with spring boot web and other with spring boot data rest and I want secure them with keycloak server 18 through OpenId protocol. The microservices endpoint can be accessed from frontend adding in the "Authorization header" the bearer token obtained from post request to the url http://localhost:8280/auth/realms/--realm_name--/.well-known/openid-configuration inserting in the body request the key client_id, username, password, grant_type, client_secret.
I have create
1.a realm,
2.a client named "springboot-mc-dev" (with Access Type = confidential;,
"Root URL" and "Valid Redirect URIs" both setted to "http://localhost:8490",
"Standard Flow Enabled", "Direct Access Grants Enabled", "Service Accounts Enabled" and "Authorization Enabled" setted to "ON"),
3.before a role inside client ("named springboot-mc-dev-role" composite False) and after a role inside realm (named always "springboot-mc-dev-role" composite true that is associated Client Roles "springboot-mc-dev-role" of client springboot-mc-dev),
4.user mapped to role "springboot-mc-dev-role" in "realm roles"
After I have imported the following dependency in parent pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java-version>1.8</java-version>
<keycloak.version>18.0.2</keycloak.version>
</properties>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Here there is the code of SecurityConfig.java class
package it.organization.project.microservice.datamart.config.security;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
#KeycloakConfiguration
#Configuration
#EnableWebSecurity
#Import(KeycloakSpringBootConfigResolver.class)
#EnableGlobalMethodSecurity(jsr250Enabled = true)
#ConditionalOnProperty(name = "ms-security-enable", havingValue = "true")
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
//keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
/*protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}*/
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/**").hasRole("springboot-mc-dev-role")
//.anyRequest().hasRole("springboot-mc-dev-role")
//.anyRequest().//authenticated()
//permitAll()
;
http.csrf().disable();
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
//public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
this is the code of main class
package it.organization.project.microservice.datamart;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class MAINDataMartApplication {
public static void main(String[] args) {
Security.addProvider(new BouncyCastleProvider());
SpringApplication.run(MAINDataMartApplication.class, args);
}
}
and last there is the application.yml with keycloak settings
#KEYCLOAK CONFIGURATION
keycloak:
auth-server-url: http://localhost:8280/auth
#ssl-required: external
realm: realmname
bearer-only: true
#public-client: true
use-resource-role-mappings: true
resource: springboot-mc-dev
credentials:
secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
this is the result
What is wrong?
Keycloak spring adapter is deprecated. Don't use it.
Have look at those tutorials for alternatives.
We are missing important info to answer preecisely:
Spring versions?
reason for 401 (from Postman console, value of WWW-Authenticate response header)?
There can be few reasons for 401 Unauthorized:
missing authorization header
expired or not yet valid token (for instance because of timezone misconfiguration on either authorization or resource server)
different issuer in access-token claim and Spring config: host, port, etc must be exactly the same
Spring configured for a different type of authentication than what authorization server supports (i.e. opaque token with introspaction and one side and JWT on the other)
...
If you're using Keycloak 18 with Quarkus, it is likely that your conf should reference http://localhost:8280 as issuer (and not http://localhost:8280/auth).
So, look at Postman console, open your access-token with a tool like https://jwt.io and compare values you find there with what you have in spring conf and logs.
When I try the sample code from Microsoft Azure to use oAuth2 and Spring Boot, it uses a stateful session to check authentication/authorization. You can see this in that:
It never passes any headers/JWT on any calls
It has a cookie "JSESSIONID" that you can use in new postman session (after obtaining it in a different browser) and it will consider you logged in
This will not work as our micro-services will be multiple instances.
How could I convert this to use a JWT (Authorization: Bearer AQab...) for subsequent calls instead of the cookie?
Dependencies:
//All using Spring Boot 2.0.5.RELEASE
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.security:spring-security-oauth2-client')
compile('org.springframework.security:spring-security-oauth2-jose')
//Using 2.0.7
compile('com.microsoft.azure:azure-active-directory-spring-boot-starter')
Config:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
#EnableWebSecurity
#EnableGlobalMethodSecurity( prePostEnabled = true )
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> userService;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService( userService );
}
}
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class MainController
{
private OAuth2AuthorizedClientService clientService;
#Autowired
public MainController(OAuth2AuthorizedClientService clientService)
{
this.clientService = clientService;
}
#GetMapping( "checkrole" )
#ResponseBody
#PreAuthorize( "hasRole('ROLE__Test')" )
public String group1()
{
return "ok";
}
#GetMapping( "/" )
#ResponseBody
public String getUser(OAuth2AuthenticationToken userToken)
{
//Printing out the oAuth token just for testing
return clientService.loadAuthorizedClient(
userToken.getAuthorizedClientRegistrationId(),
userToken.getName()
).getAccessToken().getTokenValue();
}
}
application.yml:
spring:
security:
oauth2:
client:
registration:
azure:
client-id: ${YOUR_CLIENT_ID:}
client-secret: ${YOUR_CLIENT_SECRET:}
azure:
activedirectory:
tenant-id: ${YOUR_TENANT_OR_DIRECTORY_ID:}
active-directory-groups: Test
Complete Sample Code
https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-spring-boot-backend-sample
we are using AWS Cognito for Oauth2. Our UI is built on Angular. After my user logs in, I initiate a call to Cognito to get Authorization token. I am using Authorization Code Grant with PKCE for getting the token from Cognito. After getting the toke from Cognito, I call my Spring Boot REST service. When calling Spring Boot service from Angular, I send the token in the Authorization header as "Bearer" token.
Here is my ResourceServerConfiguration.java:
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource-server-rest-api";
private static final String SECURED_READ_SCOPE = "#oauth2.hasScope('openid')";
private static final String SECURED_WRITE_SCOPE = "#oauth2.hasScope('openid')";
private static final String SECURED_PATTERN = "/**";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers(SECURED_PATTERN).and().authorizeRequests()
.antMatchers(HttpMethod.POST, SECURED_PATTERN).access(SECURED_WRITE_SCOPE)
.anyRequest().access(SECURED_READ_SCOPE);
}
}
When calling the REST service my Angular UI gets HTTP Response 401 with the following error message:
DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - Authentication request failed: error="invalid_token", error_description="Invalid access token: eyJraWQiOiIy.......
Any idea why I am getting invalid_token?
Does spring makes a call to Cognito to verify the token?
I am NOT storing the token in my REST servic layer. Is this required?
I enabled DEBUG using logging.level.root=DEBUG. But I do not see descriptive message in the output. How do i trouble shoot this issue?
The below changes worked for me
Remove #EnableResourceServer
Add below to your spring security configuration
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS,"**").permitAll()
.anyRequest()
.authenticated()
.and().oauth2ResourceServer().jwt();
adding below 2 dependency to pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
finally add following properties to your application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://cognito-idp.us-east-1.amazonaws.com/{{userpoolid}}
com:
ixortalk:
security:
jwt:
aws:
userPoolId: {{userpoolid}}
region: "us-east-1"