@Override
  public OAuth2AccessTokenEntity createIdToken(
      ClientDetailsEntity client,
      OAuth2Request request,
      Date issueTime,
      String sub,
      OAuth2AccessTokenEntity accessToken) {

    JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm();

    if (client.getIdTokenSignedResponseAlg() != null) {
      signingAlg = client.getIdTokenSignedResponseAlg();
    }

    OAuth2AccessTokenEntity idTokenEntity = new OAuth2AccessTokenEntity();
    JWTClaimsSet.Builder idClaims = new JWTClaimsSet.Builder();

    // if the auth time claim was explicitly requested OR if the client always wants the auth time,
    // put it in
    if (request.getExtensions().containsKey("max_age")
        || (request
            .getExtensions()
            .containsKey(
                "idtoken")) // TODO: parse the ID Token claims (#473) -- for now assume it could be
                            // in there
        || (client.getRequireAuthTime() != null && client.getRequireAuthTime())) {

      if (request.getExtensions().get(AuthenticationTimeStamper.AUTH_TIMESTAMP) != null) {

        Long authTimestamp =
            Long.parseLong(
                (String) request.getExtensions().get(AuthenticationTimeStamper.AUTH_TIMESTAMP));
        if (authTimestamp != null) {
          idClaims.claim("auth_time", authTimestamp / 1000L);
        }
      } else {
        // we couldn't find the timestamp!
        logger.warn(
            "Unable to find authentication timestamp! There is likely something wrong with the configuration.");
      }
    }

    idClaims.issueTime(issueTime);

    if (client.getIdTokenValiditySeconds() != null) {
      Date expiration =
          new Date(System.currentTimeMillis() + (client.getIdTokenValiditySeconds() * 1000L));
      idClaims.expirationTime(expiration);
      idTokenEntity.setExpiration(expiration);
    }

    idClaims.issuer(configBean.getIssuer());
    idClaims.subject(sub);
    idClaims.audience(Lists.newArrayList(client.getClientId()));
    idClaims.jwtID(UUID.randomUUID().toString()); // set a random NONCE in the middle of it

    String nonce = (String) request.getExtensions().get("nonce");
    if (!Strings.isNullOrEmpty(nonce)) {
      idClaims.claim("nonce", nonce);
    }

    Set<String> responseTypes = request.getResponseTypes();

    if (responseTypes.contains("token")) {
      // calculate the token hash
      Base64URL at_hash = IdTokenHashUtils.getAccessTokenHash(signingAlg, accessToken);
      idClaims.claim("at_hash", at_hash);
    }

    if (client.getIdTokenEncryptedResponseAlg() != null
        && !client.getIdTokenEncryptedResponseAlg().equals(Algorithm.NONE)
        && client.getIdTokenEncryptedResponseEnc() != null
        && !client.getIdTokenEncryptedResponseEnc().equals(Algorithm.NONE)
        && (!Strings.isNullOrEmpty(client.getJwksUri()) || client.getJwks() != null)) {

      JWTEncryptionAndDecryptionService encrypter = encrypters.getEncrypter(client);

      if (encrypter != null) {

        EncryptedJWT idToken =
            new EncryptedJWT(
                new JWEHeader(
                    client.getIdTokenEncryptedResponseAlg(),
                    client.getIdTokenEncryptedResponseEnc()),
                idClaims.build());

        encrypter.encryptJwt(idToken);

        idTokenEntity.setJwt(idToken);

      } else {
        logger.error("Couldn't find encrypter for client: " + client.getClientId());
      }

    } else {

      JWT idToken;

      if (signingAlg.equals(Algorithm.NONE)) {
        // unsigned ID token
        idToken = new PlainJWT(idClaims.build());

      } else {

        // signed ID token

        if (signingAlg.equals(JWSAlgorithm.HS256)
            || signingAlg.equals(JWSAlgorithm.HS384)
            || signingAlg.equals(JWSAlgorithm.HS512)) {

          JWSHeader header =
              new JWSHeader(
                  signingAlg,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  jwtService.getDefaultSignerKeyId(),
                  null,
                  null);
          idToken = new SignedJWT(header, idClaims.build());

          JWTSigningAndValidationService signer =
              symmetricCacheService.getSymmetricValidtor(client);

          // sign it with the client's secret
          signer.signJwt((SignedJWT) idToken);
        } else {
          idClaims.claim("kid", jwtService.getDefaultSignerKeyId());

          JWSHeader header =
              new JWSHeader(
                  signingAlg,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  null,
                  jwtService.getDefaultSignerKeyId(),
                  null,
                  null);

          idToken = new SignedJWT(header, idClaims.build());

          // sign it with the server's key
          jwtService.signJwt((SignedJWT) idToken);
        }
      }

      idTokenEntity.setJwt(idToken);
    }

    idTokenEntity.setAuthenticationHolder(accessToken.getAuthenticationHolder());

    // create a scope set with just the special "id-token" scope
    // Set<String> idScopes = new HashSet<String>(token.getScope()); // this would copy the original
    // token's scopes in, we don't really want that
    Set<String> idScopes = Sets.newHashSet(SystemScopeService.ID_TOKEN_SCOPE);
    idTokenEntity.setScope(idScopes);

    idTokenEntity.setClient(accessToken.getClient());

    return idTokenEntity;
  }
  private OAuth2AccessTokenEntity createAssociatedToken(
      ClientDetailsEntity client, Set<String> scope) {

    // revoke any previous tokens that might exist, just to be sure
    OAuth2AccessTokenEntity oldToken = tokenService.getRegistrationAccessTokenForClient(client);
    if (oldToken != null) {
      tokenService.revokeAccessToken(oldToken);
    }

    // create a new token

    Map<String, String> authorizationParameters = Maps.newHashMap();
    OAuth2Request clientAuth =
        new OAuth2Request(
            authorizationParameters,
            client.getClientId(),
            Sets.newHashSet(new SimpleGrantedAuthority("ROLE_CLIENT")),
            true,
            scope,
            null,
            null,
            null,
            null);
    OAuth2Authentication authentication = new OAuth2Authentication(clientAuth, null);

    OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity();
    token.setClient(client);
    token.setScope(scope);

    AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity();
    authHolder.setAuthentication(authentication);
    authHolder = authenticationHolderRepository.save(authHolder);
    token.setAuthenticationHolder(authHolder);

    JWTClaimsSet claims =
        new JWTClaimsSet.Builder()
            .audience(Lists.newArrayList(client.getClientId()))
            .issuer(configBean.getIssuer())
            .issueTime(new Date())
            .expirationTime(token.getExpiration())
            .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it
            .build();

    JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm();
    JWSHeader header =
        new JWSHeader(
            signingAlg,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            jwtService.getDefaultSignerKeyId(),
            null,
            null);
    SignedJWT signed = new SignedJWT(header, claims);

    jwtService.signJwt(signed);

    token.setJwt(signed);

    return token;
  }