Repository: jhipster/generator-jhipster
Pull request #9874 (2019-06-08 12:33:18)

Feign token refresh
Handle another oauth2 use case: communicating from the  gateway to a microservice, the token kind changes from Oauth2AuthenticationToken to JwtAuthenticationToken.

Also, refreshes the access token when calling Feign, also, consider the token as a JWT one when the gateway is calling a microservice via Feign.
so relates to #9707. 

It's functional, but I'm not sure that we can't achieve the same thing with some higher level spring capabilites.

@mraible , WDYT of this PR?

Regards,

-   [x] [Travis tests](https://travis-ci.org/jhipster/generator-jhipster/pull_requests) are green
-   [x] Tests are added where necessary
-   [x] Documentation is added/updated where necessary
-   [x] Coding Rules & Commit Guidelines as per our [CONTRIBUTING.md document](https://github.com/jhipster/generator-jhipster/blob/master/CONTRIBUTING.md) are followed

<!--
Please also reference the issue number in a commit message to [automatically close the related Github issue](https://help.github.com/articles/closing-issues-via-commit-messages/)

Note: It is also possible to add `[skip ci]` to your commit message to skip Travis tests
-->


vishal423: Looking at current change-list, I think there should have been an easy way to get this working. I am planning to spend time on refresh issue.
Tcharl: Up to you to merge after review.
Best regards
vishal423: I had quick look into the spring security code, and seems they support auto refresh with `WebClient` in `ServletOAuth2AuthorizedClientExchangeFilterFunction`. I am not sure if we can switch to use `WebClient` as ribbon rest client. For moment, your change looks good to unblock this issue.

Patch:
diff --git a/generators/server/files.js b/generators/server/files.js
index 7c5ff96644..38c5dcb3ca 100644
--- a/generators/server/files.js
+++ b/generators/server/files.js
@@ -444,6 +444,14 @@ const serverFiles = {
                 {
                     file: 'package/security/oauth2/AudienceValidator.java',
                     renameTo: generator => `${generator.javaDir}security/oauth2/AudienceValidator.java`
+                },
+                {
+                    file: 'package/security/oauth2/JwtAuthorityExtractor.java',
+                    renameTo: generator => `${generator.javaDir}security/oauth2/JwtAuthorityExtractor.java`
+                },
+                {
+                    file: 'package/security/oauth2/OAuthIdpTokenResponseDTO.java',
+                    renameTo: generator => `${generator.javaDir}security/oauth2/OAuthIdpTokenResponseDTO.java`
                 }
             ]
         },
diff --git a/generators/server/templates/src/main/java/package/config/SecurityConfiguration.java.ejs b/generators/server/templates/src/main/java/package/config/SecurityConfiguration.java.ejs
index 6858cb1821..cc2add653d 100644
--- a/generators/server/templates/src/main/java/package/config/SecurityConfiguration.java.ejs
+++ b/generators/server/templates/src/main/java/package/config/SecurityConfiguration.java.ejs
@@ -20,80 +20,79 @@ package <%= packageName %>.config;
 
 <%_ if (authenticationType === 'session' || authenticationType === 'jwt' || authenticationType === 'oauth2') { _%>
 import <%= packageName %>.security.*;
-<%_ if (authenticationType === 'jwt') { _%>
+    <%_ if (authenticationType === 'jwt') { _%>
 import <%= packageName %>.security.jwt.*;
-<%_ } _%>
-<%_ if (authenticationType === 'session') { _%>
+    <%_ } _%>
+    <%_ if (authenticationType === 'session') { _%>
 
-    <%_ if (!skipUserManagement) { _%>
+        <%_ if (!skipUserManagement) { _%>
 import io.github.jhipster.config.JHipsterProperties;
-    <%_ } _%>
+        <%_ } _%>
 import io.github.jhipster.security.*;
-<%_ } _%>
+    <%_ } _%>
 
-<%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
+    <%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
 import org.springframework.beans.factory.BeanInitializationException;
 import org.springframework.beans.factory.InitializingBean;
-<%_ } _%>
+    <%_ } _%>
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
-<%_ if (authenticationType !== 'uaa' && !(applicationType === 'microservice' && authenticationType === 'oauth2')) { _%>
+    <%_ if (authenticationType !== 'uaa' && !(applicationType === 'microservice' && authenticationType === 'oauth2')) { _%>
 import org.springframework.http.HttpMethod;
-<%_ } _%>
+    <%_ } _%>
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-<%_ if (authenticationType !== 'uaa' && !(applicationType === 'microservice' && authenticationType === 'oauth2')) { _%>
+    <%_ if (authenticationType !== 'uaa' && !(applicationType === 'microservice' && authenticationType === 'oauth2')) { _%>
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
-<%_ } _%>
+    <%_ } _%>
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-<%_ if (authenticationType === 'jwt' || (authenticationType === 'oauth2' && applicationType === 'microservice')) { _%>
+    <%_ if (authenticationType === 'jwt' || (authenticationType === 'oauth2' && applicationType === 'microservice')) { _%>
 import org.springframework.security.config.http.SessionCreationPolicy;
-<%_ } _%>
-<%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
+    <%_ } _%>
+    <%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
-<%_ } _%>
-<%_ if (authenticationType === 'oauth2') { _%>
-import org.springframework.core.convert.converter.Converter;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
+    <%_ } _%>
+    <%_ if (authenticationType === 'oauth2') { _%>
 import <%= packageName %>.security.oauth2.AudienceValidator;
+import <%=packageName%>.security.SecurityUtils;
 import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 import org.springframework.security.oauth2.jwt.*;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
-    <%_ if (applicationType === 'gateway') { _%>
+        <%_ if (applicationType === 'gateway') { _%>
 import <%=packageName%>.security.oauth2.AuthorizationHeaderFilter;
 import <%=packageName%>.security.oauth2.AuthorizationHeaderUtil;
-    <%_ } _%>
+        <%_ } _%>
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-<%_ if (applicationType !== 'microservice') { _%>
+        <%_ if (applicationType !== 'microservice') { _%>
 import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
 import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
-<%_ } _%>
+        <%_ } _%>
 import java.util.*;
-import java.util.stream.Collectors;
-<%_ } _%>
-<%_ if (authenticationType === 'session') { _%>
-    <%_ if (!skipUserManagement) { _%>
-import org.springframework.security.web.authentication.RememberMeServices;
     <%_ } _%>
+    <%_ if (authenticationType === 'session') { _%>
+        <%_ if (!skipUserManagement) { _%>
+import org.springframework.security.web.authentication.RememberMeServices;
+        <%_ } _%>
 import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
 import org.springframework.security.web.csrf.CsrfFilter;
-<%_ } _%>
-<%_ if (authenticationType === 'oauth2' && applicationType !== 'microservice') { _%>
+    <%_ } _%>
+    <%_ if (authenticationType === 'oauth2' && applicationType !== 'microservice') { _%>
 import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
 import org.springframework.security.web.csrf.CsrfFilter;
-<%_ } _%>
-<%_ if (authenticationType === 'jwt' && applicationType !== 'microservice') { _%>
+    <%_ } _%>
+    <%_ if (authenticationType === 'oauth2') { _%>
+import <%=packageName%>.security.oauth2.JwtAuthorityExtractor;
+    <%_ } _%>
+    <%_ if (authenticationType === 'jwt' && applicationType !== 'microservice') { _%>
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-<%_ } _%>
+    <%_ } _%>
 import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
-<%_ if (applicationType !== 'microservice') { _%>
+    <%_ if (applicationType !== 'microservice') { _%>
 import org.springframework.web.filter.CorsFilter;
-<%_ } _%>
+    <%_ } _%>
 import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
 
 @EnableWebSecurity
@@ -118,21 +117,26 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
     @Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}")
     private String issuerUri;
+
+    private final JwtAuthorityExtractor jwtAuthorityExtractor;
     <%_ } _%>
     private final SecurityProblemSupport problemSupport;
 
-    public SecurityConfiguration(<% if (authenticationType === 'session' && !skipUserManagement) { %>JHipsterProperties jHipsterProperties, RememberMeServices rememberMeServices, <% } if (authenticationType === 'jwt') { %>TokenProvider tokenProvider, <% } %><% if (applicationType !== 'microservice') { %>CorsFilter corsFilter, <% } %>SecurityProblemSupport problemSupport) {
-        <%_ if (authenticationType === 'session' && !skipUserManagement) { _%>
+    public SecurityConfiguration(<% if (authenticationType === 'session' && !skipUserManagement) { %>JHipsterProperties jHipsterProperties, RememberMeServices rememberMeServices, <% } if (authenticationType === 'jwt') { %>TokenProvider tokenProvider, <% } %><% if (applicationType !== 'microservice') { %>CorsFilter corsFilter, <% } %><% if (authenticationType === 'oauth2') { %>JwtAuthorityExtractor jwtAuthorityExtractor, <% } %>SecurityProblemSupport problemSupport) {
+    <%_ if (authenticationType === 'session' && !skipUserManagement) { _%>
         this.jHipsterProperties = jHipsterProperties;
         this.rememberMeServices = rememberMeServices;
-        <%_ } _%>
-        <%_ if (authenticationType === 'jwt') { _%>
+    <%_ } _%>
+    <%_ if (authenticationType === 'jwt') { _%>
         this.tokenProvider = tokenProvider;
-        <%_ } _%>
-        <%_ if (applicationType !== 'microservice') { _%>
+    <%_ } _%>
+    <%_ if (applicationType !== 'microservice') { _%>
         this.corsFilter = corsFilter;
-        <%_ } _%>
+    <%_ } _%>
         this.problemSupport = problemSupport;
+    <%_ if (authenticationType === 'oauth2') { _%>
+        this.jwtAuthorityExtractor = jwtAuthorityExtractor;
+    <%_ } _%>
     }
     <%_ if (authenticationType === 'session') { _%>
 
@@ -145,8 +149,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     public AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler() {
         return new AjaxAuthenticationFailureHandler();
     }
-    <%_ } _%>
-    <%_ if (authenticationType === 'session') { _%>
 
     @Bean
     public AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler() {
@@ -160,20 +162,20 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
         return new BCryptPasswordEncoder();
     }
     <%_ } _%>
-    <%_ if (authenticationType !== 'uaa' && !(applicationType === 'microservice' && authenticationType === 'oauth2')) { _%>
+    <%_ if (authenticationType !== 'uaa' && applicationType !== 'microservice') { _%>
 
     @Override
-    public void configure(WebSecurity web) throws Exception {
+    public void configure(WebSecurity web) {
         web.ignoring()
             .antMatchers(HttpMethod.OPTIONS, "/**")
-            <%_ if (!skipClient) { _%>
+        <%_ if (!skipClient) { _%>
             .antMatchers("/app/**/*.{js,html}")
             .antMatchers("/i18n/**")
             .antMatchers("/content/**")
-            <%_ } _%>
-            <%_ if (devDatabaseType === 'h2Disk' || devDatabaseType === 'h2Memory') { _%>
+        <%_ } _%>
+        <%_ if (devDatabaseType === 'h2Disk' || devDatabaseType === 'h2Memory') { _%>
             .antMatchers("/h2-console/**")
-            <%_ } _%>
+        <%_ } _%>
             .antMatchers("/swagger-ui/index.html")
             .antMatchers("/test/**");
     }
@@ -184,46 +186,44 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
         // @formatter:off
         http
             .csrf()
-            <%_ if (['session','oauth2'].includes(authenticationType) && applicationType !== 'microservice') { _%>
+    <%_ if (['session','oauth2'].includes(authenticationType) && applicationType !== 'microservice') { _%>
             .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
         .and()
-            <%_ } else{ _%>
+    <%_ } else { _%>
             .disable()
-            <%_ } _%>
-            <%_ if (applicationType !== 'microservice') { _%>
-                <%_ if (authenticationType === 'jwt') { _%>
+    <%_ } _%>
+    <%_ if (applicationType !== 'microservice') { _%>
+        <%_ if (authenticationType === 'jwt') { _%>
             .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
-                <%_ } else if (authenticationType === 'session' || authenticationType === 'oauth2') { _%>
+        <%_ } else if (authenticationType === 'session' || authenticationType === 'oauth2') { _%>
             .addFilterBefore(corsFilter, CsrfFilter.class)
-                <%_ } _%>
-            <%_ } _%>
+        <%_ } _%>
+    <%_ } _%>
             .exceptionHandling()
-            <%_ if (authenticationType !== 'oauth2') { _%>
+    <%_ if (authenticationType !== 'oauth2') { _%>
             .authenticationEntryPoint(problemSupport)
-            <%_ } _%>
+    <%_ } _%>
             .accessDeniedHandler(problemSupport)
-            <%_ if (authenticationType === 'session') { _%>
-                <%_ if (!skipUserManagement) { _%>
+    <%_ if (authenticationType === 'session') { _%>
+        <%_ if (!skipUserManagement) { _%>
         .and()
             .rememberMe()
             .rememberMeServices(rememberMeServices)
             .rememberMeParameter("remember-me")
             .key(jHipsterProperties.getSecurity().getRememberMe().getKey())
-                <%_ } _%>
+        <%_ } _%>
         .and()
             .formLogin()
             .loginProcessingUrl("/api/authentication")
             .successHandler(ajaxAuthenticationSuccessHandler())
             .failureHandler(ajaxAuthenticationFailureHandler())
             .permitAll()
-            <%_ } _%>
-            <%_ if (authenticationType === 'session') { _%>
         .and()
             .logout()
             .logoutUrl("/api/logout")
             .logoutSuccessHandler(ajaxLogoutSuccessHandler())
             .permitAll()
-            <%_ } _%>
+    <%_ } _%>
         .and()
             .headers()
             .contentSecurityPolicy("default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:")
@@ -235,50 +235,53 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
             .frameOptions()
             .deny()
         .and()
-        <%_ if (authenticationType === 'jwt' || (authenticationType === 'oauth2' && applicationType === 'microservice')) { _%>
+    <%_ if (authenticationType === 'jwt' || (authenticationType === 'oauth2' && applicationType === 'microservice')) { _%>
             .sessionManagement()
             .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
         .and()
-        <%_ } _%>
+    <%_ } _%>
             .authorizeRequests()
-            <%_ if (authenticationType !== 'oauth2') { _%>
+    <%_ if (authenticationType !== 'oauth2') { _%>
             .antMatchers("/api/authenticate").permitAll()
-            <%_ } _%>
-            <%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
+    <%_ } _%>
+    <%_ if (authenticationType !== 'oauth2' && !skipUserManagement) { _%>
             .antMatchers("/api/register").permitAll()
             .antMatchers("/api/activate").permitAll()
             .antMatchers("/api/account/reset-password/init").permitAll()
             .antMatchers("/api/account/reset-password/finish").permitAll()
-            <%_ } _%>
-            <%_ if (authenticationType === 'oauth2') { _%>
+    <%_ } _%>
+    <%_ if (authenticationType === 'oauth2') { _%>
             .antMatchers("/api/auth-info").permitAll()
-            <%_ } _%>
+    <%_ } _%>
             .antMatchers("/api/**").authenticated()
-            <%_ if (websocket === 'spring-websocket') { _%>
+    <%_ if (websocket === 'spring-websocket') { _%>
             .antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
             .antMatchers("/websocket/**").permitAll()
-            <%_ } _%>
+    <%_ } _%>
             .antMatchers("/management/health").permitAll()
             .antMatchers("/management/info").permitAll()
             .antMatchers("/management/prometheus").permitAll()
             .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)<%_ if (authenticationType === 'session') { %>;<% } %>
-        <%_ if (authenticationType === 'jwt') { _%>
-            <%_ if (applicationType === 'monolith') { _%>
+    <%_ if (authenticationType === 'jwt') { _%>
+        <%_ if (applicationType === 'monolith') { _%>
         .and()
             .httpBasic()
-            <%_ } _%>
+        <%_ } _%>
         .and()
             .apply(securityConfigurerAdapter());
-        <%_ } else if (authenticationType === 'oauth2') { _%>
-            <%_ if (['monolith', 'gateway'].includes(applicationType)) { _%>
+    <%_ } else if (authenticationType === 'oauth2') { _%>
+        <%_ if (['monolith', 'gateway'].includes(applicationType)) { _%>
         .and()
             .oauth2Login()
-            <%_ } _%>
+        <%_ } _%>
         .and()
             .oauth2ResourceServer()
                 .jwt()
-                .jwtAuthenticationConverter(mapGroupOrRolesClaimToGrantedAuthorities());
-        <%_ } _%>
+                .jwtAuthenticationConverter(jwtAuthorityExtractor)
+                .and()
+            .and()
+                .oauth2Client();
+    <%_ } _%>
         // @formatter:on
     }
     <%_ if (authenticationType === 'jwt') { _%>
@@ -289,7 +292,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     <%_ } _%>
     <%_ if (authenticationType === 'oauth2') { _%>
 
-    <%_ if (applicationType !== 'microservice') { _%>
+        <%_ if (applicationType !== 'microservice') { _%>
     /**
      * Map authorities from "groups" or "roles" claim in ID Token.
      *
@@ -303,37 +306,12 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
             authorities.forEach(authority -> {
                 OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
-                mappedAuthorities.addAll(mapRolesToGrantedAuthorities(
-                    getRolesFromClaims(oidcUserAuthority.getUserInfo().getClaims())));
+                mappedAuthorities.addAll(SecurityUtils.extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
             });
-
             return mappedAuthorities;
         };
     }
-
-    <%_ } _%>
-    private Converter<Jwt, AbstractAuthenticationToken> mapGroupOrRolesClaimToGrantedAuthorities() {
-        return new JwtAuthenticationConverter() {
-            @Override
-            protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
-                return mapRolesToGrantedAuthorities(
-                    getRolesFromClaims(jwt.getClaims())
-                );
-            }
-        };
-    }
-
-    @SuppressWarnings("unchecked")
-    private Collection<String> getRolesFromClaims(Map<String, Object> claims) {
-        return (Collection<String>) claims.getOrDefault("groups",
-            claims.getOrDefault("roles", new ArrayList<>()));
-    }
-
-    private List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
-        return roles.stream()
-            .filter(role -> role.startsWith("ROLE_"))
-            .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
-    }
+        <%_ } _%>
 
     @Bean
     JwtDecoder jwtDecoder() {
@@ -377,10 +355,10 @@ import org.springframework.security.oauth2.provider.token.TokenStore;
 import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
 import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
 import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
-<%_ if (applicationType === 'gateway') { _%>
+    <%_ if (applicationType === 'gateway') { _%>
 import org.springframework.security.web.csrf.CsrfFilter;
 import org.springframework.web.filter.CorsFilter;
-<%_ } _%>
+    <%_ } _%>
 import org.springframework.web.client.RestTemplate;
 
 @Configuration
@@ -395,23 +373,23 @@ public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
     <%_ } _%>
     public SecurityConfiguration(OAuth2Properties oAuth2Properties<% if (applicationType === 'gateway') { %>, CorsFilter corsFilter<% } %>) {
         this.oAuth2Properties = oAuth2Properties;
-        <%_ if (applicationType === 'gateway') { _%>
+    <%_ if (applicationType === 'gateway') { _%>
         this.corsFilter = corsFilter;
-        <%_ } _%>
+    <%_ } _%>
     }
 
     @Override
     public void configure(HttpSecurity http) throws Exception {
         http
             .csrf()
-		<%_ if (applicationType === 'gateway') { _%>
+    <%_ if (applicationType === 'gateway') { _%>
             .ignoringAntMatchers("/h2-console/**")
             .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
         .and()
             .addFilterBefore(corsFilter, CsrfFilter.class)
-		<%_ } else { _%>
+    <%_ } else { _%>
             .disable()
-		<%_ } _%>
+    <%_ } _%>
             .headers()
             .frameOptions()
             .disable()
diff --git a/generators/server/templates/src/main/java/package/security/SecurityUtils.java.ejs b/generators/server/templates/src/main/java/package/security/SecurityUtils.java.ejs
index fc3c6ab9ed..0fc30e529f 100644
--- a/generators/server/templates/src/main/java/package/security/SecurityUtils.java.ejs
+++ b/generators/server/templates/src/main/java/package/security/SecurityUtils.java.ejs
@@ -18,6 +18,10 @@
 -%>
 package <%=packageName%>.security;
 
+import org.springframework.security.core.GrantedAuthority;
+<%_ if (authenticationType === 'oauth2') { _%>
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+<%_ } _%>
 <%_ if (reactive) { _%>
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 <%_ } _%>
@@ -28,15 +32,22 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
 <%_ if (authenticationType === 'oauth2') { _%>
 import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 <%_ } _%>
 <%_ if (reactive) { _%>
 import reactor.core.publisher.Mono;
 <%_ } _%>
-<%_ if (!reactive) { _%>
 
+import java.util.ArrayList;
+<%_ if (authenticationType === 'oauth2') { _%>
+import java.util.stream.Collectors;
+import java.util.Collection;
+<%_ } _%>
+import java.util.List;
 <%_ if (authenticationType === 'oauth2') { _%>
 import java.util.Map;
 <%_ } _%>
+<%_ if (!reactive) { _%>
 import java.util.Optional;
 <%_ } _%>
 
@@ -54,31 +65,33 @@ public final class SecurityUtils {
      * @return the login of the current user.
      */
     public static <% if (!reactive) { %>Optional<% } else { %>Mono<% } %><String> getCurrentUserLogin() {
-        <%_ if (!reactive) { _%>
+<%_ if (!reactive) { _%>
         SecurityContext securityContext = SecurityContextHolder.getContext();
         return Optional.ofNullable(securityContext.getAuthentication())
-        <%_ } else { _%>
+<%_ } else { _%>
         return ReactiveSecurityContextHolder.getContext()
             .map(SecurityContext::getAuthentication)
-        <%_ } _%>
+<%_ } _%>
             .map(authentication -> {
                 if (authentication.getPrincipal() instanceof UserDetails) {
                     UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
                     return springSecurityUser.getUsername();
-                <%_ if (authenticationType === 'oauth2') { _%>
+<%_ if (authenticationType === 'oauth2') { _%>
+                } else if (authentication instanceof JwtAuthenticationToken) {
+                    return (String) ((JwtAuthenticationToken)authentication).getToken().getClaims().get("preferred_username");
                 } else if (authentication.getPrincipal() instanceof DefaultOidcUser) {
                     Map<String, Object> attributes = ((DefaultOidcUser) authentication.getPrincipal()).getAttributes();
                     if (attributes.containsKey("preferred_username")) {
                         return (String) attributes.get("preferred_username");
                     }
-                <%_ } _%>
+<%_ } _%>
                 } else if (authentication.getPrincipal() instanceof String) {
                     return (String) authentication.getPrincipal();
                 }
                 return null;
             });
     }
-    <%_ if (authenticationType === 'jwt') { _%>
+<%_ if (authenticationType === 'jwt') { _%>
 
     /**
      * Get the JWT of the current user.
@@ -96,7 +109,7 @@ public final class SecurityUtils {
             .filter(authentication -> authentication.getCredentials() instanceof String)
             .map(authentication -> (String) authentication.getCredentials());
     }
-    <%_ } _%>
+<%_ } _%>
 
     /**
      * Check if a user is authenticated.
@@ -104,15 +117,29 @@ public final class SecurityUtils {
      * @return true if the user is authenticated, false otherwise.
      */
     public static <% if (!reactive) { %>boolean<% } else { %>Mono<Boolean><% } %> isAuthenticated() {
-        <%_ if (!reactive) { _%>
+<%_ if (!reactive) { _%>
         SecurityContext securityContext = SecurityContextHolder.getContext();
         return Optional.ofNullable(securityContext.getAuthentication())
-        <%_ } else { _%>
+<%_ } else { _%>
         return ReactiveSecurityContextHolder.getContext()
             .map(SecurityContext::getAuthentication)
-        <%_ } _%>
-            .map(authentication -> authentication.getAuthorities().stream()
-                .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS)))<% if (!reactive) { %>
+<%_ } _%>
+            .map(authentication -> {
+                List<GrantedAuthority> authorities = new ArrayList<>();
+<%_ if (authenticationType === 'oauth2') { _%>
+                if (authentication instanceof JwtAuthenticationToken) {
+                    authorities.addAll(
+                        extractAuthorityFromClaims(((JwtAuthenticationToken) authentication).getToken()
+                            .getClaims()));
+                } else {
+<%_ } _%>
+                    authorities.addAll(authentication.getAuthorities());
+<%_ if (authenticationType === 'oauth2') { _%>
+                }
+<%_ } _%>
+                return authorities.stream()
+                    .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
+            })<% if (!reactive) { %>
             .orElse(false)<% } %>;
     }
 
@@ -125,15 +152,47 @@ public final class SecurityUtils {
      * @return true if the current user has the authority, false otherwise.
      */
     public static <% if (!reactive) { %>boolean<% } else { %>Mono<Boolean><% } %> isCurrentUserInRole(String authority) {
-        <%_ if (!reactive) { _%>
+<%_ if (!reactive) { _%>
         SecurityContext securityContext = SecurityContextHolder.getContext();
         return Optional.ofNullable(securityContext.getAuthentication())
-        <%_ } else { _%>
+<%_ } else { _%>
         return ReactiveSecurityContextHolder.getContext()
             .map(SecurityContext::getAuthentication)
-        <%_ } _%>
-            .map(authentication -> authentication.getAuthorities().stream()
-                .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)))<% if (!reactive) { %>
+<%_ } _%>
+            .map(authentication -> {
+                List<GrantedAuthority> authorities = new ArrayList<>();
+<%_ if (authenticationType === 'oauth2') { _%>
+                if (authentication instanceof JwtAuthenticationToken) {
+                    authorities.addAll(
+                        extractAuthorityFromClaims(((JwtAuthenticationToken) authentication).getToken()
+                            .getClaims()));
+                } else {
+<%_ } _%>
+                    authorities.addAll(authentication.getAuthorities());
+<%_ if (authenticationType === 'oauth2') { _%>
+                }
+<%_ } _%>
+                return authorities.stream()
+                    .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
+            })<% if (!reactive) { %>
             .orElse(false)<% } %>;
     }
+<%_ if (authenticationType === 'oauth2') { _%>
+    public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
+        return mapRolesToGrantedAuthorities(
+            getRolesFromClaims(claims));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
+        return (Collection<String>) claims.getOrDefault("groups",
+            claims.getOrDefault("roles", new ArrayList<>()));
+    }
+
+    private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
+        return roles.stream()
+            .filter(role -> role.startsWith("ROLE_"))
+            .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
+    }
+<%_ } _%>
 }
diff --git a/generators/server/templates/src/main/java/package/security/oauth2/AuthorizationHeaderUtil.java.ejs b/generators/server/templates/src/main/java/package/security/oauth2/AuthorizationHeaderUtil.java.ejs
index cebd7ee5b9..2c5efed21b 100644
--- a/generators/server/templates/src/main/java/package/security/oauth2/AuthorizationHeaderUtil.java.ejs
+++ b/generators/server/templates/src/main/java/package/security/oauth2/AuthorizationHeaderUtil.java.ejs
@@ -18,46 +18,158 @@
 -%>
 package <%=packageName%>.security.oauth2;
 
-import org.springframework.security.authentication.AbstractAuthenticationToken;
+import java.net.URI;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import <%=packageName%>.security.oauth2.OAuthIdpTokenResponseDTO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.http.MediaType;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.FormHttpMessageConverter;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
+import org.springframework.security.oauth2.core.*;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
 
-import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 @Component
 public class AuthorizationHeaderUtil {
 
     private final OAuth2AuthorizedClientService clientService;
+    private final RestTemplateBuilder restTemplateBuilder;
+    private final Logger log = LoggerFactory.getLogger(AuthorizationHeaderUtil.class);
 
-    public AuthorizationHeaderUtil(OAuth2AuthorizedClientService clientService) {
+    public AuthorizationHeaderUtil(OAuth2AuthorizedClientService clientService, RestTemplateBuilder restTemplateBuilder) {
         this.clientService = clientService;
+        this.restTemplateBuilder = restTemplateBuilder;
     }
 
     public Optional<String> getAuthorizationHeader() {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        String accessToken = null;
-        AbstractAuthenticationToken authToken = (AbstractAuthenticationToken) authentication;
-        if (authToken instanceof OAuth2AuthenticationToken) {
-            OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authToken;
+        if (authentication instanceof OAuth2AuthenticationToken) {
+            OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
+            String name =  oauthToken.getName();
+            String registrationId = oauthToken.getAuthorizedClientRegistrationId();
             OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(
-                oauthToken.getAuthorizedClientRegistrationId(),
-                oauthToken.getName());
-            accessToken = client.getAccessToken().getTokenValue();
-        } else if (authToken instanceof JwtAuthenticationToken) {
-            accessToken = ((JwtAuthenticationToken) authToken).getToken().getTokenValue();
-        }
+            registrationId,
+            name);
+
+            if (null == client) {
+                throw new OAuth2AuthorizationException(new OAuth2Error("access_denied", "The token is expired", null));
+            }
+            OAuth2AccessToken accessToken = client.getAccessToken();
+
+            if (accessToken != null) {
+                String tokenType = accessToken.getTokenType().getValue();
+                String accessTokenValue = accessToken.getTokenValue();
+                if (isExpired(accessToken)) {
+                    log.info("AccessToken expired, refreshing automatically");
+                    accessTokenValue = refreshToken(client, oauthToken);
+                    if (null == accessTokenValue) {
+                        SecurityContextHolder.getContext().setAuthentication(null);
+                        throw new OAuth2AuthorizationException(new OAuth2Error("access_denied", "The token is expired", null));
+                    }
+                }
+                String authorizationHeaderValue = String.format("%s %s", tokenType, accessTokenValue);
+                return Optional.of(authorizationHeaderValue);
+            }
 
-        if (accessToken == null) {
-            return Optional.empty();
-        } else {
-            String tokenType = "Bearer";
-            String authorizationHeaderValue = String.format("%s %s", tokenType, accessToken);
+        } else if (authentication instanceof JwtAuthenticationToken) {
+            JwtAuthenticationToken accessToken = (JwtAuthenticationToken) authentication;
+            String tokenValue = accessToken.getToken().getTokenValue();
+            String authorizationHeaderValue = String.format("%s %s", OAuth2AccessToken.TokenType.BEARER.getValue(), tokenValue);
             return Optional.of(authorizationHeaderValue);
         }
+        return Optional.empty();
+    }
+
+    private String refreshToken(OAuth2AuthorizedClient client, OAuth2AuthenticationToken oauthToken) {
+        OAuth2AccessTokenResponse atr = refreshTokenClient(client);
+        if (atr == null || atr.getAccessToken() == null) {
+            log.info("Failed to refresh token for user");
+            return null;
+        }
+
+        OAuth2RefreshToken refreshToken = atr.getRefreshToken() != null? atr.getRefreshToken(): client.getRefreshToken();
+        OAuth2AuthorizedClient updatedClient = new OAuth2AuthorizedClient(
+            client.getClientRegistration(),
+            client.getPrincipalName(),
+            atr.getAccessToken(),
+            refreshToken
+        );
+
+        clientService.saveAuthorizedClient(updatedClient, oauthToken);
+        return atr.getAccessToken().getTokenValue();
+    }
+
+    private OAuth2AccessTokenResponse refreshTokenClient(OAuth2AuthorizedClient currentClient ) {
+
+        MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
+        formParameters.add(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());
+        formParameters.add(OAuth2ParameterNames.REFRESH_TOKEN, currentClient.getRefreshToken().getTokenValue());
+        formParameters.add(OAuth2ParameterNames.CLIENT_ID, currentClient.getClientRegistration().getClientId());
+        RequestEntity requestEntity = RequestEntity
+            .post(URI.create(currentClient.getClientRegistration().getProviderDetails().getTokenUri()))
+            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+            .body(formParameters);
+        try {
+            RestTemplate r = restTemplate(currentClient.getClientRegistration().getClientId(), currentClient.getClientRegistration().getClientSecret());
+            ResponseEntity<OAuthIdpTokenResponseDTO> responseEntity = r.exchange(requestEntity, OAuthIdpTokenResponseDTO.class);
+            return toOAuth2AccessTokenResponse(responseEntity.getBody());
+        } catch (OAuth2AuthorizationException e) {
+            log.error("Unable to refresh token", e);
+            throw new OAuth2AuthenticationException(e.getError(), e);
+        }
+    }
+
+    private OAuth2AccessTokenResponse toOAuth2AccessTokenResponse(OAuthIdpTokenResponseDTO oAuthIdpResponse) {
+        Map<String, Object> additionalParameters = new HashMap<>();
+        additionalParameters.put("id_token", oAuthIdpResponse.getIdToken());
+        additionalParameters.put("not-before-policy", oAuthIdpResponse.getNotBefore());
+        additionalParameters.put("refresh_expires_in", oAuthIdpResponse.getRefreshExpiresIn());
+        additionalParameters.put("session_state", oAuthIdpResponse.getSessionState());
+        return OAuth2AccessTokenResponse.withToken(oAuthIdpResponse.getAccessToken())
+            .expiresIn(oAuthIdpResponse.getExpiresIn())
+            .refreshToken(oAuthIdpResponse.getRefreshToken())
+            .scopes(Pattern.compile("\\s").splitAsStream(oAuthIdpResponse.getScope()).collect(Collectors.toSet()))
+            .tokenType(OAuth2AccessToken.TokenType.BEARER)
+            .additionalParameters(additionalParameters)
+            .build();
+    }
+
+    private RestTemplate restTemplate(String clientId, String clientSecret) {
+        return restTemplateBuilder
+            .additionalMessageConverters(
+                new FormHttpMessageConverter(),
+                new OAuth2AccessTokenResponseHttpMessageConverter())
+            .errorHandler(new OAuth2ErrorResponseErrorHandler())
+            .basicAuthentication(clientId, clientSecret)
+            .build();
+    }
+
+    private boolean isExpired(OAuth2AccessToken accessToken) {
+        Instant now = Instant.now();
+        Instant expiresAt = accessToken.getExpiresAt();
+        return now.isAfter(expiresAt.minus(Duration.ofMinutes(1L)));
     }
 }
diff --git a/generators/server/templates/src/main/java/package/security/oauth2/JwtAuthorityExtractor.java.ejs b/generators/server/templates/src/main/java/package/security/oauth2/JwtAuthorityExtractor.java.ejs
new file mode 100644
index 0000000000..75c1483ac6
--- /dev/null
+++ b/generators/server/templates/src/main/java/package/security/oauth2/JwtAuthorityExtractor.java.ejs
@@ -0,0 +1,39 @@
+<%#
+ Copyright 2013-2019 the original author or authors from the JHipster project.
+
+ This file is part of the JHipster project, see https://jhipster.github.io/
+ for more information.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-%>
+package <%=packageName%>.security.oauth2;
+
+import <%=packageName%>.security.SecurityUtils;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+
+@Component
+public class JwtAuthorityExtractor extends JwtAuthenticationConverter {
+
+    public JwtAuthorityExtractor() {
+    }
+
+    @Override
+    protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
+        return SecurityUtils.extractAuthorityFromClaims(jwt.getClaims());
+    }
+}
diff --git a/generators/server/templates/src/main/java/package/security/oauth2/OAuthIdpTokenResponseDTO.java.ejs b/generators/server/templates/src/main/java/package/security/oauth2/OAuthIdpTokenResponseDTO.java.ejs
new file mode 100644
index 0000000000..5d8b960046
--- /dev/null
+++ b/generators/server/templates/src/main/java/package/security/oauth2/OAuthIdpTokenResponseDTO.java.ejs
@@ -0,0 +1,159 @@
+<%#
+ Copyright 2013-2019 the original author or authors from the JHipster project.
+
+ This file is part of the JHipster project, see https://jhipster.github.io/
+ for more information.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-%>
+package <%=packageName%>.security.oauth2;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+public class OAuthIdpTokenResponseDTO implements Serializable {
+
+    @JsonProperty("token_type")
+    private String tokenType;
+
+    private String scope;
+
+    @JsonProperty("expires_in")
+    private Long expiresIn;
+
+    @JsonProperty("ext_expires_in")
+    private Long extExpiresIn;
+
+    @JsonProperty("expires_on")
+    private Long expiresOn;
+
+    @JsonProperty("not-before-policy")
+    private Long notBefore;
+
+    private UUID resource;
+
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    @JsonProperty("refresh_token")
+    private String refreshToken;
+
+    @JsonProperty("id_token")
+    private String idToken;
+
+    @JsonProperty("session_state")
+    private String sessionState;
+
+    @JsonProperty("refresh_expires_in")
+    private String refreshExpiresIn;
+
+    public OAuthIdpTokenResponseDTO() {}
+
+    public String getRefreshExpiresIn() {
+        return refreshExpiresIn;
+    }
+
+    public void setRefreshExpiresIn(String refreshExpiresIn) {
+        this.refreshExpiresIn = refreshExpiresIn;
+    }
+
+    public String getSessionState() {
+        return sessionState;
+    }
+
+    public void setSessionState(String sessionState) {
+        this.sessionState = sessionState;
+    }
+
+    public String getTokenType() {
+        return tokenType;
+    }
+
+    public void setTokenType(String tokenType) {
+        this.tokenType = tokenType;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public Long getExpiresIn() {
+        return expiresIn;
+    }
+
+    public void setExpiresIn(Long expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    public Long getExtExpiresIn() {
+        return extExpiresIn;
+    }
+
+    public void setExtExpiresIn(Long extExpiresIn) {
+        this.extExpiresIn = extExpiresIn;
+    }
+
+    public Long getExpiresOn() {
+        return expiresOn;
+    }
+
+    public void setExpiresOn(Long expiresOn) {
+        this.expiresOn = expiresOn;
+    }
+
+    public Long getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(Long notBefore) {
+        this.notBefore = notBefore;
+    }
+
+    public UUID getResource() {
+        return resource;
+    }
+
+    public void setResource(UUID resource) {
+        this.resource = resource;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public String getRefreshToken() {
+        return refreshToken;
+    }
+
+    public void setRefreshToken(String refreshToken) {
+        this.refreshToken = refreshToken;
+    }
+
+    public String getIdToken() {
+        return idToken;
+    }
+
+    public void setIdToken(String idToken) {
+        this.idToken = idToken;
+    }
+}
diff --git a/generators/server/templates/src/main/java/package/service/UserService.java.ejs b/generators/server/templates/src/main/java/package/service/UserService.java.ejs
index 4ae1e26480..f39a9e732e 100644
--- a/generators/server/templates/src/main/java/package/service/UserService.java.ejs
+++ b/generators/server/templates/src/main/java/package/service/UserService.java.ejs
@@ -92,9 +92,6 @@ import java.util.*;
 <%_ if (!reactive) { _%>
 import java.util.stream.Collectors;
 <%_ } _%>
-<%_ if (authenticationType === 'oauth2' && databaseType !== 'no') { _%>
-import java.util.stream.Stream;
-<%_ } _%>
 
 /**
  * Service class for managing users.


Test patch:
diff --git a/test/utils/expected-files.js b/test/utils/expected-files.js
index 20f8ee1021..67cbf1a044 100644
--- a/test/utils/expected-files.js
+++ b/test/utils/expected-files.js
@@ -569,6 +569,9 @@ const expectedFiles = {
 
     oauth2: [
         `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/SecurityConfiguration.java`,
+        `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/security/oauth2/JwtAuthorityExtractor.java`,
+        `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/security/oauth2/AudienceValidator.java`,
+        `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/security/oauth2/OAuthIdpTokenResponseDTO.java`,
         `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/domain/User.java`,
         `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/web/rest/AccountResource.java`,
         `${DOCKER_DIR}keycloak.yml`