/** {@inheritDoc} */
  public void deleteAuthorizationCode(String authorizationCode) {
    if (logger.messageEnabled()) {
      logger.message(
          "DefaultOAuthTokenStoreImpl::Deleting Authorization code: " + authorizationCode);
    }
    JsonValue oAuthToken;

    // Read from CTS
    try {
      oAuthToken = tokenStore.read(authorizationCode);
    } catch (CoreTokenException e) {
      logger.error(
          "DefaultOAuthTokenStoreImpl::Unable to read authorization code corresponding to id: "
              + authorizationCode,
          e);
      throw new OAuthProblemException(
          Status.SERVER_ERROR_INTERNAL.getCode(),
          "Internal error",
          "Could not read token from CTS: " + e.getMessage(),
          null);
    }

    if (oAuthToken == null) {
      logger.error(
          "DefaultOAuthTokenStoreImpl::Unable to read authorization code corresponding to id: "
              + authorizationCode);
      throw new OAuthProblemException(
          Status.CLIENT_ERROR_NOT_FOUND.getCode(),
          "Not found",
          "Could not find token using CTS",
          null);
    }

    // Delete the code
    try {
      tokenStore.delete(authorizationCode);
    } catch (CoreTokenException e) {
      logger.error(
          "DefaultOAuthTokenStoreImpl::Unable to delete authorization code corresponding to id: "
              + authorizationCode,
          e);
      throw new OAuthProblemException(
          Status.SERVER_ERROR_INTERNAL.getCode(),
          "Internal error",
          "Could not delete token from CTS: " + e.getMessage(),
          null);
    }
  }
  /** {@inheritDoc} */
  public AccessToken readAccessToken(OAuth2Request request, String tokenId)
      throws ServerException, InvalidGrantException, NotFoundException {

    logger.message("Reading access token");

    JsonValue token;

    // Read from CTS
    try {
      token = tokenStore.read(tokenId);
    } catch (CoreTokenException e) {
      logger.error("Unable to read access token corresponding to id: " + tokenId, e);
      throw new ServerException("Could not read token in CTS: " + e.getMessage());
    }

    if (token == null) {
      logger.error("Unable to read access token corresponding to id: " + tokenId);
      throw new InvalidGrantException("Could not read token in CTS");
    }

    OpenAMAccessToken accessToken = new OpenAMAccessToken(token);
    validateTokenRealm(accessToken.getRealm(), request);

    request.setToken(AccessToken.class, accessToken);
    return accessToken;
  }
  /** {@inheritDoc} */
  public AuthorizationCode readAuthorizationCode(OAuth2Request request, String code)
      throws InvalidGrantException, ServerException, NotFoundException {
    if (logger.messageEnabled()) {
      logger.message("Reading Authorization code: " + code);
    }
    final JsonValue token;

    // Read from CTS
    try {
      token = tokenStore.read(code);
    } catch (CoreTokenException e) {
      logger.error("Unable to read authorization code corresponding to id: " + code, e);
      throw new ServerException("Could not read token from CTS: " + e.getMessage());
    }

    if (token == null) {
      logger.error("Unable to read authorization code corresponding to id: " + code);
      throw new InvalidGrantException("The provided access grant is invalid, expired, or revoked.");
    }

    OpenAMAuthorizationCode authorizationCode = new OpenAMAuthorizationCode(token);
    validateTokenRealm(authorizationCode.getRealm(), request);

    request.setToken(AuthorizationCode.class, authorizationCode);
    return authorizationCode;
  }
 @Override
 public void updateRefreshToken(RefreshToken refreshToken) {
   try {
     deleteRefreshToken(refreshToken.getTokenId());
     tokenStore.create(refreshToken);
   } catch (CoreTokenException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to create refresh token " + refreshToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not create token in CTS",
         null);
   } catch (InvalidRequestException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to delete refresh token " + refreshToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not delete token in CTS",
         null);
   }
 }
  /** {@inheritDoc} */
  public void updateAuthorizationCode(AuthorizationCode authorizationCode) {
    deleteAuthorizationCode(authorizationCode.getTokenId());

    // Store in CTS
    try {
      tokenStore.create(authorizationCode);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"UPDATED_AUTHORIZATION_CODE", authorizationCode.toString()};
        auditLogger.logAccessMessage("CREATED_AUTHORIZATION_CODE", obs, null);
      }
    } catch (CoreTokenException e) {
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_UPDATE_AUTHORIZATION_CODE", authorizationCode.toString()};
        auditLogger.logErrorMessage("FAILED_UPDATE_AUTHORIZATION_CODE", obs, null);
      }
      logger.error(
          "DefaultOAuthTokenStoreImpl::Unable to create authorization code "
              + authorizationCode.getTokenInfo(),
          e);
      throw new OAuthProblemException(
          Status.SERVER_ERROR_INTERNAL.getCode(),
          "Internal error",
          "Could not create token in CTS",
          null);
    }
  }
  /** {@inheritDoc} */
  public void deleteRefreshToken(String refreshTokenId) throws InvalidRequestException {

    // Delete the code
    try {
      tokenStore.delete(refreshTokenId);
    } catch (CoreTokenException e) {
      logger.error("Unable to delete refresh token corresponding to id: " + refreshTokenId, e);
      throw new InvalidRequestException();
    }
  }
 @Override
 public void deleteDeviceCode(String clientId, String code, OAuth2Request request)
     throws ServerException, NotFoundException, InvalidGrantException {
   try {
     readDeviceCode(clientId, code, request);
     tokenStore.delete(code);
   } catch (CoreTokenException e) {
     throw new ServerException("Could not delete user code state");
   }
 }
 @Override
 public void updateDeviceCode(DeviceCode code, OAuth2Request request)
     throws ServerException, NotFoundException, InvalidGrantException {
   try {
     readDeviceCode(code.getClientId(), code.getDeviceCode(), request);
     tokenStore.update(code);
   } catch (CoreTokenException e) {
     throw new ServerException("Could not update user code state");
   }
 }
  /** {@inheritDoc} */
  public void deleteAccessToken(String accessTokenId) throws ServerException {
    logger.message("Deleting access token");

    // Delete the code
    try {
      tokenStore.delete(accessTokenId);
    } catch (CoreTokenException e) {
      logger.error("Unable to delete access token corresponding to id: " + accessTokenId, e);
      throw new ServerException("Could not delete token from CTS: " + e.getMessage());
    }
  }
  /** {@inheritDoc} */
  public JsonValue queryForToken(String tokenId) throws InvalidRequestException {

    JsonValue results;

    try {
      results =
          tokenStore.query(
              or(
                  equalTo(OAuthTokenField.PARENT.getField(), tokenId),
                  equalTo(OAuthTokenField.REFRESH_TOKEN.getField(), tokenId)));
    } catch (CoreTokenException e) {
      logger.error("Unable to query refresh token corresponding to id: " + tokenId, e);
      throw new InvalidRequestException();
    }

    return results;
  }
  @Override
  public DeviceCode readDeviceCode(String userCode, OAuth2Request request)
      throws ServerException, NotFoundException, InvalidGrantException {
    try {
      JsonValue token = tokenStore.query(equalTo(CoreTokenField.STRING_FOURTEEN, userCode));

      if (token.size() != 1) {
        throw new InvalidGrantException();
      }

      DeviceCode deviceCode = new DeviceCode(json(token.asSet().iterator().next()));
      request.setToken(DeviceCode.class, deviceCode);
      return deviceCode;
    } catch (CoreTokenException e) {
      logger.error("Unable to read device code corresponding to id: " + userCode, e);
      throw new ServerException("Could not read token in CTS: " + e.getMessage());
    }
  }
  @Override
  public DeviceCode readDeviceCode(String clientId, String code, OAuth2Request request)
      throws ServerException, NotFoundException, InvalidGrantException {
    try {
      JsonValue token = tokenStore.read(code);

      if (token == null) {
        return null;
      }

      DeviceCode deviceCode = new DeviceCode(token);
      if (!clientId.equals(deviceCode.getClientId())) {
        throw new InvalidGrantException();
      }
      validateTokenRealm(deviceCode.getRealm(), request);
      request.setToken(DeviceCode.class, deviceCode);
      return deviceCode;
    } catch (CoreTokenException e) {
      logger.error("Unable to read device code corresponding to id: " + code, e);
      throw new ServerException("Could not read token in CTS: " + e.getMessage());
    }
  }
  /** {@inheritDoc} */
  public RefreshToken readRefreshToken(OAuth2Request request, String tokenId)
      throws ServerException, InvalidGrantException, NotFoundException {

    logger.message("Read refresh token");
    JsonValue token;

    try {
      token = tokenStore.read(tokenId);
    } catch (CoreTokenException e) {
      logger.error("Unable to read refresh token corresponding to id: " + tokenId, e);
      throw new ServerException("Could not read token in CTS: " + e.getMessage());
    }

    if (token == null) {
      logger.error("Unable to read refresh token corresponding to id: " + tokenId);
      throw new InvalidGrantException("grant is invalid");
    }

    OpenAMRefreshToken refreshToken = new OpenAMRefreshToken(token);
    validateTokenRealm(refreshToken.getRealm(), request);

    request.setToken(RefreshToken.class, refreshToken);
    return refreshToken;
  }
 public void updateAccessToken(AccessToken accessToken) {
   try {
     deleteAccessToken(accessToken.getTokenId());
     tokenStore.create(accessToken);
   } catch (ServerException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to delete access token " + accessToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not delete token in CTS",
         null);
   } catch (CoreTokenException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to create access token " + accessToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not create token in CTS",
         null);
   }
 }
  /** {@inheritDoc} */
  public DeviceCode createDeviceCode(
      Set<String> scope,
      String clientId,
      String nonce,
      String responseType,
      String state,
      String acrValues,
      String prompt,
      String uiLocales,
      String loginHint,
      Integer maxAge,
      String claims,
      OAuth2Request request,
      String codeChallenge,
      String codeChallengeMethod)
      throws ServerException, NotFoundException {

    logger.message("DefaultOAuthTokenStoreImpl::Creating Authorization code");

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
    final String deviceCode = UUID.randomUUID().toString();

    String userCode = null;
    int i;
    for (i = 0; userCode == null && i < 10; i++) {
      // A 6-byte array will result in an 8-char Base64 user code.
      byte[] randomBytes = new byte[6];
      secureRandom.nextBytes(randomBytes);
      userCode = Base64.encode(randomBytes);
      try {
        readDeviceCode(userCode, request);
        // code can be found - try again
      } catch (InvalidGrantException e) {
        // Good, it doesn't exist yet.
        break;
      } catch (ServerException e) {
        logger.message("Could not query CTS, assume duplicate to be safe", e);
      }
      userCode = null;
    }

    if (i == 10) {
      throw new ServerException("Could not generate a unique user code");
    }

    long expiryTime =
        System.currentTimeMillis() + (1000 * providerSettings.getDeviceCodeLifetime());
    final DeviceCode code =
        new DeviceCode(
            deviceCode,
            userCode,
            clientId,
            nonce,
            responseType,
            state,
            acrValues,
            prompt,
            uiLocales,
            loginHint,
            maxAge,
            claims,
            expiryTime,
            scope,
            realmNormaliser.normalise(request.<String>getParameter(REALM)),
            codeChallenge,
            codeChallengeMethod);

    // Store in CTS
    try {
      tokenStore.create(code);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"CREATED_DEVICE_CODE", code.toString()};
        auditLogger.logAccessMessage("CREATED_DEVICE_CODE", obs, null);
      }
    } catch (CoreTokenException e) {
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_CREATE_DEVICE_CODE", code.toString()};
        auditLogger.logErrorMessage("FAILED_CREATE_DEVICE_CODE", obs, null);
      }
      logger.error("Unable to create device code " + code, e);
      throw new ServerException("Could not create token in CTS");
    }

    request.setToken(DeviceCode.class, code);

    return code;
  }
  /** {@inheritDoc} */
  public AuthorizationCode createAuthorizationCode(
      Set<String> scope,
      ResourceOwner resourceOwner,
      String clientId,
      String redirectUri,
      String nonce,
      OAuth2Request request,
      String codeChallenge,
      String codeChallengeMethod)
      throws ServerException, NotFoundException {

    logger.message("DefaultOAuthTokenStoreImpl::Creating Authorization code");

    OpenIdConnectClientRegistration clientRegistration = getClientRegistration(clientId, request);

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
    final String code = UUID.randomUUID().toString();

    long expiryTime = 0;
    if (clientRegistration == null) {
      expiryTime = providerSettings.getAuthorizationCodeLifetime() + System.currentTimeMillis();
    } else {
      expiryTime =
          clientRegistration.getAuthorizationCodeLifeTime(providerSettings)
              + System.currentTimeMillis();
    }

    final String ssoTokenId = getSsoTokenId(request);

    final OpenAMAuthorizationCode authorizationCode =
        new OpenAMAuthorizationCode(
            code,
            resourceOwner.getId(),
            clientId,
            redirectUri,
            scope,
            getClaimsFromRequest(request),
            expiryTime,
            nonce,
            realmNormaliser.normalise(request.<String>getParameter(REALM)),
            getAuthModulesFromSSOToken(request),
            getAuthenticationContextClassReferenceFromRequest(request),
            ssoTokenId,
            codeChallenge,
            codeChallengeMethod);

    // Store in CTS
    try {
      tokenStore.create(authorizationCode);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"CREATED_AUTHORIZATION_CODE", authorizationCode.toString()};
        auditLogger.logAccessMessage("CREATED_AUTHORIZATION_CODE", obs, null);
      }
    } catch (CoreTokenException e) {
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_CREATE_AUTHORIZATION_CODE", authorizationCode.toString()};
        auditLogger.logErrorMessage("FAILED_CREATE_AUTHORIZATION_CODE", obs, null);
      }
      logger.error("Unable to create authorization code " + authorizationCode.getTokenInfo(), e);
      throw new ServerException("Could not create token in CTS");
    }

    request.setToken(AuthorizationCode.class, authorizationCode);

    return authorizationCode;
  }
  /** {@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;
  }
  /** {@inheritDoc} */
  public AccessToken createAccessToken(
      String grantType,
      String accessTokenType,
      String authorizationCode,
      String resourceOwnerId,
      String clientId,
      String redirectUri,
      Set<String> scope,
      RefreshToken refreshToken,
      String nonce,
      String claims,
      OAuth2Request request)
      throws ServerException, NotFoundException {

    OpenIdConnectClientRegistration clientRegistration = getClientRegistration(clientId, request);

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
    final String id = UUID.randomUUID().toString();
    final String auditId = UUID.randomUUID().toString();

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

    long expiryTime = 0;
    if (clientRegistration == null) {
      expiryTime = providerSettings.getAccessTokenLifetime() + System.currentTimeMillis();
    } else {
      expiryTime =
          clientRegistration.getAccessTokenLifeTime(providerSettings) + System.currentTimeMillis();
    }

    final AccessToken accessToken;
    if (refreshToken == null) {
      accessToken =
          new OpenAMAccessToken(
              id,
              authorizationCode,
              resourceOwnerId,
              clientId,
              redirectUri,
              scope,
              expiryTime,
              null,
              OAuth2Constants.Token.OAUTH_ACCESS_TOKEN,
              grantType,
              nonce,
              realm,
              claims,
              auditId);
    } else {
      accessToken =
          new OpenAMAccessToken(
              id,
              authorizationCode,
              resourceOwnerId,
              clientId,
              redirectUri,
              scope,
              expiryTime,
              refreshToken.getTokenId(),
              OAuth2Constants.Token.OAUTH_ACCESS_TOKEN,
              grantType,
              nonce,
              realm,
              claims,
              auditId);
    }
    try {
      tokenStore.create(accessToken);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"CREATED_TOKEN", accessToken.toString()};
        auditLogger.logAccessMessage("CREATED_TOKEN", obs, null);
      }
    } catch (CoreTokenException e) {
      logger.error("Could not create token in CTS: " + e.getMessage());
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_CREATE_TOKEN", accessToken.toString()};
        auditLogger.logErrorMessage("FAILED_CREATE_TOKEN", obs, null);
      }
      throw new ServerException("Could not create token in CTS: " + e.getMessage());
    }
    request.setToken(AccessToken.class, accessToken);
    return accessToken;
  }
  /** {@inheritDoc} */
  public RefreshToken createRefreshToken(
      String grantType,
      String clientId,
      String resourceOwnerId,
      String redirectUri,
      Set<String> scope,
      OAuth2Request request)
      throws ServerException, NotFoundException {

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

    logger.message("Create refresh token");

    OpenIdConnectClientRegistration clientRegistration = getClientRegistration(clientId, request);

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);

    final String id = UUID.randomUUID().toString();
    final String auditId = UUID.randomUUID().toString();

    final long lifeTime;
    if (clientRegistration == null) {
      lifeTime = providerSettings.getRefreshTokenLifetime();
    } else {
      lifeTime = clientRegistration.getRefreshTokenLifeTime(providerSettings);
    }

    long expiryTime = lifeTime < 0 ? -1 : lifeTime + System.currentTimeMillis();

    AuthorizationCode token = request.getToken(AuthorizationCode.class);
    String authModules = null;
    String acr = null;
    if (token != null) {
      authModules = token.getAuthModules();
      acr = token.getAuthenticationContextClassReference();
    }

    RefreshToken currentRefreshToken = request.getToken(RefreshToken.class);
    if (currentRefreshToken != null) {
      authModules = currentRefreshToken.getAuthModules();
      acr = currentRefreshToken.getAuthenticationContextClassReference();
    }

    RefreshToken refreshToken =
        new OpenAMRefreshToken(
            id,
            resourceOwnerId,
            clientId,
            redirectUri,
            scope,
            expiryTime,
            OAuth2Constants.Bearer.BEARER,
            OAuth2Constants.Token.OAUTH_REFRESH_TOKEN,
            grantType,
            realm,
            authModules,
            acr,
            auditId);

    try {
      tokenStore.create(refreshToken);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"CREATED_REFRESH_TOKEN", refreshToken.toString()};
        auditLogger.logAccessMessage("CREATED_REFRESH_TOKEN", obs, null);
      }
    } catch (CoreTokenException e) {
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_CREATE_REFRESH_TOKEN", refreshToken.toString()};
        auditLogger.logErrorMessage("FAILED_CREATE_REFRESH_TOKEN", obs, null);
      }
      logger.error("Unable to create refresh token: " + refreshToken.getTokenInfo(), e);
      throw new ServerException("Could not create token in CTS: " + e.getMessage());
    }

    request.setToken(RefreshToken.class, refreshToken);

    return refreshToken;
  }