/**
   * Get Subject Attributes
   *
   * @param token
   * @return
   */
  @Override
  protected Map<ClaimMapping, String> getSubjectAttributes(OAuthClientResponse token) {

    Map<ClaimMapping, String> claims = new HashMap<ClaimMapping, String>();

    try {

      String json =
          sendRequest(
              token.getParam(GoogleOAuth2AuthenticationConstant.GOOGLE_USERINFO_ENDPOINT),
              token.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN));
      if (StringUtils.isNotBlank(json)) {
        Map<String, Object> jsonObject = JSONUtils.parseJSON(json);

        if (jsonObject != null) {
          for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            claims.put(
                ClaimMapping.build(entry.getKey(), entry.getKey(), null, false),
                entry.getValue().toString());
            if (log.isDebugEnabled()) {
              log.debug(
                  "Adding claim from end-point data mapping : "
                      + entry.getKey()
                      + " - "
                      + entry.getValue());
            }
          }
        }
      }
    } catch (Exception e) {
      log.error("Error occurred while accessing google user info endpoint", e);
    }

    return claims;
  }
  /**
   * this method are overridden for extra claim request to google end-point
   *
   * @param request
   * @param response
   * @param context
   * @throws AuthenticationFailedException
   */
  @Override
  protected void processAuthenticationResponse(
      HttpServletRequest request, HttpServletResponse response, AuthenticationContext context)
      throws AuthenticationFailedException {

    try {

      Map<String, String> authenticatorProperties = context.getAuthenticatorProperties();
      String clientId = authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID);
      String clientSecret = authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_SECRET);
      String tokenEndPoint;
      if (getTokenEndpoint(authenticatorProperties) != null) {
        tokenEndPoint = getTokenEndpoint(authenticatorProperties);
      } else {
        tokenEndPoint = authenticatorProperties.get(OIDCAuthenticatorConstants.OAUTH2_TOKEN_URL);
      }

      String callBackUrl =
          authenticatorProperties.get(GoogleOAuth2AuthenticationConstant.CALLBACK_URL);

      log.debug("callBackUrl : " + callBackUrl);

      if (callBackUrl == null) {
        callBackUrl = CarbonUIUtil.getAdminConsoleURL(request);
        callBackUrl = callBackUrl.replace("commonauth/carbon/", "commonauth");
      }

      @SuppressWarnings({"unchecked"})
      Map<String, String> paramValueMap =
          (Map<String, String>) context.getProperty("oidc:param.map");

      if (paramValueMap != null && paramValueMap.containsKey("redirect_uri")) {
        callBackUrl = paramValueMap.get("redirect_uri");
      }

      OAuthAuthzResponse authzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
      String code = authzResponse.getCode();

      OAuthClientRequest accessRequest = null;
      accessRequest = getAccessRequest(tokenEndPoint, clientId, clientSecret, callBackUrl, code);

      // create OAuth client that uses custom http client under the hood
      OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
      OAuthClientResponse oAuthResponse = null;
      oAuthResponse = getOAuthResponse(accessRequest, oAuthClient, oAuthResponse);
      // TODO : return access token and id token to framework
      String accessToken = "";
      String idToken = "";
      if (oAuthResponse != null) {
        accessToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);
        idToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ID_TOKEN);
      }

      if (accessToken != null && (idToken != null || !requiredIDToken(authenticatorProperties))) {

        context.setProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN, accessToken);

        if (idToken != null) {
          context.setProperty(OIDCAuthenticatorConstants.ID_TOKEN, idToken);

          String base64Body = idToken.split("\\.")[1];
          byte[] decoded = Base64.decodeBase64(base64Body.getBytes());
          String json = new String(decoded, Charset.forName("utf-8"));

          if (log.isDebugEnabled()) {
            log.debug("Id token json string : " + json);
          }

          Map<String, Object> jsonObject = JSONUtils.parseJSON(json);

          if (jsonObject != null) {
            Map<ClaimMapping, String> claims = getSubjectAttributes(oAuthResponse);

            String authenticatedUser =
                (String) jsonObject.get(OIDCAuthenticatorConstants.Claim.EMAIL);
            AuthenticatedUser authenticatedUserObj =
                AuthenticatedUser.createFederateAuthenticatedUserFromSubjectIdentifier(
                    authenticatedUser);
            authenticatedUserObj.setUserAttributes(claims);
            context.setSubject(authenticatedUserObj);
          } else {
            if (log.isDebugEnabled()) {
              log.debug("Decoded json object is null");
            }
            throw new AuthenticationFailedException("Decoded json object is null");
          }
        } else {
          if (log.isDebugEnabled()) {
            log.debug("Authentication Failed");
          }
          throw new AuthenticationFailedException("Authentication Failed");
        }

      } else {
        throw new AuthenticationFailedException("Authentication Failed");
      }
    } catch (OAuthProblemException e) {
      throw new AuthenticationFailedException("Error occurred while acquiring access token", e);
    } catch (JSONException e) {
      throw new AuthenticationFailedException("Error occurred while parsing json object", e);
    }
  }