private Future<AuthorizationResult> authorizeRequest(HttpServerRequest request) {
        String apiKey = request.headers().get(HEADER_API_KEY);
        String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
        log.debug("Authorization header: {}", authorization);

        if (apiKey == null && authorization == null) {
            return Future.failedFuture(new HttpException(HttpStatus.UNAUTHORIZED, "At least API-KEY or Authorization header must be provided"));
        }

        if (apiKey != null && authorization == null) {
            return apiKeyStore.getApiKeyData(apiKey)
                    .map(apiKeyData -> new AuthorizationResult(apiKeyData, null));
        }

        if (apiKey == null) {
            return tokenValidator.extractClaims(authorization)
                    .compose(extractedClaims -> Future.succeededFuture(new AuthorizationResult(new ApiKeyData(), extractedClaims)),
                            error -> {
                                log.error("Can't extract claims from authorization header", error);
                                return Future.failedFuture(new HttpException(HttpStatus.UNAUTHORIZED, "Bad Authorization header"));
                            });
        }

        // see https://github.com/epam/ai-dial-core/issues/675
        if ("Bearer".equalsIgnoreCase(authorization.trim())) {
            return apiKeyStore.getApiKeyData(apiKey)
                    .map(apiKeyData -> new AuthorizationResult(apiKeyData, null));
        }

        if (apiKey.equals(AccessTokenValidator.extractTokenFromHeader(authorization))) {
            // we don't know exactly what kind of credentials a client provided to us.
            // we try if it's access token the first and then API key
            return tokenValidator.extractClaims(authorization)
                    .compose(claims -> Future.succeededFuture(new AuthorizationResult(new ApiKeyData(), claims)),
                            error -> apiKeyStore.getApiKeyData(apiKey).map(apiKeyData -> new AuthorizationResult(apiKeyData, null)));
        }
        // interceptor case
        return apiKeyStore.getApiKeyData(apiKey)
                .compose(apiKeyData -> {
                    if (apiKeyData.isInterceptor()) {
                        return Future.succeededFuture(new AuthorizationResult(apiKeyData, null));
                    } else {
                        return Future.failedFuture(new HttpException(HttpStatus.BAD_REQUEST, "Either API-KEY or Authorization header must be provided but not both"));
                    }
                });

    }
The method authorizes HTTP request by user access token or (and) API key.
<p>
    There are four possible use cases:
    <ol>
    <li>Both API key and access token are missed</li>
    <li>Both API key and access token are provided</li>
    <li>Just API key is provided</li>
    <li>Just access token is provided</li>
    </ol>
    The 2nd use case has two sub-cases:
    <ol>
    <li>API key is equal to access token. The credentials could be either access token or API key</li>
    <li>API key is not equal to access token. It could be per-request API key sent by an interceptor</li>
    </ol>
</p>

@param request HTTP request
@return the future of {@link AuthorizationResult}
server/src/main/java/com/epam/aidial/core/server/Proxy.java