private String calculateExpiryDateTime(
      Iterable<JsonValue> tokens, OAuth2ProviderSettings oAuth2ProviderSettings)
      throws ServerException {
    long maxExpiryMilliseconds = 0L;

    for (JsonValue token : tokens) {
      long tokenExpiryMilliseconds =
          Long.parseLong(getAttributeValue(token, EXPIRY_TIME.getOAuthField()));

      if (tokenExpiryMilliseconds == -1) {
        return null;
      }

      if (OAUTH_REFRESH_TOKEN.equals(getAttributeValue(token, TOKEN_NAME.getOAuthField()))) {
        if (oAuth2ProviderSettings.issueRefreshTokensOnRefreshingToken()) {
          return null;
        }
        tokenExpiryMilliseconds += oAuth2ProviderSettings.getAccessTokenLifetime() * 1000;
      }

      if (tokenExpiryMilliseconds > maxExpiryMilliseconds) {
        maxExpiryMilliseconds = tokenExpiryMilliseconds;
      }
    }

    return ISODateTimeFormat.dateTime().print(maxExpiryMilliseconds);
  }
  /** {@inheritDoc} */
  public OpenIdConnectToken createOpenIDToken(
      ResourceOwner resourceOwner,
      String clientId,
      String authorizationParty,
      String nonce,
      String ops,
      OAuth2Request request)
      throws ServerException, InvalidClientException, NotFoundException {

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);

    final OpenIdConnectClientRegistration clientRegistration =
        clientRegistrationStore.get(clientId, request);
    final String algorithm = clientRegistration.getIDTokenSignedResponseAlgorithm();

    final long currentTimeInSeconds = System.currentTimeMillis() / 1000;
    final long exp =
        clientRegistration.getJwtTokenLifeTime(providerSettings) / 1000 + currentTimeInSeconds;

    final String realm = realmNormaliser.normalise(request.<String>getParameter(REALM));

    final String iss = providerSettings.getIssuer();

    final List<String> amr = getAMRFromAuthModules(request, providerSettings);

    final byte[] clientSecret = clientRegistration.getClientSecret().getBytes(Utils.CHARSET);
    final KeyPair keyPair = providerSettings.getServerKeyPair();

    final String atHash = generateAtHash(algorithm, request, providerSettings);
    final String cHash = generateCHash(algorithm, request, providerSettings);

    final String acr = getAuthenticationContextClassReference(request);

    final String kid = generateKid(providerSettings.getJWKSet(), algorithm);

    final String opsId = UUID.randomUUID().toString();

    final long authTime = resourceOwner.getAuthTime();

    final String subId = clientRegistration.getSubValue(resourceOwner.getId(), providerSettings);

    try {
      tokenStore.create(
          json(
              object(
                  field(OAuth2Constants.CoreTokenParams.ID, set(opsId)),
                  field(OAuth2Constants.JWTTokenParams.LEGACY_OPS, set(ops)),
                  field(OAuth2Constants.CoreTokenParams.EXPIRE_TIME, set(Long.toString(exp))))));
    } catch (CoreTokenException e) {
      logger.error("Unable to create id_token user session token", e);
      throw new ServerException("Could not create token in CTS");
    }

    final OpenAMOpenIdConnectToken oidcToken =
        new OpenAMOpenIdConnectToken(
            kid,
            clientSecret,
            keyPair,
            algorithm,
            iss,
            subId,
            clientId,
            authorizationParty,
            exp,
            currentTimeInSeconds,
            authTime,
            nonce,
            opsId,
            atHash,
            cHash,
            acr,
            amr,
            realm);
    request.setSession(ops);

    // See spec section 5.4. - add claims to id_token based on 'response_type' parameter
    String responseType = request.getParameter(OAuth2Constants.Params.RESPONSE_TYPE);
    if (providerSettings.isAlwaysAddClaimsToToken()
        || (responseType != null
            && responseType.trim().equals(OAuth2Constants.JWTTokenParams.ID_TOKEN))) {
      appendIdTokenClaims(request, providerSettings, oidcToken);
    } else if (providerSettings.getClaimsParameterSupported()) {
      appendRequestedIdTokenClaims(request, providerSettings, oidcToken);
    }

    return oidcToken;
  }