OIDC client discovery and tokens
OIDC client discovery and bearer-token authentication are available in QuestDB Enterprise.
A client needs two things from QuestDB before it can authenticate a user
against your Identity Provider: where the provider's endpoints are, and which
of the two tokens the provider issues QuestDB expects in the
Authorization: Bearer header. Both come from the unauthenticated settings
endpoint, so neither has to be hard coded.
Settings endpoint
QuestDB publishes the parts of its OIDC configuration that a client needs in order to start an authentication flow. Any client can read them from the settings endpoint, which requires no authentication:
curl https://questdb.example.com:9000/settings
The OIDC-related entries of the response look like this, alongside other server settings:
{
"config": {
"acl.enabled": true,
"acl.basic.auth.realm.enabled": false,
"acl.oidc.enabled": true,
"acl.oidc.pkce.required": true,
"acl.oidc.state.required": false,
"acl.oidc.groups.encoded.in.token": false,
"acl.oidc.client.id": "questdb",
"acl.oidc.redirect.uri": "https://questdb.example.com:9000",
"acl.oidc.scope": "openid",
"acl.oidc.authorization.endpoint": "https://oidc.provider:443/as/authorization.oauth2",
"acl.oidc.token.endpoint": "https://oidc.provider:443/as/token.oauth2",
"acl.oidc.device.authorization.endpoint": "https://oidc.provider:443/as/device_authz.oauth2"
},
"preferences.version": 0,
"preferences": {}
}
Each key is the name of the configuration option it carries. The Web Console reads them to build its authorization request, and any other client can do the same instead of hard coding the provider's details.
The published acl.oidc.client.id and acl.oidc.scope belong to QuestDB's Web
Console registration. Reusing them keeps an evaluation client short, but a
production application should have its own provider registration, client ID,
redirect URIs, scopes, and policies. It can still discover the provider
endpoints and acl.oidc.groups.encoded.in.token here while overriding the
client ID and scope.
| Key | Type | Present |
|---|---|---|
acl.enabled | boolean | always |
acl.basic.auth.realm.enabled | boolean | always |
acl.oidc.enabled | boolean | always |
acl.oidc.pkce.required | boolean | always |
acl.oidc.state.required | boolean | always |
acl.oidc.groups.encoded.in.token | boolean | always |
acl.oidc.client.id | string or null | always |
acl.oidc.redirect.uri | string or null | always |
acl.oidc.scope | string | always |
acl.oidc.authorization.endpoint | string | when the endpoint is resolved |
acl.oidc.token.endpoint | string | when the endpoint is resolved |
acl.oidc.device.authorization.endpoint | string | when the endpoint is configured or discovered |
The endpoints are absolute URLs, resolved either from acl.oidc.host and the
endpoint settings, or from the provider's
configuration document when acl.oidc.configuration.url is set.
acl.oidc.device.authorization.endpoint
is present when a device authorization endpoint has been configured, or when the
provider advertises one. QuestDB publishes it without using it, for clients
which implement the OIDC device flow
themselves.
acl.oidc.redirect.uri is null unless
acl.oidc.redirect.uri is set.
A client should then fall back to its own location, as the Web Console does.
QuestDB advertises acl.oidc.pkce.required and acl.oidc.state.required, and
enforces neither. The client generates the code verifier and the state value;
the provider checks the verifier, and the client checks the state value it
gets back.
Read acl.oidc.enabled before any of the other keys. The acl.oidc.* entries
are published whether or not OIDC is enabled. With OIDC disabled and
acl.oidc.host in use, the endpoint URLs are built from the defaults rather
than from a real provider; with acl.oidc.configuration.url set they are not
resolved at all, and the endpoint keys are absent from the response.
OIDC also requires acl.enabled to be
true, which the same response carries. When access control is disabled,
acl.oidc.enabled still reports the configured value while no OIDC
authentication takes place, so read both keys.
The path of the endpoint is set by
http.context.settings.
Setting it adds paths rather than moving the endpoint, so the default path keeps
working too. That default is /settings only while
http.context.web.console
is at its default, as the settings path follows the Web Console context path.
A client which cannot assume both are at their defaults should make the path
configurable.
The official device-flow clients append /settings to the QuestDB base URL
passed to from_questdb, fromQuestDB, or the corresponding C/C++ builder. If
http.context.web.console=/console, pass the context in that base URL:
https://questdb.example.com:9000/console
The clients then request https://questdb.example.com:9000/console/settings.
With a custom http.context.settings, QuestDB still preserves the path beneath
the Web Console context, so the same base URL works. A client which fetches the
settings document itself must use one of the exact configured paths.
Which token to send
acl.oidc.groups.encoded.in.token tells the client which of the tokens returned
by the provider belongs in the Authorization: Bearer header:
| Value | Token to send | How QuestDB validates it |
|---|---|---|
false, the default | the access token | by calling the user info endpoint |
true | the ID token | locally, by checking its signature, audience, and claims |
Sending the wrong one fails authentication with 401 Unauthorized and the
reason in the server log. With
acl.oidc.groups.encoded.in.token
enabled the token has to be a JWT, because QuestDB reads the group memberships
straight out of its payload. The
Web Console picks the token this
way, and so should any other client.
See also
- Client integration patterns - use discovery and bearer tokens from browser, notebook, CLI, and PGWire clients
- OIDC device flow - discovery in the official Java, Python, Rust, C, and C++ clients
- OIDC settings reference - configure the values published to clients