3

I am new to spring and feign and exploring since a couple of days. I am able to make auth request to our protected resources(username/password) and using the JWT token returned by auth service in the subsequent request headers. However, i would like to call the auth service only when the token is expired using the same credentials.Spring cloud has OAuth2FeignRequestInterceptor which is doing exactly the same thing but using client id and secret key.

Any such custom interceptors for handling tokens generated by username and password?

PabloMayor
  • 31
  • 1
  • 1
  • 3
  • You will likely have to write your own interceptor that will cache the tokens and reuse them, only requesting a new token once they are expired. – Shadow Man Mar 14 '18 at 23:17

3 Answers3

6

If your JWT token provider is OAuth 2.0 compliant, you can configure the OAuth2FeignRequestInterceptor, with an OAuth2ProtectedResourceDetails object. This object is the base class for all OAuth 2.0 grant type information. In your case, I recommend using ResourceOwnerPasswordResourceDetails instead. This will allow you to configure an interceptor using a username and password.

@Configuration
public class OAuth2RequestInterceptorConfiguration {

    @Bean
    public OAuth2FeignRequestInterceptor requestInterceptor() {
        OAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
        OAuth2ProtectedResourceDetails resourceDetails =
            new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setUsername("username");
        resourceDetails.setPassword("password");
        return new OAuth2FeignRequestInterceptor(clientContext, resourceDetails);
    }
}

For other cases, you will need to create your own RequestInterceptor

public class MyRequestInterceptor implements RequestInterceptor {

    private String jwt;
    private LocalDateTime expirationDate;

    @Override
    public void apply(RequestTemplate requestTemplate) {
        /* validate and refresh your token, this sample is not thread safe */
        if (LocalDateTime.now().isAfter(expirationDate)) {
            requestToken();
        }

        /* use the token */
        requestTemplate.header("Authorization: Bearer " + this.jwt);
    }
}
Kevin Davis
  • 1,193
  • 8
  • 14
3

You can try with something like:

public class FeignConfiguration {

    @Value("${security.jwt.token}")
    private String jwtToken;

    @Bean
    public RequestInterceptor requestInterceptor() {
        @Override
        public void apply(RequestTemplate requestTemplate) {
            requestTemplate.header("Authorization", jwtToken);
        }
    }
}
alvaro torrico
  • 675
  • 6
  • 17
0

Not entirely clear on the use case but maybe following code snippet will be useful as well. Our service expects a signed OAuth Bearer JWT access token. You can see we validate explicitly on the JWT expiration and we return an error when JWT token would be expired. In our set-up, it's the consumer's responsibility to catch this error and to use the refresh token (to retrieve a new access token) when this scenario would occur.

Hopefully this clarifies a bit.

/**
 * Created by Wim Van den Brande on 11/02/2018.
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);

    public static final String SCOPE = "scope";
    public static final String HEADER_AUTHORIZATION = "Authorization";
    public static final String HEADER_BEARER = "Bearer";
    public static final String HEADER_ENCRYPTION_TYPE = "Encryption-Type";
    public static final String ASSYMETRIC_ENCRYPTION_RS256 = "RS256";

    @Value("${CS.signedPublicKey}")
    private String signedPublicKey;

    @Autowired
    private Key signinKey;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO : to enable CSRF
        http.httpBasic().and().csrf().disable();

        TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
        http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/admin", "/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**");
    }

    // Actual Implementation of Token Based Authentication
    // ---------------------------------------------------
    public class TokenAuthenticationFilter extends GenericFilterBean {

        @Override
        public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
            final HttpServletRequest httpRequest = (HttpServletRequest) request;
            final HttpServletResponse httpResponse = (HttpServletResponse) response;

            String authorizationHeader = httpRequest.getHeader(HEADER_AUTHORIZATION);

            if (authorizationHeader == null) {
                    httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, String.format("Request doesn't contain header %s.", HEADER_AUTHORIZATION));
                    return;
            }

            String[] authorizationHeaderValues = authorizationHeader.split("\\s+");

            if (authorizationHeaderValues.length != 2 || !authorizationHeaderValues[0].equals(HEADER_BEARER)) {
                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, String.format("AuthorizationHeader %s is an invalid header ", authorizationHeader));
                return;
            }

            String accessToken = authorizationHeaderValues[1];
            Claims jwtClaims;

            // Symmetric Encryption is our default

            if (httpRequest.getHeader(HEADER_ENCRYPTION_TYPE) == null || !httpRequest.getHeader(HEADER_ENCRYPTION_TYPE).equals(ASSYMETRIC_ENCRYPTION_RS256)) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Using Symmetric algorithm");
                }

                try {
                    jwtClaims = Jwts.parser().setSigningKey(signinKey).parseClaimsJws(accessToken).getBody();
                }
                catch (ExpiredJwtException e) {
                    httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JSON Web Token has expired");
                    return;
                }
            }
Sven Eberth
  • 3,057
  • 12
  • 24
  • 29