class ClientAuthenticator {

  public Optional<Client> authenticate(Principal principal, boolean createMissingClient) {
    // Try to retrieve clients based on the client name and SPIFFE ID
    Optional<String> possibleClientName = getClientName(principal);
    Optional<URI> possibleClientSpiffeId = getSpiffeId(principal);

    Optional<Client> possibleClientFromName = possibleClientName.flatMap((name) -> {
      if (clientAuthConfig.typeConfig().useCommonName()) {
        return clientDAOReadOnly.getClientByName(name);
      } else {
        return Optional.empty();
      }
    });

    Optional<Client> possibleClientFromSpiffeId = possibleClientSpiffeId.flatMap((spiffeId) -> {
      if (clientAuthConfig.typeConfig().useSpiffeId()) {
        return clientDAOReadOnly.getClientBySpiffeId(spiffeId);
      } else {
        return Optional.empty();
      }
    });

    // If the name and SPIFFE id both defined a client, make sure that they defined the same
    // client! (Note that if retrieving clients by common name or SPIFFE ID is disabled, this
    // check will not reject a certificate with a mismatched CN and SPIFFE URI.)
    if (possibleClientFromName.isPresent() && possibleClientFromSpiffeId.isPresent()) {
      if (!possibleClientFromName.get().equals(possibleClientFromSpiffeId.get())) {
        throw new NotAuthorizedException(format(
            "Input principal's CN and SPIFFE ID correspond to different clients! (cn = %s, spiffe = %s)",
            possibleClientFromName.get().getName(), possibleClientFromSpiffeId.get().getName()));
      }
    } else if (possibleClientFromName.isEmpty() && possibleClientFromSpiffeId.isEmpty()) {
      // Create missing clients if configured to do so (client name must be present)
      return handleMissingClient(createMissingClient, possibleClientName.orElse(""),
          possibleClientSpiffeId);
    }

    // Retrieve whichever of the clients is set (if both are present, the earlier check guarantees
    // that they contain the same client).
    Client client = possibleClientFromName
        .or(() -> possibleClientFromSpiffeId)
        .orElseThrow(() -> new IllegalStateException(
            "Unable to identify client, and fallback code in server did not handle this case"));

    // Record that this client has been retrieved
    clientDAOReadWrite.sawClient(client, principal);
    if (client.isEnabled()) {
      return Optional.of(client);
    } else {
      logger.warn("Client {} authenticated but disabled via DB", client);
      return Optional.empty();
    }
  }

  public  ClientAuthenticator(
      ClientDAO clientDAOReadWrite,
      ClientDAO clientDAOReadOnly,
      ClientAuthConfig clientAuthConfig);

}