Untitled

 avatar
unknown
plain_text
a year ago
10 kB
6
Indexable
package com.kotak.collection.reporting.filter;

import com.kotak.collection.reporting.client.TokenRefreshAuthClient;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.WebUtils;

/** The type Refresh access token filter. */
@Configuration
@Log4j2
public class RefreshAccessTokenFilter extends GenericFilterBean {

  @Value("${httpOnly.cookie.key}")
  private String cookieKey;

  @Autowired private TokenRefreshAuthClient tokenRefreshAuthClient;

  /* Custom filter to return same access token if it is found valid else continue the request  */
  @Override
  public void doFilter(
      final ServletRequest request, final ServletResponse response, final FilterChain chain)
      throws IOException, ServletException {
    try {

      chain.doFilter(request, response);
      switch (((HttpServletResponse) response).getStatus()) {
        case 401: {
          String token = WebUtils.getCookie((HttpServletRequest) request, cookieKey).getValue();

          ResponseEntity refreshTokenResponse =
              tokenRefreshAuthClient.refreshAccessToken(cookieKey, token);

          HttpServletResponse finalResponse = (HttpServletResponse) response;
          int status =
              refreshTokenResponse.getStatusCode().is2xxSuccessful()
                  ? finalResponse.getStatus()
                  : 401;
          finalResponse.setStatus(status);
          finalResponse.addHeader(
              "Set-Cookie", refreshTokenResponse.getHeaders().getFirst("Set-Cookie"));
        }
          break;

        default:
          return;
      }
    } catch (Exception e) {
      log.error("Error occurred while refreshing access token {}", e);
    }
  }
}



package com.kotak.collection.reporting.client;

import java.util.HashMap;
import java.util.Map;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * The type Token refresh auth client.
 */
@Component
@Log4j2
public class TokenRefreshAuthClient {
  @Value("${refresh.token.endpoint}")
  private String refreshTokenEndpoint;

  @Value("${auth.server.clientid}")
  private String clientId;

  @Value("${auth.server.domain}")
  private String domain;

  /**
   * Refresh access token response entity.
   *
   * @param cookieKey the cookie key
   * @param token the token
   * @return the response entity
   */
  public ResponseEntity refreshAccessToken(final String cookieKey, final String token) {
    HttpHeaders tokenHeaders = new HttpHeaders();
    tokenHeaders.setContentType(MediaType.APPLICATION_JSON);

    Map<String, String> requestBody = new HashMap<>();
    requestBody.put("clientId", clientId);
    requestBody.put("domain", domain);
    tokenHeaders.add("Cookie", cookieKey + "=" + token + "; Max-Age=7776000; Path=/; HttpOnly;");

    RestTemplate restTemplate = new RestTemplate();
    HttpEntity renewTokenRequest = new HttpEntity<>(requestBody, tokenHeaders);
    return restTemplate.postForEntity(refreshTokenEndpoint, renewTokenRequest, String.class);
  }
}



package com.kotak.collection.reporting.configuration;

import com.kotak.collection.reporting.filter.RefreshAccessTokenFilter;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.jwk.source.JWKSourceBuilder;
import com.nimbusds.jose.proc.JWSAlgorithmFamilyJWSKeySelector;
import com.nimbusds.jose.proc.JWSKeySelector;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.util.DefaultResourceRetriever;
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtIssuerValidator;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.util.WebUtils;

/** The type Security config. */
@EnableWebSecurity
@Configuration
@Log4j2
public class SecurityConfig {

  @Value("${spring.security.oauth2.resourceserver.jwt.jwks-uri}")
  private String keyUri;

  @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
  private String iss;

  @Value("${auth.server.clientid}")
  private String clientId;

  /*
   * Need to place [clientId]_Token  (eg. 19kungvqs1dmi21234nfjgta7l_Token) in properties file
   */
  @Value("${httpOnly.cookie.key}")
  private String cookieKey;

  private Collection<OAuth2TokenValidator<Jwt>> validators;

  @Autowired private ApplicationContext applicationContext;

  @Autowired private RefreshAccessTokenFilter refreshAccessTokenFilter;

  /**
   * Filter requests for authentication and authorization.
   *
   * @param http HttpSecurity
   * @return SecurityFilterChain
   */
  @Bean
  public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {

    return http.csrf((csrf) -> csrf.disable())
        .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
        .oauth2ResourceServer(
            oauth2 ->
                oauth2.jwt(jwt -> jwt.decoder(jwtDecoder())).bearerTokenResolver(this::resolver))
        .build();
  }

  /**
   * Resolver string.
   *
   * @param request the request
   * @return the string
   */
  public String resolver(final HttpServletRequest request) {
    final Cookie cookie = WebUtils.getCookie(request, cookieKey);
    if (cookie != null) {
      int delimiterIndex = cookie.getValue().lastIndexOf("#");
      return cookie.getValue().substring(0, delimiterIndex);
    }
    return null;
  }

  /*  */
  /**
   * Calling custom filter in case of /token/refresh API to check validity of access token.
   *
   * @return FilterRegistrationBean
   */
  @Bean
  public FilterRegistrationBean<RefreshAccessTokenFilter> expirationCheckFilter() {
    FilterRegistrationBean<RefreshAccessTokenFilter> registrationBean =
        new FilterRegistrationBean<>();

    registrationBean.setFilter(refreshAccessTokenFilter);
    registrationBean.addUrlPatterns("*");
    registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);

    return registrationBean;
  }

  /**
   * Client validator o auth 2 token validator.
   *
   * @return the o auth 2 token validator
   */
  public OAuth2TokenValidator<Jwt> clientValidator() {
    return new JwtClaimValidator<>(
        OAuth2TokenIntrospectionClaimNames.CLIENT_ID, clientID -> clientID.equals(clientId));
  }

  /**
   * Token validator o auth 2 token validator.
   *
   * @return the o auth 2 token validator
   */
  public OAuth2TokenValidator<Jwt> tokenValidator() {
    this.validators =
        List.of(new JwtTimestampValidator(), new JwtIssuerValidator(iss), clientValidator());
    return new DelegatingOAuth2TokenValidator<>(this.validators);
  }

  /**
   * Jwt decoder jwt decoder.
   *
   * @return the jwt decoder
   */
  @Bean
  public JwtDecoder jwtDecoder() {

    JWSKeySelector<SecurityContext> jwsKeySelector = null;
    DefaultJWTProcessor<SecurityContext> jwtProcessor = null;

    try {
      URL jwksUrl = new URL(keyUri);
      DefaultResourceRetriever resourceRetriever = new DefaultResourceRetriever(5000, 5000);
      /* Schedule public key refresh in (TTL - (refresh timeout + refresh ahead timeout)) */
      JWKSource<SecurityContext> jwkSource =
          JWKSourceBuilder.create(jwksUrl, resourceRetriever)
              .cache(360000, 60000)
              .rateLimited(30000)
              .refreshAheadCache(60000, true)
              .build();

      jwsKeySelector = JWSAlgorithmFamilyJWSKeySelector.fromJWKSource(jwkSource);

      jwtProcessor = new DefaultJWTProcessor<>();
      jwtProcessor.setJWSKeySelector(jwsKeySelector);

    } catch (Exception e) {
      log.error("Error while refreshing jwks cache {}", e);
    }
    return new NimbusJwtDecoder(jwtProcessor);
  }
}


refresh.token.endpoint=https://dev-ums.collection.kotak.internal/ra-uams/api/token/refresh
auth.server.clientid=2bj0pg2s2kggjsoa18g1dc2rto
auth.server.domain=collectionuamsdev
httpOnly.cookie.key=2bj0pg2s2kggjsoa18g1dc2rto_Token

spring.security.oauth2.resourceserver.jwt.jwks-uri=https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_pKhfXQU56/.well-known/jwks.json
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_pKhfXQU56
Editor is loading...
Leave a Comment