spring custom mongo TokenStore not refresh new access_token automatically when old expired? - spring

I use mongo custom tokenStore and codeService:
this is my custom mongoTokenStore:
public class MongoTokenStore implements TokenStore {
private final MongoAccessTokenRepository mongoAccessTokenRepository;
private final MongoRefreshTokenRepository mongoRefreshTokenRepository;
private AuthenticationKeyGenerator authenticationKeyGenerator =
new DefaultAuthenticationKeyGenerator();
public MongoTokenStore(MongoAccessTokenRepository mongoAccessTokenRepository, MongoRefreshTokenRepository mongoRefreshTokenRepository) {
this.mongoAccessTokenRepository = mongoAccessTokenRepository;
this.mongoRefreshTokenRepository = mongoRefreshTokenRepository;
}
#Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken oAuth2AccessToken) {
return readAuthentication(oAuth2AccessToken.getValue());
}
#Override
public OAuth2Authentication readAuthentication(String tokenId) {
return mongoAccessTokenRepository.findByTokenId(tokenId)
.map(MongoAccessToken::getAuthentication)
.orElse(null);
}
#Override
public void storeAccessToken(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
MongoAccessToken mongoAccessToken = new MongoAccessToken(oAuth2AccessToken, oAuth2Authentication,
authenticationKeyGenerator.extractKey(oAuth2Authentication));
mongoAccessTokenRepository.save(mongoAccessToken);
}
#Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
return mongoAccessTokenRepository.findByTokenId(tokenValue)
.map(MongoAccessToken::getOAuth2AccessToken)
.orElse(null);
}
#Override
public void removeAccessToken(OAuth2AccessToken oAuth2AccessToken) {
mongoAccessTokenRepository.findByTokenId(oAuth2AccessToken.getValue())
.ifPresent(mongoAccessTokenRepository::delete);
}
#Override
public void storeRefreshToken(OAuth2RefreshToken oAuth2RefreshToken, OAuth2Authentication oAuth2Authentication) {
MongoRefreshToken token=new MongoRefreshToken(oAuth2RefreshToken,oAuth2Authentication);
mongoRefreshTokenRepository.save(token);
}
#Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return mongoRefreshTokenRepository.findByTokenId(tokenValue)
.map(MongoRefreshToken::getOAuth2RefreshToken)
.orElse(null);
}
#Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
return mongoRefreshTokenRepository.findByTokenId(oAuth2RefreshToken.getValue())
.map(MongoRefreshToken::getAuthentication)
.orElse(null);
}
#Override
public void removeRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
mongoRefreshTokenRepository.findByTokenId(oAuth2RefreshToken.getValue())
.ifPresent(mongoRefreshTokenRepository::delete);
}
#Override
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
mongoAccessTokenRepository.findByRefreshToken(oAuth2RefreshToken.getValue())
.ifPresent(mongoAccessTokenRepository::delete);
}
#Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication oAuth2Authentication) {
return mongoAccessTokenRepository.findByAuthenticationId(authenticationKeyGenerator
.extractKey(oAuth2Authentication))
.map(MongoAccessToken::getOAuth2AccessToken)
.orElse(null);
}
#Override
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String s, String s1) {
return mongoAccessTokenRepository.findByClientIdAndUserName(s,s1)
.stream()
.map(MongoAccessToken::getOAuth2AccessToken)
.collect(Collectors.toList());
}
#Override
public Collection<OAuth2AccessToken> findTokensByClientId(String s) {
return mongoAccessTokenRepository.findByClientId(s)
.stream()
.map(MongoAccessToken::getOAuth2AccessToken)
.collect(Collectors.toList());
}
}
and this is my custom mongoCodeService:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MongoAuthenticationCodeService extends RandomValueAuthorizationCodeServices{
private final MongoAuthenticationCodeRepository repository;
#Override
protected void store(String code, OAuth2Authentication oAuth2Authentication) {
repository.save(new MongoAuthenticationCode(code,oAuth2Authentication));
}
#Override
protected OAuth2Authentication remove(String code) {
return repository.findOneByCode(code)
.map(MongoAuthenticationCode::getAuthentication)
.orElse(null);
}
}
and my OAuth2Config:
#Configuration
public class OAuth2ServerConfiguration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(READ_AND_WRITE_RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/webjars/**", "/oauth/authorize/**", "/", "/customLogout",
"/oauth/check_token/**", "/login").permitAll()
.mvcMatchers(HttpMethod.GET, "/loginAttemptUsers").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailService customUserDetailService;
#Autowired
private MongoAuthenticationCodeService mongoAuthenticationCodeService;
#Autowired
private MongoTokenStore mongoTokenStore;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.authorizationCodeServices(mongoAuthenticationCodeService)
.tokenStore(mongoTokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(customUserDetailService)
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("kksdi2388wmkwe")
.authorizedGrantTypes("authorization_code", "password", "refresh_token")
.scopes("read", "write")
.resourceIds("ReadAndWriteResource")
.secret("kksd23isdmsisdi2")
.autoApprove(true)
.accessTokenValiditySeconds(120)
.refreshTokenValiditySeconds(1200);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
return tokenServices;
}
}
}
the problem is:
I can login from spring zuul ui-server by "authorization_code",and can
access ResourceServer data .
But when the access_token expired(120s), while re-getting ResourceServer data, I see mongoTokenStore removed the existed access_token , but why not refresh new access_token automatically?
How to resolve it?

The whole point of refresh token is to add one more security dimension to OAuth2.
When the end user has acquired access token, he now can access any resource within the authorization rules that the token awards him.
If his access token is stolen somehow, the attacker can now access to any resource that the access token authorizes him, but if we set an expiry time for the access token the attacker will have limited access time.
With that being said, if spring oauth2 would have refresh that token automatically that one more security aspect would not be applied, and the whole idea of refreshing the token would be wasted.
So, to conclude it's your responsibility to make sure that your end user is re-authorize via OAuth2 again.
You can also read this:
Why Does OAuth v2 Have Both Access and Refresh Tokens?

Related

Spring Boot security sign-in 403 Forbidden error question

I am having some issue on my Spring security sign-in. Signup works fine with no error but only sign-in returns 403 forbidden error.
I tried add http.httpBasic() and it returns 401 error.
I have http.csrf().disable() in the SecurityConfig.java but it still doesn't work even it's permitAll() condition. I am stuck in this problem for days :/ I tried every single solution that I googled but nothing worked.
Here is SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();//cross-origin-resource-sharing
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/api/authentication/**").permitAll()//login and register pre-path
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public JwtAuthorizationFilter jwtAuthorizationFilter()
{
return new JwtAuthorizationFilter();
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer()
{
#Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
};
}
I think from this jwtAutheorizationFiler.java cause the issue if the Security config is fine:
public class JwtAuthorizationFilter extends OncePerRequestFilter
{
#Autowired
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
Authentication authentication = jwtProvider.getAuthentication(request);
if (authentication != null && jwtProvider.isTokenValid(request))
{
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
SecurityUtils.java
public class SecurityUtils
{
public static final String ROLE_PREFIX = "ROLE_";
public static final String AUTH_HEADER = "authorization";
public static final String AUTH_TOKEN_HEADER = "Bearer";
public static final String AUTH_TOKEN_PREFIX = AUTH_TOKEN_HEADER + " ";
public static SimpleGrantedAuthority convertToAuthority(String role)
{
String formattedRole = role.startsWith(ROLE_PREFIX) ? role : ROLE_PREFIX + role;
return new SimpleGrantedAuthority(formattedRole);
}
public static String extractAuthTokenFromRequest(HttpServletRequest request)
{
String bearerToken = request.getHeader(AUTH_HEADER);
if(StringUtils.hasLength(bearerToken) && bearerToken.startsWith(AUTH_TOKEN_PREFIX))
{
return bearerToken.substring(7);
}
return null;
}
}
CustomUserDetailService.java :
#Service
public class CustomUserDetailsService implements UserDetailsService
{
private LoginService loginService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Login login = loginService.findByUsername(username)
.orElseThrow(()-> new UsernameNotFoundException("User not found with username: "+ username));
Set<GrantedAuthority> authorities = Set.of(SecurityUtils.convertToAuthority(login.getRole().name()));
return UserPrincipal.builder()
.login(login)
.id(login.getId())
.username(login.getUsername())
.password(login.getPassword())
.authorities(authorities)
.build();
}
}
AuthenticationController.java
#Autowired
private AuthenticationService authenticationService;
#Autowired
private LoginService loginService;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#PostMapping("sign-up")//api/authentication/sign-up
public ResponseEntity<?> signUp(#RequestBody Login login)
{
if(loginService.findByUsername(login.getUsername()).isPresent())
{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
return new ResponseEntity<>(loginService.saveLogin(login), HttpStatus.CREATED);
}
#PostMapping("sign-in")//api/authentication/sign-in
public ResponseEntity<?> signIn(#RequestBody Login login)
{
return new ResponseEntity<>(authenticationService.signInAndReturnJWT(login), HttpStatus.OK);
}
AuthenticationServiceImple.java
#Service
public class AuthenticationServiceImpl implements AuthenticationService
{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtProvider jwtProvider;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#Override
public Login signInAndReturnJWT(Login signInRequest)
{
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword())
);
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
String jwt = jwtProvider.generateToken(userPrincipal);
Login signInUser = userPrincipal.getLogin();
signInUser.setAccessToken(jwt);
signInUser.setRefreshToken(jwtRefreshTokenService.createRefreshToken(signInUser.getId()).getTokenId());
return signInUser;
}
}

Oauth2 - java.lang.IllegalStateException: UserDetailsService is required. with refresh token

I am trying to implement oauth2 with a jwt in spring boot and the autentication works but when I want to get the refresh_token an error occurs that indicates the following ...
java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:464)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)
What am I doing wrong?
These are my files
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Value("${token.secret}")
private String secret;
#Value("${server.servlet.context-path}")
private String contextPath;
#Value("${oauth2.client.id}")
public String CLIENT_ID;
#Value("${oauth2.client.secret}")
public String CLIENT_SECRET;
#Value("${oauth2.scope.read}")
public String SCOPE_READ;
#Value("${oauth2.grant.types}")
public String GRANT_TYPES;
#Value("${oauth2.scopes}")
public String SCOPES;
#Value("${oauth2.access.token.validity}")
public Integer TOKEN_VALID_SECONDS;
#Value("${oauth2.refresh.token.validity}")
public Integer REFRESH_TOKEN_VALID_SECONDS;
#Autowired
private DataSource dataSource;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(CLIENT_ID)
.secret(passwordEncoder().encode(CLIENT_SECRET))
.authorizedGrantTypes("password", "refresh_token", "client_credentials","authorization_code")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(TOKEN_VALID_SECONDS)
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALID_SECONDS);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
And this is my SecurityConfig class
#EnableGlobalMethodSecurity(securedEnabled=true)
#Configuration
#RequiredArgsConstructor
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final CustomUserDetailsService customUserDetailsService;
#Autowired
public BCryptPasswordEncoder passwordEncoder;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.antMatchers("/oauth/token/**").permitAll()
.antMatchers("/api/**" ).authenticated()
.anyRequest().authenticated()
.and().formLogin().permitAll()
.and().csrf().disable();
}
}
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
//login web service url describe on properties file
#Value("${loginServiceUri}")
public String loginServiceUri;
//login web service enabled
#Value("${loginWebServiceEnabled}")
public Boolean loginWebServiceEnabled;
#Override
public UserDetails loadUserByUsername(String username) {
log.debug("Trying to authenticate user with details....");
if (loginWebServiceEnabled && loginServiceUri != null){
//its necessary to call external Web Service to find the user and then look for
//into database
UserDto userDto = this.loginWebService(username);
if (userDto != null) {
// look for the user in data base
log.debug("User found at external login web service trying to look for at data base");
return lookUserDataBase(userDto.getUsername());
} else {
log.error("User not found at external login web service", username);
throw new UsernameNotFoundException(username);
}
} else {
// look for the user in data base
return lookUserDataBase(username);
}
}
/**
* Look for use in data base by user name
* #return
*/
private UserDetails lookUserDataBase(String userName) {
UserEntity user = userService.findEntityByUsername(userName);
if (user == null) {
log.error("User not found in data base", userName);
throw new UsernameNotFoundException(userName);
}
log.debug("User found in data base with userName: " + userName + " and authorities: " + user.getUserAuthority().toString());
return new UserAuthenticatedDetails(user);
}
/**
* Example Login Web Service call login
* #param name
* #return
*/
private UserDto loginWebService (String name){
xxxxxxxxxxx
}
}
Add these in your WebSecurityConfigurerAdapter class
#Autowired
public void setApplicationContext(ApplicationContext context) {
super.setApplicationContext(context);
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
try {
globalAuthBuilder.userDetailsService(userDetailsService);
} catch (Exception e) {
e.printStackTrace();
}
}
There is a "local" AuthenticationManagerBuilder and a "global" AuthenticationManagerBuilder and we need to set it on the global version in order to have this information passed to these other builder contexts.
Read more about it here
I solved with this lines in SecurityConfig;
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);
//this line solved the problem
endpoints.userDetailsService(customUserDetailsService);**
}
Either you configure some inMemory user as follows:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER")
.and()
.withUser("manager")
.password("password")
.credentialsExpired(true)
.accountExpired(true)
.accountLocked(true)
.authorities("WRITE_PRIVILEGES", "READ_PRIVILEGES")
.roles("MANAGER");
}
or you implement a UserDetailService e.g. which looks up in the database for a user.

Spring oauth2 basic authentication

I am trying to develop a rest api with spring security Using OAuth2 implementation. but how do I remove basic authentication. I just want to send a username and password to body and get a token on postman.
#Configuration
public class OAuthServerConfigration {
private static final String SERVER_RESOURCE_ID = "oauth2-server";
private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(SERVER_RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/api/**").and().authorizeRequests().antMatchers("/api/**").access("#oauth2.hasScope('read')");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret("$2a$10$5OkeCLKNs/BkdO0qcYRri.MdIcKhFvElAllhPgLfRQqG7wkEiPmq2")
.authorizedGrantTypes("password","authorization_code","refresh_token")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds(SERVER_RESOURCE_ID)
//.accessTokenValiditySeconds(ONE_DAY)
.accessTokenValiditySeconds(300)
.refreshTokenValiditySeconds(50);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
// we're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
}
}
#Configuration
#Order(2)
public static class ApiLoginConfig extends
WebSecurityConfigurerAdapter{
#Autowired
DataSource dataSource;
#Autowired
ClientDetailsService clientDetailsService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().csrf().disable().antMatcher("/oauth/token").authorizeRequests().anyRequest().permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
want to remove the basic authentication and send the username password in the body tag from the postman for get token
and I have got some problem
{
"error": "unauthorized",
"error_description": "There is no client authentication. Try adding an appropriate authentication filter."
}
In your #EnableAuthorizationServer configuration class in the method:-
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
Try to add the following:-
oauthServer.allowFormAuthenticationForClients()
After you have done that you will have to call the oauth get token url as below:-
URL will be the same as http(s)://{HOST_NAME}/oauth/token
HTTP method type now will be POST
Header:-
Content-Type=application/x-www-form-urlencoded
Parameters will be key value pairs in x-www-form-urlencoded in the body of postman
for client_credentials grant_type:-
grant_type=client_credentials
client_id=client_id_value
client_secret=client_secret_value
scope=scopes
for password grant_type:-
grant_type=password
client_id=client_id_value
client_secret=client_secret_value
scope=scopes
username=username
password=password
scopes will be comma separated here

Facing Access Denied (403) - Forbidden error in spring security oauth2

.antMatchers("/secure2/**").authenticated();
I have configured one of my api as protected, when I try to access it, It gives me Access Denied error message, I do not know what could be the reason. Note that I am passing valid access token.
My scenario:
Basically I have created logout rest api in authorization server and I want that, request with valid token is allowed to hit this api.
Request:
GET /auth/secure2 HTTP/1.1
Host: localhost:9191
Authorization: Bearer 33984141-1249-4465-a3aa-0b95a053fc63
Cache-Control: no-cache
Postman-Token: f4661790-a8e1-90ea-f6db-79cb37958cdf
Response:
{
"timestamp": 1500186837033,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/auth/secure2"
}
I found out that below method return false and due to that it raise the access denied error.
public final class ExpressionUtils {
public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
try {
return ((Boolean) expr.getValue(ctx, Boolean.class)).booleanValue();
}
catch (EvaluationException e) {
throw new IllegalArgumentException("Failed to evaluate expression '"
+ expr.getExpressionString() + "'", e);
}
}
}
Below are the screen shots which I captured by debugging in framework. Please also check the images mentioned in comment.
Code:
SecurityConfiguration.java :
import org.springframework.beans.factory.annotation.Autowired;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public ShaPasswordEncoder encoder() {
return new ShaPasswordEncoder(256);
}
#Override
public void configure(WebSecurity web) throws Exception {}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/secure3/").permitAll()
.antMatchers("/oauth/token/revoke/**").authenticated()
.antMatchers("/secure2/**").authenticated();
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
public GlobalSecurityConfiguration() {
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
Oauth2Configuration.java
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
//private static final String PROP_CLIENTID = "clientid";
//private static final String PROP_SECRET = "secret";
private static final String PROP_ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValidityInSeconds";
private static final String PROP_REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Autowired
private CustomUserDetailService userDetailsService;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(tokenStore())
.userDetailsService(userDetailsService)
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("clientId")
.scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_ACCESS_TOKEN_VALIDITY_SECONDS, Integer.class, 80))
.refreshTokenValiditySeconds(propertyResolver.getProperty(PROP_REFRESH_TOKEN_VALIDITY_SECONDS, Integer.class, 180))
.and().inMemory()
.withClient("clientid")
.scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("client_credentials")
.secret("secret");
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
Controller:
#Controller
#RequestMapping("/secure2")
public class SecureController1 {
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public String sayHello() {
return "Secure Hello secure2!";
}
}
What are the scenarios in which it raises access denied error message? Please let me know if any other information in needed.
I used these codes and they worked well.
OAuth2AuthorizationServerConfig.java:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
// Configure the token store and authentication manager
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//#formatter:off
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter()) // added for JWT
.authenticationManager(authenticationManager);
//#formatter:on
}
// Configure a client store. In-memory for simplicity, but consider other
// options for real apps.
//It is not necessary.works even without this func:)
// #Override
// public void configure(AuthorizationServerSecurityConfigurer oauthServer)
// throws Exception {
// oauthServer
// .tokenKeyAccess("permitAll()")
// .checkTokenAccess("isAuthenticated()");
// }
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//#formatter:off
clients
.inMemory()
.withClient("myclient")//username in basic auth header
.secret ("{noop}123")//password in basic auth header;
.authorizedGrantTypes("authorization_code", "implicit", "password", "client_credentials", "refresh_token")
.scopes("read")
//.redirectUris("http://localhost:9191/x")
.accessTokenValiditySeconds(86400); // 24 hours
//#formatter:on
}
// A token store bean. JWT token store
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter()); // For JWT. Use in-memory, jdbc, or other if not JWT
}
// Token converter. Needed for JWT
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123"); // symmetric key
return converter;
}
// Token services. Needed for JWT
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
// #Bean
//
// public PasswordEncoder passwordEncoder () {
//
// return new BCryptPasswordEncoder();
//
// }
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
//It is not important to use this func. /oauth/token is the default path of spring security to use oauth2. she is so clever!! :)
// #Override
// protected void configure(HttpSecurity http) throws Exception {
//
//
// http.authorizeRequests()
// .antMatchers(HttpMethod.POST, "/oauth/token").permitAll()
// .anyRequest().authenticated();
// }
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("ROLE");
}
//
// #Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(userDetailsService)
// .passwordEncoder(passwordEncoder());
// }
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//
// #Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
}
Output:
Please notice that the values of username and password used in code and postman, must be the same.
enter image description here
enter image description here
Hope to be helpful:)

Oauth2 with Spring Security Basic Authentication

I have a oauth2 service and I have a client. I'm trying to login with my oauth2 service but I keep getting "user must be login to get authentication". I'm trying to send user to oauth service to login then I'm trying to gettin the token but when I try that pop-up security login opens and I can't get authentication from my oauth2 service. When I login that pop-up security login then I can get authentication. Am I missing something? Please I need help.
My auth service class:
#SpringBootApplication
#EnableResourceServer
#Order(6)
public class AuthServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
}
#RestController
class PrincipalRestController {
#RequestMapping({"/user", "/me"})
Principal principal(Principal principal) {
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return principal;
}
#RequestMapping("/giris")
Principal giris(Principal principal) {
return principal;
}
}
#Configuration
#EnableAuthorizationServer
class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public OAuthConfiguration(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.scopes("openid", "read", "write").autoApprove(".*");
}
}
//#Component
//class AccountCLR implements CommandLineRunner {
//
// #Override
// public void run(String... strings) throws Exception {
// Stream.of("jlong,spring", "pwebb,boot", "zeynep,Bisoft123").map(x -> x.split(",")).forEach(tuple -> this.accountRepository.save(new Account(tuple[0], tuple[1], true)));
// }
//
// private final AccountRepository accountRepository;
//
// #Autowired
// public AccountCLR(AccountRepository accountRepository) {
// this.accountRepository = accountRepository;
// }
//
//}
#Service
class AccountUserDetailService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return accountRepository.findByKullaniciAdi(username);
}
private final KullaniciRepository accountRepository;
#Autowired
public AccountUserDetailService(KullaniciRepository accountRepository) {
this.accountRepository = accountRepository;
}
}
//My web security config
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
//#EnableOAuth2Client
//#EnableAuthorizationServer
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/metronic/css/**").permitAll()
.and().authorizeRequests().antMatchers("/metronic/image/**", "/image/**", "/metronic/css/fonts/**", "/metronic/fonts/**").permitAll()
.and().authorizeRequests().antMatchers("/js/**", "/metronic/js/**").permitAll()
.and().httpBasic().and().authorizeRequests()
.antMatchers("/login.html", "/language/**","/uaa/*", "/api/kullanici/user", "/logout", "/kilitEkrani.html", "/404.html").permitAll()
.anyRequest().authenticated().and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class).csrf().csrfTokenRepository(csrfTokenRepository()).and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login.html")
.permitAll().and().csrf().disable();
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
//my resource server
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/me")
.authorizeRequests().anyRequest().authenticated();//.and().csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
// #Autowired
// public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .inMemoryAuthentication()
// .withUser("zeynep").password("Bisoft123").roles("USER");
// }
}

Resources