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();
}
Related
I`m developing a microservice structure using spring-boot, this structure has an external oauth2 Authorization Server and multiples Resource Servers.
My problem is, each http request to my resources, calls a url to my Authorization Server, to validate the token ( .../oauth/check_token/). This way there is a lot of requests. There is a way to validate/check this token only when it is expired?
My Resource servers:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Value("${security.oauth2.resource.token-info-uri}")
private String tokenInfoUri;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(ADMIN_ANT_MATCHER).hasRole("ADMIN")
.antMatchers(PROTECTED_ANT_MATCHER).hasRole("USER")
.and()
.csrf().disable();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenService()).resourceId(resourceId).stateless(true);
}
#Bean
#Primary
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl(tokenInfoUri);
tokenService.setClientId(clientId);
tokenService.setClientSecret(clientSecret);
return tokenService;
}
}
Authorization Server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Value("${security.oauth2.client.access-token-validity-seconds}")
private Integer tokenValidateSeconds;
#Value("${security.oauth2.client.token-secret}")
private String tokenSecret;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private OauthAccessTokenRepository oauthAccessTokenRepository;
#Autowired
private OauthRefreshTokenRepository oauthRefreshTokenRepository;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(this.tokenStore)
.tokenEnhancer(tokenEnhancer())
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(clientId)
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.authorities("ROLE_USER","ROLE_ADMIN")
.scopes("read","write","trust")
.resourceIds(resourceId)
.accessTokenValiditySeconds(tokenValidateSeconds)
.secret(bCryptPasswordEncoder().encode(clientSecret));
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new TokenEnhancer() {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
CustomUser user = (CustomUser) authentication.getPrincipal();
String token = JwtTokenHelper.generateToken(
user.getIdUser(),
user.getTenantID(),
user.getIdEntity(),
user.getIdBusinessUnit(),
user.getProfile(),
tokenValidateSeconds,
tokenSecret
);
((DefaultOAuth2AccessToken) accessToken).setValue(token);
return accessToken;
}
};
}
}
You can remove the need to use the check_token endpoint, by using signed JWT tokens.
When the resource server receive a JWT token, it verify it's signature by using a public key, and the expiration date by checking the corresponding field in the JSON object.
For this you can use the JwtAccessTokenConverter, JwtTokenStore and the nimbus-jose-jwt library.
The downside of this approach is that you cannot revoke a token. It's then preferable to have short lived tokens.
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;
}
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()");
}
´´´
I am trying to use AuthorizationServer and ResourceServer in Spring Boot.
Code for AuthorizationServer is like this
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
static final String CLIEN_ID = "client";
static final String CLIENT_SECRET = "$2a$04$e/c1/RfsWuThaWFCrcCuJeoyvwCV0URN/6Pn9ZFlrtIWaU/vj/BfG";
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*60;
static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60;
#Autowired
private AuthenticationManager authenticationManager;
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("as466gf");
return converter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIEN_ID)
.secret(CLIENT_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);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter());
}
}```
Code for ResourceServer is like this
```#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource_id";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.authorizeRequests()
.antMatchers("/users/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}```
Then the code for WebSecurityConfigurerAdapter is like this
```#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api-docs/**").permitAll();
}
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
So whenever I try to hit this url {{BASE_URL}}/oauth/token I am getting this error
"error": "invalid_grant",
"error_description": "Bad credentials"
}```
I am using PostMan to use the api. I have provided a username and a password as the Basic Auth. I don't know what is missing. Any help would be greatly appreciated. Thanks.
Your api call should be like this:
Don't forget to click on PREVIEW REQUEST in Authorization tab
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;
}
});
}
}
}