diff --git a/.gitmodules b/.gitmodules
index 0a66031de8d1..0d9ec97c4087 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -372,3 +372,6 @@
[submodule "contrib/numactl"]
path = contrib/numactl
url = https://github.com/ClickHouse/numactl.git
+[submodule "contrib/jwt-cpp"]
+ path = contrib/jwt-cpp
+ url = https://github.com/Thalhammer/jwt-cpp.git
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
index dc2ad2a31501..9bee7c5268f0 100644
--- a/contrib/CMakeLists.txt
+++ b/contrib/CMakeLists.txt
@@ -80,7 +80,6 @@ add_contrib (openldap-cmake openldap)
add_contrib (grpc-cmake grpc)
add_contrib (msgpack-c-cmake msgpack-c)
add_contrib (libarchive-cmake libarchive)
-
add_contrib (corrosion-cmake corrosion)
if (ENABLE_FUZZING)
diff --git a/contrib/jwt-cpp-cmake/CMakeLists.txt b/contrib/jwt-cpp-cmake/CMakeLists.txt
new file mode 100644
index 000000000000..606c13d29de2
--- /dev/null
+++ b/contrib/jwt-cpp-cmake/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(ENABLE_JWT_CPP_DEFAULT ON)
+
+option(ENABLE_JWT_CPP "Enable jwt-cpp library" ${ENABLE_JWT_CPP_DEFAULT})
+
+if (NOT ENABLE_JWT_CPP)
+ message(STATUS "Not using jwt-cpp")
+ return()
+endif()
+
+if(ENABLE_JWT_CPP)
+ if(NOT TARGET OpenSSL::Crypto)
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use jwt-cpp without OpenSSL")
+ endif()
+endif()
+
+set (JWT_CPP_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/jwt-cpp/include")
+
+add_library (_jwt-cpp INTERFACE)
+target_include_directories(_jwt-cpp SYSTEM BEFORE INTERFACE ${JWT_CPP_INCLUDE_DIR})
+add_library(ch_contrib::jwt-cpp ALIAS _jwt-cpp)
\ No newline at end of file
diff --git a/docker/test/fasttest/run.sh b/docker/test/fasttest/run.sh
index 9920326b11ce..4b6c2add61f8 100755
--- a/docker/test/fasttest/run.sh
+++ b/docker/test/fasttest/run.sh
@@ -158,6 +158,7 @@ function clone_submodules
contrib/libfiu
contrib/incbin
contrib/yaml-cpp
+ contrib/jwt-cpp
)
git submodule sync
diff --git a/docs/en/operations/external-authenticators/index.md b/docs/en/operations/external-authenticators/index.md
index f644613641cc..2730389e1177 100644
--- a/docs/en/operations/external-authenticators/index.md
+++ b/docs/en/operations/external-authenticators/index.md
@@ -16,4 +16,5 @@ The following external authenticators and directories are supported:
- [LDAP](./ldap.md#external-authenticators-ldap) [Authenticator](./ldap.md#ldap-external-authenticator) and [Directory](./ldap.md#ldap-external-user-directory)
- Kerberos [Authenticator](./kerberos.md#external-authenticators-kerberos)
- [SSL X.509 authentication](./ssl-x509.md#ssl-external-authentication)
-- HTTP [Authenticator](./http.md)
\ No newline at end of file
+- HTTP [Authenticator](./http.md)
+- JWT [Authenticator](./jwt.md)
diff --git a/docs/en/operations/external-authenticators/jwt.md b/docs/en/operations/external-authenticators/jwt.md
new file mode 100644
index 000000000000..93020bc15b7d
--- /dev/null
+++ b/docs/en/operations/external-authenticators/jwt.md
@@ -0,0 +1,204 @@
+---
+slug: /en/operations/external-authenticators/jwt
+---
+# JWT
+import SelfManaged from '@site/docs/en/_snippets/_self_managed_only_no_roadmap.md';
+
+
+
+Existing and properly configured ClickHouse users can be authenticated via JWT.
+
+Currently, JWT can only be used as an external authenticator for existing users, which are defined in `users.xml` or in local access control paths.
+The username will be extracted from the JWT after validating the token expiration and against the signature. Signature can be validated by:
+- static public key
+- static JWKS
+- received from the JWKS servers
+
+It is mandatory for a JWT tot indicate the name of the ClickHouse user under `"sub"` claim, otherwise it will not be accepted.
+
+A JWT may additionally be verified by checking the JWT payload.
+In this case, the occurrence of specified claims from the user settings in the JWT payload is checked.
+See [Enabling JWT authentication in `users.xml`](#enabling-jwt-auth-in-users-xml)
+
+To use JWT authentication, JWT validators must be configured in ClickHouse config.
+
+
+## Enabling JWT validators in ClickHouse {#enabling-jwt-validators-in-clickhouse}
+
+To enable JWT validators, add `jwt_validators` section in `config.xml`. This section may contain several JWT verifiers, minimum is 1.
+
+### Verifying JWT signature using static key {$verifying-jwt-signature-using-static-key}
+
+**Example**
+```xml
+
+
+
+
+ HS256
+ my_static_secret
+
+
+
+```
+
+#### Parameters:
+
+- `algo` - Algorithm for validate signature. Supported:
+
+ | HMAC | RSA | ECDSA | PSS | EdDSA |
+ |-------| ----- | ------ | ----- | ------- |
+ | HS256 | RS256 | ES256 | PS256 | Ed25519 |
+ | HS384 | RS384 | ES384 | PS384 | Ed448 |
+ | HS512 | RS512 | ES512 | PS512 | |
+ | | | ES256K | | |
+ Also support None.
+- `static_key` - key for symmetric algorithms. Mandatory for `HS*` family algorithms.
+- `static_key_in_base64` - indicates if the `static_key` key is base64-encoded. Optional, default: `False`.
+- `public_key` - public key for asymmetric algorithms. Mandatory except for `HS*` family algorithms and `None`.
+- `private_key` - private key for asymmetric algorithms. Optional.
+- `public_key_password` - public key password. Optional.
+- `private_key_password` - private key password. Optional.
+
+### Verifying JWT signature using static JWKS {$verifying-jwt-signature-using-static-jwks}
+
+:::note
+Only RS* family algorithms are supported!
+:::
+
+**Example**
+```xml
+
+
+
+
+ {"keys": [{"kty": "RSA", "alg": "RS256", "kid": "mykid", "n": "_public_key_mod_", "e": "AQAB"}]}
+
+
+
+```
+
+#### Parameters:
+- `static_jwks` - content of JWKS in json
+- `static_jwks_file` - path to file with JWKS
+
+:::note
+Only one of `static_jwks` or `static_jwks_file` keys must be present in one verifier
+:::
+
+### Verifying JWT signature using JWKS servers {$verifying-jwt-signature-using-static-jwks}
+
+**Example**
+```xml
+
+
+
+
+ http://localhost:8000/.well-known/jwks.json
+ 1000
+ 1000
+ 1000
+ 3
+ 50
+ 1000
+ 300000
+
+
+
+```
+
+#### Parameters:
+
+- `uri` - JWKS endpoint. Mandatory.
+- `refresh_ms` - Period for resend request for refreshing JWKS. Optional, default: 300000.
+
+Timeouts in milliseconds on the socket used for communicating with the server (optional):
+- `connection_timeout_ms` - Default: 1000.
+- `receive_timeout_ms` - Default: 1000.
+- `send_timeout_ms` - Default: 1000.
+
+Retry parameters (optional):
+- `max_tries` - The maximum number of attempts to make an authentication request. Default: 3.
+- `retry_initial_backoff_ms` - The backoff initial interval on retry. Default: 50.
+- `retry_max_backoff_ms` - The maximum backoff interval. Default: 1000.
+
+### Enabling JWT authentication in `users.xml` {#enabling-jwt-auth-in-users-xml}
+
+In order to enable JWT authentication for the user, specify `jwt` section instead of `password` or other similar sections in the user definition.
+
+Parameters:
+- `claims` - An optional string containing a json object that should be contained in the token payload.
+
+Example (goes into `users.xml`):
+```xml
+
+
+
+
+
+ {"resource_access":{"account": {"roles": ["view-profile"]}}}
+
+
+
+```
+
+Here, the JWT payload must contain `["view-profile"]` on path `resource_access.account.roles`, otherwise authentication will not succeed even with a valid JWT.
+
+```
+{
+...
+ "resource_access": {
+ "account": {
+ "roles": ["view-profile"]
+ }
+ },
+...
+}
+```
+
+:::note
+JWT authentication cannot be used together with any other authentication method. The presence of any other sections like `password` alongside `jwt` will force ClickHouse to shut down.
+:::
+
+### Enabling JWT authentication using SQL {#enabling-jwt-auth-using-sql}
+
+When [SQL-driven Access Control and Account Management](/docs/en/guides/sre/user-management/index.md#access-control) is enabled in ClickHouse, users identified by JWT authentication can also be created using SQL statements.
+
+```sql
+CREATE USER my_user IDENTIFIED WITH jwt CLAIMS '{"resource_access":{"account": {"roles": ["view-profile"]}}}'
+```
+
+Or without additional JWT payload checks:
+
+```sql
+CREATE USER my_user IDENTIFIED WITH jwt
+```
+
+## JWT authentication examples {#jwt-authentication-examples}
+
+#### Console client
+
+```
+clickhouse-client -jwt
+```
+
+#### HTTP requests
+
+```
+curl 'http://localhost:8080/?' \
+ -H 'Authorization: Bearer ' \
+ -H 'Content type: text/plain;charset=UTF-8' \
+ --data-raw 'SELECT current_user()'
+```
+:::note
+ClickHouse will look for a JWT token in (by priority):
+1. `X-ClickHouse-JWT-Token` header.
+2. `Authorization` header.
+3. `token` request parameter. In this case, the "Bearer" prefix should not exist.
+:::
+
+### Passing session settings {#passing-session-settings}
+
+If `settings_key` exists in the `jwt_validators` section or exists in the verifier section and the payload contains a sub-object of that `settings_key`, ClickHouse will attempt to parse its key:value pairs as string values and set them as session settings for the currently authenticated user. If parsing fails, the JWT payload will be ignored.
+
+The `settings_key` in the verifier section takes precedence over the `settings_key` from the `jwt_validators` section. If `settings_key` in the verifier section does not exist, the `settings_key` from the `jwt_validators` section will be used.
diff --git a/docs/ru/operations/external-authenticators/jwt.md b/docs/ru/operations/external-authenticators/jwt.md
new file mode 100644
index 000000000000..6ed63a351821
--- /dev/null
+++ b/docs/ru/operations/external-authenticators/jwt.md
@@ -0,0 +1,208 @@
+---
+slug: /ru/operations/external-authenticators/jwt
+---
+# JWT
+import SelfManaged from '@site/docs/en/_snippets/_self_managed_only_no_roadmap.md';
+
+
+
+Существующие и корректно настроенные пользователи ClickHouse могут быть аутентифицированы с помощью JWT.
+
+Сейчас JWT работает только как внешний аутентификатор для уже существующих пользователей.
+Имя пользователя будет извлечено из JWT после проверки срока действия токена и подписи.
+Подпись может быть проверена с помощью:
+- статического (указанного в конфигурации) открытого ключа,
+- статического (указанного в конфигурации) JWKS или файла, содержащего JWKS,
+- полученного от JWKS-сервера.
+
+Имя пользователя ClickHouse должно быть обязательно указано в поле (claim) `"sub"`, в противном случае токен не будет принят.
+
+Можно также дополнительно проверять JWT на наличие определённого содержимого (payload).
+В этом случае проверяется наличие указанных полей (claims) из настроек пользователя в содержимом JWT.
+Смотри [Настройка JWT аутентификации пользователя через `users.xml`](#enabling-jwt-auth-in-users-xml) и [Настройка JWT аутентификации пользователя через SQL](#enabling-jwt-auth-using-sql)
+
+
+## Настройка JWT валидаторов {#enabling-jwt-validators}
+
+Для аутентификации с помощью JWT сконфигурировать как минимум один валидатор.
+Это делается в секции `jwt_validators` в `config.xml`. Эта секция может содержать несколько JWT-верификаторов.
+
+### Проверка JWT с помощью статического ключа {$verifying-jwt-signature-using-static-key}
+
+**Пример**
+```xml
+
+
+
+
+ HS256
+ my_static_secret
+
+
+
+```
+
+#### Параметры:
+
+- `algo` - Алгоритм для проверки подписи. Поддерживаемые алгоритмы:
+
+ | HMAC | RSA | ECDSA | PSS | EdDSA |
+ |-------| ----- | ------ | ----- | ------- |
+ | HS256 | RS256 | ES256 | PS256 | Ed25519 |
+ | HS384 | RS384 | ES384 | PS384 | Ed448 |
+ | HS512 | RS512 | ES512 | PS512 | |
+ | | | ES256K | | |
+ Можно не проверять подпись, указав `None` для этого параметра.
+- `static_key` - ключ симметричного алгоритма. Обязателен для алгоритмов семейства `HS*`.
+- `static_key_in_base64` - указывает, закодирован ли `static_key` в формате base64. Необязательный параметр, по умолчанию: `False`.
+- `public_key` - открытый ключ для асимметричных алгоритмов. Обязателен для всех алгоритмов, кроме семейства `HS*` и `None`.
+- `private_key` - закрытый ключ для асимметричных алгоритмов. Необязательный параметр.
+- `public_key_password` - пароль открытого ключа, необязательный параметр.
+- `private_key_password` - пароль закрытого ключа, необязательный параметр.
+
+### Проверка JWT с помощью статического JWKS {$verifying-jwt-signature-using-static-jwks}
+
+:::note
+Проверка с помощью JWKS невозможна для алгоритмов семейства `HS*`.
+:::
+
+**Пример**
+```xml
+
+
+
+
+ {"keys": [{"kty": "RSA", "alg": "RS256", "kid": "mykid", "n": "_public_key_mod_", "e": "AQAB"}]}
+
+
+
+```
+
+#### Параметры:
+- `static_jwks` - содержимое JWKS в виде JSON.
+- `static_jwks_file` - путь к файлу, содержащему JWKS.
+
+:::note
+Должен быть указан один и только один из этих двух параметров.
+:::
+
+### Проверка JWT с помощью JWKS сервера {$verifying-jwt-signature-using-static-jwks}
+
+:::note
+Проверка с помощью JWKS невозможна для алгоритмов семейства `HS*`.
+:::
+
+**Пример**
+```xml
+
+
+
+
+ http://localhost:8000/.well-known/jwks.json
+ 1000
+ 1000
+ 1000
+ 3
+ 50
+ 1000
+ 300000
+
+
+
+```
+
+#### Параметры:
+
+- `uri` - адрес, по которому доступен JWKS. Обязательный параметр.
+- `refresh_ms` - Период обновления JWKS. Необязательный параметр, по умолчанию: 300000.
+
+Таймауты в миллисекундах для сокета, используемого для связи с сервером (необязательные параметры):
+- `connection_timeout_ms` - По умолчанию: 1000.
+- `receive_timeout_ms` - По умолчанию: 1000.
+- `send_timeout_ms` - По умолчанию: 1000.
+
+Настройка повторных попыток (необязательные параметры):
+- `max_tries` - Максимальное количество попыток аутентификации. По умолчанию: 3.
+- `retry_initial_backoff_ms` - Стартовый интервал между повторными попытками (backoff). По умолчанию: 50.
+- `retry_max_backoff_ms` - Максимальный интервал между повторными попытками (backoff). По умолчанию: 1000.
+
+### Настройка JWT аутентификации пользователя в `users.xml` {#enabling-jwt-auth-in-users-xml}
+
+Чтобы включить аутентификацию с помощью JWT для пользователя, укажите секцию `jwt` вместо секции `password` и аналогичных секций.
+
+**Пример (`users.xml`)**
+```xml
+
+
+
+
+
+ {"resource_access":{"account": {"roles": ["view-profile"]}}}
+
+
+
+```
+
+#### Параметры
+- `claims` - строка, содержащая JSON, который должен присутствовать в содержимом токена.
+
+В данном случае содержимое JWT должно содержать значение ["view-profile"] по пути `resource_access.account.roles`,
+в противном случае аутентификация не будет успешной, даже если в остальном JWT верный.
+
+**Пример payload**
+```json
+{
+ "sub": "my_user",
+ "resource_access": {
+ "account": {
+ "roles": ["view-profile"]
+ }
+ },
+}
+```
+
+:::note
+Аутентификация JWT не может использоваться вместе с другими методами аутентификации. Наличие любых других секций, таких как `password`, наряду с секцией `jwt` приведет к аварийному завершению работы.
+:::
+
+### Настройка JWT аутентификации пользователя через SQL {#enabling-jwt-auth-using-sql}
+
+When [SQL-driven Access Control and Account Management](/docs/en/guides/sre/user-management/index.md#access-control) is enabled in ClickHouse, users identified by JWT authentication can also be created using SQL statements.
+
+В случае если в ClickHouse включено управление доступом через SQL ([SQL-driven Access Control and Account Management](/docs/en/guides/sre/user-management/index.md#access-control)),
+можно создать пользователя с аутентификацией через JWT с помощью SQL-запросов.
+
+**Без проверки содержимого JWT**
+```sql
+CREATE USER my_user IDENTIFIED WITH jwt
+```
+
+**С проверкой содержимого JWT**
+```sql
+CREATE USER my_user IDENTIFIED WITH jwt CLAIMS '{"resource_access":{"account": {"roles": ["view-profile"]}}}'
+```
+
+## Примеры аутентификации {#jwt-authentication-examples}
+
+#### `clickhouse-client`
+
+```
+clickhouse-client -jwt
+```
+
+#### HTTP
+
+```
+curl 'http://localhost:8080/?' \
+ -H 'Authorization: Bearer ' \
+ -H 'Content type: text/plain;charset=UTF-8' \
+ --data-raw 'SELECT current_user()'
+```
+:::note
+ClickHouse ищет токен в следующих местах (по порядку):
+1. Заголовок `X-ClickHouse-JWT-Token`.
+2. Стандартный заголовок `Authorization`.
+3. Параметр `token`. В этом случае параметр не должен содержать префикс `Bearer`.
+:::
+
+### Передача параметров сессии {#passing-session-settings}
diff --git a/src/Access/AccessControl.cpp b/src/Access/AccessControl.cpp
index ec513f0692d3..700d1d10b89d 100644
--- a/src/Access/AccessControl.cpp
+++ b/src/Access/AccessControl.cpp
@@ -689,6 +689,11 @@ bool AccessControl::isNoPasswordAllowed() const
return allow_no_password;
}
+bool AccessControl::isJWTEnabled() const
+{
+ return external_authenticators->isJWTAllowed();
+}
+
void AccessControl::setPlaintextPasswordAllowed(bool allow_plaintext_password_)
{
allow_plaintext_password = allow_plaintext_password_;
diff --git a/src/Access/AccessControl.h b/src/Access/AccessControl.h
index 0c3bb9352f0a..c0820166494b 100644
--- a/src/Access/AccessControl.h
+++ b/src/Access/AccessControl.h
@@ -148,6 +148,8 @@ class AccessControl : public MultipleAccessStorage
void setNoPasswordAllowed(bool allow_no_password_);
bool isNoPasswordAllowed() const;
+ bool isJWTEnabled() const;
+
/// Allows users with plaintext password (by default it's allowed).
void setPlaintextPasswordAllowed(bool allow_plaintext_password_);
bool isPlaintextPasswordAllowed() const;
diff --git a/src/Access/Authentication.cpp b/src/Access/Authentication.cpp
index 6b9a6e05cf67..42af732bcb01 100644
--- a/src/Access/Authentication.cpp
+++ b/src/Access/Authentication.cpp
@@ -292,6 +292,11 @@ bool Authentication::areCredentialsValid(
}
#endif
+ if (const auto * jwt_credentials = typeid_cast(&credentials))
+ {
+ return external_authenticators.checkJWTCredentials(authentication_method.getJWTClaims(), *jwt_credentials, settings);
+ }
+
if ([[maybe_unused]] const auto * always_allow_credentials = typeid_cast(&credentials))
return true;
diff --git a/src/Access/AuthenticationData.cpp b/src/Access/AuthenticationData.cpp
index bf3d45d1178f..3e7d4d0fff91 100644
--- a/src/Access/AuthenticationData.cpp
+++ b/src/Access/AuthenticationData.cpp
@@ -14,7 +14,9 @@
#include
#include
#include
+#include
+#include "Access/Common/AuthenticationType.h"
#include
#include "config.h"
@@ -335,7 +337,10 @@ std::shared_ptr AuthenticationData::toAST() const
}
case AuthenticationType::JWT:
{
- throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
+ const auto & claims = getJWTClaims();
+ if (!claims.empty())
+ node->children.push_back(std::make_shared(claims));
+ break;
}
case AuthenticationType::KERBEROS:
{
@@ -544,6 +549,20 @@ AuthenticationData AuthenticationData::fromAST(const ASTAuthenticationData & que
auth_data.setHTTPAuthenticationServerName(server);
auth_data.setHTTPAuthenticationScheme(scheme);
}
+ else if (query.type == AuthenticationType::JWT)
+ {
+ if (!args.empty())
+ {
+ String value = checkAndGetLiteralArgument(args[0], "claims");
+ picojson::value json_obj;
+ auto error = picojson::parse(json_obj, value);
+ if (!error.empty())
+ throw Exception(ErrorCodes::BAD_ARGUMENTS, "Bad JWT claims: {}", error);
+ if (!json_obj.is())
+ throw Exception(ErrorCodes::BAD_ARGUMENTS, "Bad JWT claims: is not an object");
+ auth_data.setJWTClaims(value);
+ }
+ }
else
{
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unexpected ASTAuthenticationData structure");
diff --git a/src/Access/AuthenticationData.h b/src/Access/AuthenticationData.h
index a0c100264f8a..7f3ef9e79217 100644
--- a/src/Access/AuthenticationData.h
+++ b/src/Access/AuthenticationData.h
@@ -74,6 +74,9 @@ class AuthenticationData
const String & getHTTPAuthenticationServerName() const { return http_auth_server_name; }
void setHTTPAuthenticationServerName(const String & name) { http_auth_server_name = name; }
+ const String & getJWTClaims() const { return jwt_claims; }
+ void setJWTClaims(const String & jwt_claims_) { jwt_claims = jwt_claims_; }
+
friend bool operator ==(const AuthenticationData & lhs, const AuthenticationData & rhs);
friend bool operator !=(const AuthenticationData & lhs, const AuthenticationData & rhs) { return !(lhs == rhs); }
@@ -106,6 +109,7 @@ class AuthenticationData
/// HTTP authentication properties
String http_auth_server_name;
HTTPAuthenticationScheme http_auth_scheme = HTTPAuthenticationScheme::BASIC;
+ String jwt_claims;
};
}
diff --git a/src/Access/Credentials.cpp b/src/Access/Credentials.cpp
index f01700b6e461..86482fe5decb 100644
--- a/src/Access/Credentials.cpp
+++ b/src/Access/Credentials.cpp
@@ -1,6 +1,9 @@
#include
#include
#include
+#include
+
+#include
namespace DB
{
@@ -8,6 +11,7 @@ namespace DB
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
+ extern const int AUTHENTICATION_FAILED;
}
Credentials::Credentials(const String & user_name_)
@@ -97,4 +101,26 @@ const String & BasicCredentials::getPassword() const
return password;
}
+namespace
+{
+String extractSubjectFromToken(const String & token)
+{
+ try
+ {
+ auto decoded_jwt = jwt::decode(token);
+ return decoded_jwt.get_subject();
+ }
+ catch (...)
+ {
+ throw Exception(ErrorCodes::AUTHENTICATION_FAILED, "Failed to validate jwt");
+ }
+}
+}
+
+JWTCredentials::JWTCredentials(const String & token_)
+ : Credentials(extractSubjectFromToken(token_))
+ , token(token_)
+ {
+ is_ready = !user_name.empty();
+ }
}
diff --git a/src/Access/Credentials.h b/src/Access/Credentials.h
index 5f6b0269eef9..3f84d5bdf2bf 100644
--- a/src/Access/Credentials.h
+++ b/src/Access/Credentials.h
@@ -123,4 +123,20 @@ class SshCredentials : public Credentials
};
#endif
+class JWTCredentials: public Credentials
+{
+public:
+ explicit JWTCredentials(const String & token_);
+ const String & getToken() const
+ {
+ if (!isReady())
+ {
+ throwNotReady();
+ }
+ return token;
+ }
+private:
+ String token;
+};
+
}
diff --git a/src/Access/ExternalAuthenticators.cpp b/src/Access/ExternalAuthenticators.cpp
index 77812ac5eb5d..7b9b0f0e6592 100644
--- a/src/Access/ExternalAuthenticators.cpp
+++ b/src/Access/ExternalAuthenticators.cpp
@@ -2,14 +2,21 @@
#include
#include
#include
+#include "Common/Logger.h"
+#include "Common/logger_useful.h"
#include
#include
#include
#include
+#include "Access/AccessControl.h"
+#include "Access/Credentials.h"
+#include "Access/JWTValidator.h"
#include
#include
+#include