|
| 1 | +--- |
| 2 | +slug: /en/operations/external-authenticators/oauth |
| 3 | +title: "Token-based authentication" |
| 4 | +--- |
| 5 | +import SelfManaged from '@site/docs/en/_snippets/_self_managed_only_no_roadmap.md'; |
| 6 | + |
| 7 | +<SelfManaged /> |
| 8 | + |
| 9 | +ClickHouse users can be authenticated using tokens. This works in two ways: |
| 10 | + |
| 11 | +- Existing users (defined in `users.xml` or in local access control paths) can be authenticated with a token if this user can be `IDENTIFIED WITH jwt`. |
| 12 | +- Use the information from the token or from an external Identity Provider (IdP) as a source of user definitions and allow locally undefined users to be authenticated with a valid token. |
| 13 | + |
| 14 | +Although not all tokens are JWTs, under the hood both ways are treated as the same authentication method to maintain better compatibility. |
| 15 | + |
| 16 | +# Token Processors |
| 17 | + |
| 18 | +## Configuration |
| 19 | +To use token-based authentication, add `token_processors` section to `config.xml` and define at least one token processor in it. |
| 20 | +Its contents are different for different token processor types. |
| 21 | + |
| 22 | +**Common parameters** |
| 23 | +- `type` -- type of token processor. Supported values: "JWT", "Azure", "OpenID". Mandatory. Case-insensitive. |
| 24 | +- `token_cache_lifetime` -- maximum lifetime of cached token (in seconds). Optional, default: 3600. |
| 25 | +- `username_claim` -- name of claim (field) that will be treated as ClickHouse username. Optional, default: "sub". |
| 26 | +- `groups_claim` -- Name of claim (field) that contains list of groups user belongs to. This claim will be looked up in the token itself (in case token is a valid JWT, e.g. in Keycloak) or in response from `/userinfo`. Optional, default: "groups". |
| 27 | + |
| 28 | +For each type, there are additional specific parameters. |
| 29 | +If some parameters that are not required for current processor type are specified, they are ignored. |
| 30 | +If there are conflicting parameters (e.g `algo` is specified together with `jwks_uri`), an exception will be thrown. |
| 31 | + |
| 32 | +## JWT (JSON Web Token) |
| 33 | + |
| 34 | +JWT itself is a source of information about user. |
| 35 | +It is decoded locally and its integrity is verified using either static key or JWKS (JSON Web Key Set), either local or remote. |
| 36 | + |
| 37 | +`algo`, `static_jwks`/`static_jwks_file` and `jwks_uri` are defining different JWT processing workflows, and they cannot be specified together. |
| 38 | +### JWT with static key: |
| 39 | +```xml |
| 40 | +<clickhouse> |
| 41 | + <token_processors> |
| 42 | + <my_static_key_validator> |
| 43 | + <type>jwt</type> |
| 44 | + <algo>HS256</algo> |
| 45 | + <static_key>my_static_secret</static_key> |
| 46 | + </my_static_key_validator> |
| 47 | + </token_processors> |
| 48 | +</clickhouse> |
| 49 | +``` |
| 50 | +**Parameters:** |
| 51 | +- `algo` - Algorithm for validate signature. Mandatory. Supported values: |
| 52 | + |
| 53 | + | HMAC | RSA | ECDSA | PSS | EdDSA | |
| 54 | + |-------| ----- | ------ | ----- | ------- | |
| 55 | + | HS256 | RS256 | ES256 | PS256 | Ed25519 | |
| 56 | + | HS384 | RS384 | ES384 | PS384 | Ed448 | |
| 57 | + | HS512 | RS512 | ES512 | PS512 | | |
| 58 | + | | | ES256K | | | |
| 59 | + Also supports None (though not recommended). |
| 60 | +`claims` - A string containing a JSON object that should be contained in the token payload. If this parameter is defined, token without corresponding payload will be considered invalid. Optional. |
| 61 | +- `static_key` - key for symmetric algorithms. Mandatory for `HS*` family algorithms. |
| 62 | +- `static_key_in_base64` - indicates if the `static_key` key is base64-encoded. Optional, default: `False`. |
| 63 | +- `public_key` - public key for asymmetric algorithms. Mandatory except for `HS*` family algorithms and `None`. |
| 64 | +- `private_key` - private key for asymmetric algorithms. Optional. |
| 65 | +- `public_key_password` - public key password. Optional. |
| 66 | +- `private_key_password` - private key password. Optional. |
| 67 | + |
| 68 | +### JWT with static JWKS |
| 69 | +```xml |
| 70 | +<clickhouse> |
| 71 | + <token_processors> |
| 72 | + <my_static_jwks_validator> |
| 73 | + <type>jwt</type> |
| 74 | + <static_jwks>{"keys": [{"kty": "RSA", "alg": "RS256", "kid": "mykid", "n": "_public_key_mod_", "e": "AQAB"}]}</static_jwks> |
| 75 | + </my_static_jwks_validator> |
| 76 | + </token_processors> |
| 77 | +</clickhouse> |
| 78 | +``` |
| 79 | + |
| 80 | +**Parameters:** |
| 81 | + |
| 82 | +- `static_jwks` - content of JWKS in JSON |
| 83 | +- `static_jwks_file` - path to a file with JWKS |
| 84 | +- `claims` - A string containing a JSON object that should be contained in the token payload. If this parameter is defined, token without corresponding payload will be considered invalid. Optional. |
| 85 | +- `verifier_leeway` - Clock skew tolerance (seconds). Useful for handling small differences in system clocks between ClickHouse and the token issuer. Optional. |
| 86 | + |
| 87 | +:::note |
| 88 | +Only one of `static_jwks` or `static_jwks_file` keys must be present in one verifier |
| 89 | +::: |
| 90 | + |
| 91 | +:::note |
| 92 | +Only RS* family algorithms are supported! |
| 93 | +::: |
| 94 | + |
| 95 | +### JWT with remote JWKS |
| 96 | +```xml |
| 97 | +<clickhouse> |
| 98 | + <token_processors> |
| 99 | + <basic_auth_server> |
| 100 | + <type>jwt</type> |
| 101 | + <jwks_uri>http://localhost:8000/.well-known/jwks.json</jwks_uri> |
| 102 | + <jwks_cache_lifetime>3600</jwks_cache_lifetime> |
| 103 | + </basic_auth_server> |
| 104 | + </token_processors> |
| 105 | +</clickhouse> |
| 106 | +``` |
| 107 | + |
| 108 | +**Parameters:** |
| 109 | + |
| 110 | +- `uri` - JWKS endpoint. Mandatory. |
| 111 | +- `jwks_cache_lifetime` - Period for resend request for refreshing JWKS. Optional, default: 3600. |
| 112 | +- `claims` - A string containing a JSON object that should be contained in the token payload. If this parameter is defined, token without corresponding payload will be considered invalid. Optional. |
| 113 | +- `verifier_leeway` - Clock skew tolerance (seconds). Useful for handling small differences in system clocks between ClickHouse and the token issuer. Optional. |
| 114 | + |
| 115 | + |
| 116 | +## Processors with external providers |
| 117 | + |
| 118 | +Some tokens cannot be decoded and validated locally. External service is needed in this case. "Azure" and "OpenID" (a generic type) are supported now. |
| 119 | + |
| 120 | +### Azure |
| 121 | +```xml |
| 122 | +<clickhouse> |
| 123 | + <token_processors> |
| 124 | + <azure_processor> |
| 125 | + <type>azure</type> |
| 126 | + </azure_processor> |
| 127 | + </token_processors> |
| 128 | +</clickhouse> |
| 129 | +``` |
| 130 | + |
| 131 | +No additional parameters are required. |
| 132 | + |
| 133 | +### OpenID |
| 134 | +```xml |
| 135 | +<clickhouse> |
| 136 | + <token_processors> |
| 137 | + <oid_processor_1> |
| 138 | + <type>openid</type> |
| 139 | + <configuration_endpoint>url/.well-known/openid-configuration</configuration_endpoint> |
| 140 | + <verifier_leeway>60</verifier_leeway> |
| 141 | + <jwks_cache_lifetime>3600</jwks_cache_lifetime> |
| 142 | + </oid_processor_1> |
| 143 | + <oid_processor_2> |
| 144 | + <type>openid</type> |
| 145 | + <userinfo_endpoint>url/userinfo</userinfo_endpoint> |
| 146 | + <token_introspection_endpoint>url/tokeninfo</token_introspection_endpoint> |
| 147 | + <jwks_uri>url/.well-known/jwks.json</jwks_uri> |
| 148 | + <verifier_leeway>60</verifier_leeway> |
| 149 | + <jwks_cache_lifetime>3600</jwks_cache_lifetime> |
| 150 | + </oid_processor_2> |
| 151 | + </token_processors> |
| 152 | +</clickhouse> |
| 153 | +``` |
| 154 | + |
| 155 | +:::note |
| 156 | +Either `configuration_endpoint` or both `userinfo_endpoint` and `token_introspection_endpoint` (and, optionally, `jwks_uri`) shall be set. If none of them are set or all three are set, this is an invalid configuration that will not be parsed. |
| 157 | +::: |
| 158 | + |
| 159 | +**Parameters:** |
| 160 | + |
| 161 | +- `configuration_endpoint` - URI of OpenID configuration (often ends with `.well-known/openid-configuration`); |
| 162 | +- `userinfo_endpoint` - URI of endpoint that returns user information in exchange for a valid token; |
| 163 | +- `token_introspection_endpoint` - URI of token introspection endpoint (returns information about a valid token); |
| 164 | +- `jwks_uri` - URI of OpenID configuration (often ends with `.well-known/jwks.json`) |
| 165 | +- `jwks_cache_lifetime` - Period for resend request for refreshing JWKS. Optional, default: 3600. |
| 166 | +- `verifier_leeway` - Clock skew tolerance (seconds). Useful for handling small differences in system clocks between ClickHouse and the token issuer. Optional, default: 60 |
| 167 | + |
| 168 | +Sometimes a token is a valid JWT. In that case token will be decoded and validated locally if configuration endpoint returns JWKS URI (or `jwks_uri` is specified alongside `userinfo_endpoint` and `token_introspection_endpoint`). |
| 169 | + |
| 170 | +### Tokens cache |
| 171 | +To reduce number of requests to IdP, tokens are cached internally for no longer then `token_cache_lifetime` seconds. |
| 172 | +If token expires sooner than `token_cache_lifetime`, then cache entry for this token will only be valid while token is valid. |
| 173 | +If token lifetime is longer than `token_cache_lifetime`, cache entry for this token will be valid for `token_cache_lifetime`. |
| 174 | + |
| 175 | +## Enabling token authentication for a user in `users.xml` {#enabling-jwt-auth-in-users-xml} |
| 176 | + |
| 177 | +In order to enable token-based authentication for the user, specify `jwt` section instead of `password` or other similar sections in the user definition. |
| 178 | + |
| 179 | +Parameters: |
| 180 | +- `claims` - An optional string containing a json object that should be contained in the token payload. |
| 181 | + |
| 182 | +Example (goes into `users.xml`): |
| 183 | +```xml |
| 184 | +<clickhouse> |
| 185 | + <my_user> |
| 186 | + <jwt> |
| 187 | + <claims>{"resource_access":{"account": {"roles": ["view-profile"]}}}</claims> |
| 188 | + </jwt> |
| 189 | + </my_user> |
| 190 | +</clickhouse> |
| 191 | +``` |
| 192 | + |
| 193 | +Here, the JWT payload must contain `["view-profile"]` on path `resource_access.account.roles`, otherwise authentication will not succeed even with a valid JWT. |
| 194 | + |
| 195 | +:::note |
| 196 | +If `claims` is defined, this user will not be able to authenticate using opaque tokens, so, only JWT-based authentication will be available. |
| 197 | +::: |
| 198 | + |
| 199 | +``` |
| 200 | +{ |
| 201 | +... |
| 202 | + "resource_access": { |
| 203 | + "account": { |
| 204 | + "roles": ["view-profile"] |
| 205 | + } |
| 206 | + }, |
| 207 | +... |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +:::note |
| 212 | +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. |
| 213 | +::: |
| 214 | + |
| 215 | +## Enabling token authentication using SQL {#enabling-jwt-auth-using-sql} |
| 216 | + |
| 217 | +Users with "JWT" authentication type cannot be created using SQL now. |
| 218 | + |
| 219 | +## Identity Provider as an External User Directory {#idp-external-user-directory} |
| 220 | + |
| 221 | +If there is no suitable user pre-defined in ClickHouse, authentication is still possible: Identity Provider can be used as source of user information. |
| 222 | +To allow this, add `token` section to the `users_directories` section of the `config.xml` file. |
| 223 | + |
| 224 | +At each login attempt, ClickHouse tries to find the user definition locally and authenticate it as usual. |
| 225 | +If the user is not defined, ClickHouse will treat the user as externally defined and will try to validate the token and get user information from the specified processor. |
| 226 | +If validated successfully, the user will be considered existing and authenticated. The user will be assigned roles from the list specified in the `roles` section. |
| 227 | +All this implies that the SQL-driven [Access Control and Account Management](/docs/en/guides/sre/user-management/index.md#access-control) is enabled and roles are created using the [CREATE ROLE](/docs/en/sql-reference/statements/create/role.md#create-role-statement) statement. |
| 228 | + |
| 229 | +**Example** |
| 230 | + |
| 231 | +```xml |
| 232 | +<clickhouse> |
| 233 | + <user_directories> |
| 234 | + <token> |
| 235 | + <processor>processor_name</processor> |
| 236 | + <common_roles> |
| 237 | + <token_test_role_1 /> |
| 238 | + </common_roles> |
| 239 | + <roles_filter> |
| 240 | + \bclickhouse-[a-zA-Z0-9]+\b |
| 241 | + </roles_filter> |
| 242 | + </token> |
| 243 | + </user_directories> |
| 244 | +</clickhouse> |
| 245 | +``` |
| 246 | + |
| 247 | +:::note |
| 248 | +For now, no more than one `token` section can be defined inside `user_directories`. This _may_ change in future. |
| 249 | +::: |
| 250 | + |
| 251 | +**Parameters** |
| 252 | + |
| 253 | +- `processor` — Name of one of processors defined in `token_processors` config section described above. This parameter is mandatory and cannot be empty. |
| 254 | +- `common_roles` — Section with a list of locally defined roles that will be assigned to each user retrieved from the IdP. Optional. |
| 255 | +- `roles_filter` — Regex string for groups filtering. Only groups matching this regex will be mapped to roles. Optional. |
0 commit comments