# OIDC client discovery and tokens

> **For AI agents:** the complete documentation index is at [llms.txt](https://questdb.com/docs/llms.txt). Every page is available as markdown by appending `.md` to its URL, or by sending an `Accept: text/markdown` request header.

How a QuestDB client reads the OIDC settings endpoint to discover the provider's endpoints, client ID and scope, and which of the two tokens to send.

<EnterpriseNote>
  OIDC client discovery and bearer-token authentication are available in
  QuestDB Enterprise.
</EnterpriseNote>

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:

```bash title="Settings request example"
curl https://questdb.example.com:9000/settings
```

The OIDC-related entries of the response look like this, alongside other server
settings:

```json title="Settings response example"
{
  "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](/docs/configuration/oidc/) it carries. The
[Web Console](/docs/getting-started/web-console/overview/) 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](/docs/configuration/oidc/#endpoints), or from the provider's
configuration document when `acl.oidc.configuration.url` is set.
[`acl.oidc.device.authorization.endpoint`](/docs/configuration/oidc/#acloidcdeviceauthorizationendpoint)
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](/docs/security/oidc/device-flow/)
themselves.

`acl.oidc.redirect.uri` is `null` unless
[`acl.oidc.redirect.uri`](/docs/configuration/oidc/#acloidcredirecturi) 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.

:::note

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`](/docs/configuration/iam/#aclenabled) 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`](/docs/configuration/http-server/#httpcontextsettings).
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`](/docs/configuration/http-server/#httpcontextwebconsole)
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:

```text
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`](/docs/configuration/oidc/#acloidcgroupsencodedintoken)
enabled the token has to be a JWT, because QuestDB reads the group memberships
straight out of its payload. The
[Web Console](/docs/getting-started/web-console/overview/) picks the token this
way, and so should any other client.

## See also

- [Client integration patterns](/docs/security/oidc/client-integration/) - use
  discovery and bearer tokens from browser, notebook, CLI, and PGWire clients
- [OIDC device flow](/docs/security/oidc/device-flow/) - discovery in the
  official Java, Python, Rust, C, and C++ clients
- [OIDC settings reference](/docs/configuration/oidc/) - configure the values
  published to clients
