Spring Working sessionRegistry not working with CAS auth - java

I'm using Spring Boot 2.1.6.RELEASE.
I need to expire the session of the users whos ROLES has changed.
I was trying to use sessionRegistry but it dosen't work, and I can't figure out why...
This is my SecurityConfigurer class:
/**
* Configuració de seguretat
*/
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
public static final String WEB = "\\/((?!api).*)";
public static final String API = "/api/**";
#Order(1)
#Configuration
public static class RestApiWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Resource(name = "restAuthenticationFilter")
private Filter restAuthenticationFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher(API)
.csrf().disable()
.antMatcher(API)
.addFilterBefore(restAuthenticationFilter, CsrfFilter.class)
.antMatcher(API)
.authorizeRequests()
.antMatchers(API).authenticated()
.and()
.antMatcher(API)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
}
}
#Order(2)
#Configuration
public static class ApplicacionWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private final CasProperties casProperties;
private final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> casUsuariDetailsService;
private final UserDetailsService usuariDetailsService;
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
#Autowired
public ApplicacionWebSecurityConfigurerAdapter(
CasProperties casProperties,
AuthenticationUserDetailsService<CasAssertionAuthenticationToken> casUsuariDetailsService,
#Qualifier("casUserDetailsService")
UserDetailsService usuariDetailsService) {
this.casProperties = casProperties;
this.casUsuariDetailsService = casUsuariDetailsService;
this.usuariDetailsService = usuariDetailsService;
}
#Bean
public ServiceProperties serviceProperties() throws UnknownHostException {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(getUrlWithHostName(casProperties.getValidateUrl()));
serviceProperties.setSendRenew(false);
return serviceProperties;
}
#Bean
public CasAuthenticationProvider casAuthenticationProvider() throws UnknownHostException {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider
.setAuthenticationUserDetailsService(casUsuariDetailsService);
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider
.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only");
return casAuthenticationProvider;
}
#Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(casProperties.getReturnUrl());
}
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter
.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setFilterProcessesUrl("/cas/login");
casAuthenticationFilter.setSessionAuthenticationStrategy(sessionControlAuthenticationStrategy());
return casAuthenticationFilter;
}
#Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() throws UnknownHostException {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(casProperties.getLoginUrl());
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
#Bean
public ConcurrentSessionControlAuthenticationStrategy sessionControlAuthenticationStrategy() {
ConcurrentSessionControlAuthenticationStrategy csas = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
csas.setMaximumSessions(1);
csas.setExceptionIfMaximumExceeded(true);
return csas;
}
/*
* User impersonation
*/
#Bean
public SwitchUserFilter switchUserFilter() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setUserDetailsService(usuariDetailsService);
filter.setSwitchUserUrl("/login/impersonate");
filter.setExitUserUrl("/logout/impersonate");
filter.setTargetUrl("/");
filter.setUsernameParameter("username");
filter.setSwitchFailureUrl("/");
return filter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
}
#Bean
public Filter ajaxTimeOutRedirectFilter() {
AjaxTimeoutRedirectFilter f = new AjaxTimeoutRedirectFilter();
f.setCustomSessionExpiredErrorCode(HttpStatus.UNAUTHORIZED.value());
return f;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// Afegim el filtre del cas a les peticions
http.addFilter(casAuthenticationFilter());
http
.regexMatcher(WEB)
.exceptionHandling().authenticationEntryPoint(
casAuthenticationEntryPoint())
.and()
.addFilterAfter(ajaxTimeOutRedirectFilter(), ExceptionTranslationFilter.class)
;
http
.regexMatcher(WEB)
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.regexMatcher(WEB)
.authorizeRequests()
.antMatchers("/images/**", "/css/**", "/fonts/**", "/js/**", "/bower_components/**").permitAll()
.antMatchers("/logout").permitAll()
.antMatchers("/cas/login").permitAll()
.antMatchers("/login/impersonate").hasRole("ADMIN")
// Actuator
.antMatchers("/health").permitAll()
.antMatchers("/info").permitAll()
.antMatchers("/actuator").permitAll()
// Swagger
.antMatchers("/swagger-resources/**",
"/v2/api-docs", "/webjars/**", "/swagger-ui.html").permitAll()
// Resta
.regexMatchers(WEB).authenticated()
;
http
.regexMatcher(WEB)
.logout().logoutUrl("/logout").deleteCookies("JSESSIONID")
.logoutSuccessUrl(getUrlWithHostName(casProperties.getLogoutUrl()))
.invalidateHttpSession(true);
http
.regexMatcher(WEB)
.headers().frameOptions().sameOrigin();
}
private String getUrlWithHostName(String urlToFormat) throws UnknownHostException {
return MessageFormat.format(urlToFormat, InetAddress
.getLocalHost().getCanonicalHostName());
}
}
}
I have tried a lot but it seems that the registry always is empty, and principals and sessions always are returning an empty map...
Any suggestion?
Thks

It seems that the ConcurrentSessionAuthenticationStrategy also needs a RegisterSessionAuthenticationStrategy, as described in Cyril Gabis' answer here: SAML authenticated users don't appear in Spring Security's SessionRegistry. The following worked for me:
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
List<SessionAuthenticationStrategy> strategies = new ArrayList<>();
ConcurrentSessionControlAuthenticationStrategy concurrentStrategy =
new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
concurrentStrategy.setMaximumSessions(1);
concurrentStrategy.setExceptionIfMaximumExceeded(true);
RegisterSessionAuthenticationStrategy registerStrategy =
new RegisterSessionAuthenticationStrategy(sessionRegistry());
strategies.add(concurrentStrategy);
strategies.add(registerStrategy);
return new CompositeSessionAuthenticationStrategy(strategies);
}
In summary:
Create the SessionRegistry bean
Create the SessionAuthenticationStrategy bean
Register the SessionAuthenticationStrategy bean with CasAuthenticationFilter (or when using SAML, SAMLProcessingFilter).
SessionRegistry bean:
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
Registering it with CasAuthenticationFilter:
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
return casAuthenticationFilter;
}

Related

Cycle between AuthenticationConfiguration and Filters

I have a problem with cycle injection and don't know how to fix it :(
For my SecurityConfig I need JWTAuthenticationFilter.
For my JWTAuthenticationFilter I need AuthenticationManager.
For my AuthenticationManager I need SecurityConfig.
Can you help me with resolving this problem ? Thanks !
#Configuration
#RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
#Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth ) throws Exception {
return auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder()).and().build();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig {
private final JWTVerifierFilter jwtVerifierFilter;
private final AuthenticationProvider authenticationProvider;
private final JWTAuthenticationFilter jwtAuthenticationFilter;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers("/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerifierFilter, JWTAuthenticationFilter.class);
return http.build();
}
}
#RequiredArgsConstructor
#Component
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final TokenRepository tokenRepository;
private final JwtService jwtService;
private final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
JwtAuthenticationRequest authModel = OBJECT_MAPPER.readValue(request.getInputStream(), JwtAuthenticationRequest.class);
Authentication authentication = new UsernamePasswordAuthenticationToken(authModel.getUsername(), authModel.getPassword());
return authenticationManager.authenticate(authentication);
} catch (IOException e) {
throw new AssertionError(e.getMessage(), e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
String token = jwtService.generateToken((UserEntity) authResult.getPrincipal());
TokenEntity tokenEntity = TokenEntity.builder()
.id(UUID.randomUUID().toString())
.authenticationToken(token)
.username(authResult.getName())
.build();
tokenEntity = tokenRepository.save(tokenEntity);
response.addHeader(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", tokenEntity.getId()));
ConnValidationResponse respModel = ConnValidationResponse.builder().isAuthenticated(true).build();
response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
response.getOutputStream().write(OBJECT_MAPPER.writeValueAsBytes(respModel));
}
}
I tried to replace
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
by
#Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}
but for HttpSecurity I need SecurityConfig too.
I also tried add #Lazy for AuthenticationConfiguration config but it doesn't work

Getting Unauthorised Full authentication is required to access this resource in Oauth2 Spring Boot

I am using Oauth2 in Spring Boot and I am using JDBC token store to store the JWT tokens. This is my AuthorizationServerConfig
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfig.class);
static final String MERCHANT_ID = "merchant-id";
static final String MERCHANT_SECRET = "merchant-secret-bcrypted-value";
static final String CUSTOMER_ID = "customer-id";
static final String CUSTOMER_SECRET = "customer-secret-bcrypted-value";
static final String GRANT_TYPE_PASSWORD = "password";
static final String AUTHORIZATION_CODE = "authorization_code";
static final String REFRESH_TOKEN = "refresh_token";
static final String IMPLICIT = "implicit";
static final String SCOPE_READ = "read";
static final String SCOPE_WRITE = "write";
static final String TRUST = "trust";
static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1 * 60 ;
static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 5 * 60 ;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Resource(name = "UserService")
private UserDetailsService userDetailsService;
#Bean
public JwtAccessTokenConverter accessTokenConverter() throws Exception {
logger.debug("accessTokenConverter");
System.out.println("accessTokenConverter");
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("asagasdg");
return converter;
}
#Bean
public TokenStore tokenStore() throws Exception {
logger.debug("tokenStore");
return new JdbcTokenStore(dataSource);
}
#Bean
public ApprovalStore approvalStore() throws Exception {
TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
tokenApprovalStore.setTokenStore(tokenStore());
return tokenApprovalStore;
}
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
System.out.println("configure");
configurer
.jdbc(dataSource)
// .inMemory()
.withClient(MERCHANT_ID)
.secret(MERCHANT_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT)
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
.and()
.withClient(CUSTOMER_ID)
.secret(CUSTOMER_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT)
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS).and()
.build()
;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
System.out.println("configure below");
endpoints
.pathMapping("/oauth/token","/api/v1/oauth/token")
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() throws Exception {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
So whenever I try to hit this url BASE_URL/api/v1/oauth/token with the userid and secret as Basic-Auth in Postman along with another username, password and grant_type=password I get this error
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
The in-memory was authentication was working fine but when I created the databases oauth_access_token, oauth_refresh_token and oauth_client_details to save and retrieve the JWTs from database, I am getting that error.
This is my ResourceServerConfig
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource_id";
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception{
System.out.println("resource server configurer "+resources);
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
System.out.println("resource server config");
http
.authorizeRequests()
.antMatchers("api/v1/oauth/token").permitAll()
.antMatchers("/","/css/**","/js/**","/lib/**","/img/**","/scss/**","/templates/**","/device-mockups/**","/vendor/**").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
And this is my WebSecurityConfigurerAdapter
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "UserService")
private UserDetailsService userDetailsService;
#Autowired
DataSource dataSource;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
System.out.println("authenticationManagerBean");
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("globalUserDetails");
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() throws Exception {
System.out.println("bcryptEncoder");
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("configure ");
http.cors().and()
.authorizeRequests()
.antMatchers("/","/api/v1/oauth/token","/**").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
;
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
I don't know what I am missing. Any help would be highly appreciated. Thanks
Check if you have Headers set in Postman.
Key: Content-Type Value: application/x-www-form-urlencoded
You might have, but you didn't mention. Maybe it helps.
Also, I notice you didn't give permission for all to get a token. Try this in your AuthorizationServerConfigurerAdapter:
#Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
´´´

How get a new token for every request in spring-boot oauth2 tokenstore

I am using oauth2 security in spring boot with 2.1.5 version. When I send a request to issue token, I am receiving an only the same token that got before. I cannot get a token until the token is expired. After that, I can get a new token, but again the same situation.
I tried to change the token store from JdbcTokenStore to InMemoryTOkenStore. Besides that, I changed the authentication mechanism AuthenticationProvider to UserDetailsService.But nothing happened.
#Configuration
public class OAuth2SecurityConfiguration {
#Configuration
#EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private final TokenStore tokenStore;
public ResourceServerConfiguration(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.tokenStore(tokenStore).resourceId(Constants.SERVER_RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(Constants.API_BASE_PATH + "/**").authenticated()
.and().csrf().disable();
}
}
#Configuration
#EnableAuthorizationServer
public static class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final DataSource dataSource;
private final PasswordEncoder passwordEncoder;
#Value("${jwt.key.password:my_pay_jwt_password}")
private String keyStorePassword;
public OAuth2AuthorizationServerConfiguration(AuthenticationManager authenticationManager, DataSource dataSource, PasswordEncoder passwordEncoder) {
this.authenticationManager = authenticationManager;
this.dataSource = dataSource;
this.passwordEncoder = passwordEncoder;
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
public JwtAccessTokenConverter tokenEnhancer() {
// For getting user information in getPrincipal()
DefaultUserAuthenticationConverter duac = new DefaultUserAuthenticationConverter();
DefaultAccessTokenConverter datc = new DefaultAccessTokenConverter();
datc.setUserTokenConverter(duac);
JwtAccessTokenConverter converter = new CustomAccessTokenConverter();
converter.setAccessTokenConverter(datc); // IMPORTANT
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("static/jwt.jks"), keyStorePassword.toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
PublicKey publicKey = keyStoreKeyFactory.getKeyPair("jwt").getPublic();
String publicKeyString = Base64.encodeBase64String(publicKey
.getEncoded());
converter.setVerifierKey(publicKeyString);
return converter;
}
#Bean
#Primary
public AuthorizationServerTokenServices defaultAuthorizationServerTokenServices() {
DefaultTokenServices tokenServices = new LockingTokenServices();
tokenServices.setAuthenticationManager(this.authenticationManager);
tokenServices.setTokenStore(tokenStore());
tokenServices.setTokenEnhancer(tokenEnhancer());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
return tokenServices;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
.checkTokenAccess("permitAll()")
.tokenKeyAccess("isAuthenticated()")
.allowFormAuthenticationForClients()
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.reuseRefreshTokens(false)
.accessTokenConverter(tokenEnhancer())
.authenticationManager(authenticationManager)
.tokenStore(tokenStore()).approvalStoreDisabled();
endpoints.exceptionTranslator(exception -> {
if (exception instanceof OAuth2Exception) {
OAuth2Exception oAuth2Exception = (OAuth2Exception) exception;
if (OAuth2Exception.INVALID_GRANT.equals(oAuth2Exception.getOAuth2ErrorCode())) {
oAuth2Exception.addAdditionalInformation("error_description", "Invalid username or password");
}
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(oAuth2Exception);
} else {
throw exception;
}
});
}
}
}

OAuth2 is not accessible from swagger

I am getting 401 when I try to access OAuth2 from swagger. It is working fine if Swagger is configured in same project and running on same port. But when I configure swagger in another project with different port then it gives 401.
OAuth2 is accessible and working fine with Postman. I am not able to find why it is giving 401 from different port. I have checked inbound/outbound rules of the running port. Is there any other configuration required for accessing OAuth from different server or port?
OAuth2 project is configured on http://localhost:8090/
SpringBoot project is configured on http://localhost:8888/ from where OAuth2 is giving 401.
WebSecurityConfiguration
#Configuration
#EnableWebSecurity
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Lazy
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/oauth/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
/*
* https://github.com/spring-projects/spring-boot/issues/11136
* Expose it manually (there is bug)
*
* */
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthorizationServerConfig :
#Configuration
#EnableAuthorizationServer
public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String CLIENT_ID = "client";
private static final String CLIENT_SECRET = "secret";
private static final String GRANT_TYPE_PASSWORD = "password";
private static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";
private static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
private static final String GRANT_TYPE_AUTH_CODE = "authorization_code";
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String SCOPE_TRUST = "trust";
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Value("${config.oauth2.tokenTimeout}")
private int ACCESS_TOKEN_VALIDITY_SECONDS;
#Value("${config.oauth2.tokenTimeout}")
private int REFRESH_TOKEN_VALIDITY_SECONDS;
#Value("${config.oauth2.privateKey}")
private String privateKey;
#Value("${config.oauth2.publicKey}")
private String publicKey;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(CLIENT_ID)
.authorizedGrantTypes(GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_PASSWORD, GRANT_TYPE_REFRESH_TOKEN, GRANT_TYPE_AUTH_CODE)
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes(SCOPE_READ, SCOPE_WRITE, SCOPE_TRUST)
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS)
.secret(passwordEncoder.encode(CLIENT_SECRET));
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenStore(tokenStore())
.userDetailsService(userDetailsService)
.tokenServices(tokenServices())
.accessTokenConverter(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()")
.tokenKeyAccess("permitAll()");
}
}
WebSecureConfigurerAdapter:
#Configuration
#EnableResourceServer
public class CustomResourceConfig extends ResourceServerConfigurerAdapter {
#Value("${config.oauth2.publicKey}")
private String publicKey;
#Value("${config.oauth2.privateKey}")
private String privateKey;
#Value("${config.oauth2.resource.id}")
private String resourceId;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).authenticated()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/", "/home", "/register", "/login").permitAll()
.antMatchers("/oauth/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(resourceId)
.tokenServices(tokenServices())
.tokenStore(tokenStore());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
In Swagger configuration , OAuth security schema should be properly initialized while creating Docket instance. Here Access Token URI is something like : http://localhost:8080/api/oauth/token
#Value("${config.oauth2.accessTokenUri}")
private String accessTokenUri;
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select().apis(RequestHandlerSelectors.basePackage("com.authentication")).paths(regex("/.*"))
.paths(PathSelectors.any())
.build()
.securityContexts(Collections.singletonList(securityContext()))
.securitySchemes(Arrays.asList(securitySchema()))
.apiInfo(apiInfo());
}
private OAuth securitySchema() {
List<AuthorizationScope> authorizationScopeList = newArrayList();
authorizationScopeList.add(new AuthorizationScope("read", "read all"));
authorizationScopeList.add(new AuthorizationScope("write", "access all"));
List<GrantType> grantTypes = newArrayList();
GrantType passwordCredentialsGrant = new ResourceOwnerPasswordCredentialsGrant(accessTokenUri);
grantTypes.add(passwordCredentialsGrant);
return new OAuth("oauth2", authorizationScopeList, grantTypes);
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth())
.build();
}

Spring social ProviderSignInController creates a session whereas we are using oauth2

We are using a JWT token as authentication mechanism. When we sign in using Google and Spring social, our /signin endpoint added by Spring social put a JSESSIONID as a cookie. I don't know how to disabled this behavior (that's the question).
As we are using an HTTP header as auth payload we don't need a CSRF protection. We don't want a cookie with a JSESSIONID payload, especially as we don't know the scope and rights of this cookie.
Here is social configuration file:
#Configuration
#EnableSocial
public class SocialConfiguration implements SocialConfigurer {
private final Logger log = LoggerFactory.getLogger(SocialConfiguration.class);
#Inject
private SocialUserConnectionRepository socialUserConnectionRepository;
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
// Google configuration
String googleClientId = environment.getProperty("spring.social.google.clientId");
String googleClientSecret = environment.getProperty("spring.social.google.clientSecret");
if (googleClientId != null && googleClientSecret != null) {
log.debug("Configuring GoogleConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new GoogleConnectionFactory(
googleClientId,
googleClientSecret
)
);
} else {
log.error("Cannot configure GoogleConnectionFactory id or secret null");
}
// Facebook configuration
String facebookClientId = environment.getProperty("spring.social.facebook.clientId");
String facebookClientSecret = environment.getProperty("spring.social.facebook.clientSecret");
if (facebookClientId != null && facebookClientSecret != null) {
log.debug("Configuring FacebookConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new FacebookConnectionFactory(
facebookClientId,
facebookClientSecret
)
);
} else {
log.error("Cannot configure FacebookConnectionFactory id or secret null");
}
// Twitter configuration
String twitterClientId = environment.getProperty("spring.social.twitter.clientId");
String twitterClientSecret = environment.getProperty("spring.social.twitter.clientSecret");
if (twitterClientId != null && twitterClientSecret != null) {
log.debug("Configuring TwitterConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new TwitterConnectionFactory(
twitterClientId,
twitterClientSecret
)
);
} else {
log.error("Cannot configure TwitterConnectionFactory id or secret null");
}
// jhipster-needle-add-social-connection-factory
}
#Override
public UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource();
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return new CustomSocialUsersConnectionRepository(socialUserConnectionRepository, connectionFactoryLocator);
}
#Bean
public SignInAdapter signInAdapter() {
return new CustomSignInAdapter();
}
#Bean
public ProviderSignInController providerSignInController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository, SignInAdapter signInAdapter) throws Exception {
ProviderSignInController providerSignInController = new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, signInAdapter);
providerSignInController.setSignUpUrl("/social/signup");
return providerSignInController;
}
#Bean
public ProviderSignInUtils getProviderSignInUtils(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository) {
return new ProviderSignInUtils(connectionFactoryLocator, usersConnectionRepository);
}
}
And my spring security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private UserDetailsService userDetailsService;
#Inject
private TokenProvider tokenProvider;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
// TODO unit test the security
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// API part start
... antMatchers here
.and()
.apply(securityConfigurerAdapter());
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}

Categories