Skip to content

docs(oidc): document fallback claim lists for acl.oidc.sub.claim and acl.oidc.groups.claim - #575

Open
glasstiger wants to merge 13 commits into
mainfrom
feat/qwp-entra-token-provider
Open

glasstiger wants to merge 13 commits into
mainfrom
feat/qwp-entra-token-provider

Conversation

@glasstiger

@glasstiger glasstiger commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Documents questdb/questdb-enterprise#1267: acl.oidc.sub.claim and acl.oidc.groups.claim now accept a comma-separated list of claim names in priority order, such as preferred_username,oid and roles,groups.

  • Page split: the OIDC page moves to security/oidc/index.mdx, and the Microsoft Entra ID and PingFederate guides move from it to their own pages, security/oidc/entra-id.mdx and security/oidc/pingfederate.mdx, under a new OpenID Connect (OIDC) sidebar category. These are the paths that docs: rework the OIDC documentation and split the OIDC and RBAC guides into sections #522 uses, so neither PR moves a published URL again. The overview stays at /docs/security/oidc/, and the #active-directory, #microsoft-entraid and #pingfederate anchors stay on it as stubs that link to the new pages, because questdb.io redirects point at them.
  • OIDC guide: new User and group claims section. It covers:
    • how QuestDB reads the principal and the groups from the user information, how to choose the principal claim, and that username and password (ROPC) logins use the username that the client sends as the principal;
    • the fallback rules: list order wins over token order, missing, null and empty claims fall through, null and empty group names are skipped, groups are never combined across claims, only top-level claims count, names are case-sensitive, listed claims must have the expected shape, and unlisted claims are ignored whatever their shape;
    • the startup validation, with the exact error messages.
  • OIDC guide, other new sections:
    • Token validation: what QuestDB checks in a JWT, and that with the User Info endpoint it checks neither the audience nor the client of a token. Its When QuestDB stops accepting a token subsection is the one place for how long QuestDB accepts a token after it expires and what outlives the token. It also covers the Web Console: it requests new tokens shortly before the access token expires but sends the ID token, so with Entra ID's default token lifetimes it keeps sending an expired ID token, which 4.0.2 logs as ExpiredSignature and accepts through the session;
    • Troubleshooting OIDC logins: the log lines of rejected logins, such as Failed to find required claims [subClaims=..., groupsClaims=...], and what ExpiredSignature means;
    • Versions before 4.0.2: how earlier versions differ, and what to check when you upgrade, including the ExpiredSignature errors of Web Console users with Entra ID;
    • Tables created by external users: ingestion fails when it has to create a table or add a column for an external user, and SQL DDL needs OWNED BY with one of the user's QuestDB groups;
    • Group changes, sessions, and revoking access: when a change in the identity provider takes effect, that open connections and Web Console sessions keep the groups of the last token that QuestDB accepted, and how to cut off access immediately;
    • Token lifetime, under Non-interactive clients: how a client with a fixed token behaves as the token expires, and how to switch a long-running client to a new token;
    • a minimal server.conf under Configuration options, right after the introduction.
  • Mapping user permissions: corrected. A missing or empty groups claim rejects the login. "Authenticated without permissions" applies only when none of the user's groups is mapped. Also lists the endpoint permission of each interface, with a GRANT example, and states that external aliases must match the group names exactly.
  • Non-interactive clients: which token to send with each value of acl.oidc.groups.encoded.in.token, tokens that services obtain for themselves, and that the setting also changes the token that the Web Console sends. The examples, a client credentials grant and two password grant variants, ingest into fx_trades over QWP.
  • Enable ROPC: the basic authentication example now imports parse and json, which it used without importing.
  • Entra ID guide:
    • preferred_username instead of the display name as the principal claim, a single-tenant registration, the Token configuration settings in the text, and a note on the ExpiredSignature errors that 4.0.2 logs for Web Console users;
    • new section on accepting managed identity and service principal (app-only) tokens alongside user logins: the preferred_username,oid / roles,groups configuration, v2.0 access tokens (requestedAccessTokenVersion: 2) so that aud matches acl.oidc.audience, one app role and one QuestDB group per service, mapped with CREATE GROUP ... WITH EXTERNAL ALIAS, restricting access to app roles, Python examples that ingest over QWP and renew the token, and how to cut off one service with REVOKE.
  • PingFederate guide: adds the QuestDB configuration.
  • Configuration reference:
    • list syntax and the startup validation for both claim settings, and the principal of ROPC logins;
    • acl.oidc.groups.claim now shows no default, because it is required when OIDC is enabled (this was true before #1267 as well);
    • acl.oidc.pkce.enabled renamed to acl.oidc.pkce.required, the name the server reads, with a note that the old name is ignored;
    • new entries for acl.oidc.state.required and acl.oidc.public.keys.expiry, and token validation in acl.oidc.audience, acl.oidc.cache.ttl and acl.oidc.groups.encoded.in.token, including that acl.oidc.audience is not used with the User Info endpoint.
  • RBAC: the HTTP permission covers QWP over WebSocket, and the users and authentication sections describe external users.
  • Clients: the C/C++, .NET, Go and Python pages link to Non-interactive clients and to the Entra ID section, say to build a new client before the token expires, then close the old one, and state the same condition for when QuestDB rejects an expiring token on new connections: QuestDB Enterprise 4.0.2 and later, or the User Info endpoint. The Python page also corrects when bad credentials raise, and that a terminal server rejection raises QuestDBServerRejectionError from flush(wait=True), now described in its own Flushing and acknowledgements section.
  • SQL and ILP: CREATE TABLE, ALTER TABLE ADD COLUMN and the ILP overview explain what external users need.
  • Changelog: October 2026 entries.

Dependencies

Follow-ups

  • questdb.io: once this is live, point the redirects of /docs/guides/microsoft-entraid-oidc/ and /docs/guides/active-directory-pingfederate/ at /docs/security/oidc/entra-id/ and /docs/security/oidc/pingfederate/ instead of the stubs.
  • Web Console: it requests new tokens on the access token's expiry but sends the ID token when the groups are in the token. Refreshing on the ID token's exp instead would remove the ExpiredSignature errors, and the notes about them in this PR.

…acl.oidc.groups.claim

acl.oidc.sub.claim and acl.oidc.groups.claim accept a comma-separated list
of claim names in priority order (questdb/questdb-enterprise#1267).

- OIDC guide: new "User and group claims" section covering fallback claim
  lists, the rules for missing, null and empty claims, the startup
  validation and the "Failed to find required claims" log message.
- Entra ID guide: accepting managed identity and service principal
  (app-only) tokens alongside user logins.
- Configuration reference: claim list syntax for both settings;
  acl.oidc.groups.claim has no default and is required with OIDC enabled.
- Configuration reference: rename acl.oidc.pkce.enabled to
  acl.oidc.pkce.required, the name the server reads.
- Changelog: October 2026 entries.
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🚀 Build success!

Latest successful preview: https://preview-575--questdb-documentation.netlify.app/docs/

Commit SHA: d2bc437

📦 Build generates a preview & updates the link on each commit.

glasstiger and others added 12 commits October 8, 2026 15:38
…setup

- Require a single-tenant QuestDB app registration for app-role mapping:
  token mode checks the signature, aud and exp, but not the issuer or tenant
- Add 4.0.2 warnings to the claims section, the Entra ID subsection and the
  configuration reference, including the pre-4.0.2 requirement for a groups
  array in token mode
- Link the non-interactive clients section and the claim examples to the
  Entra ID subsection, and label the example payloads as Entra ID tokens
- Cover app role assignment, managed identity token caching, the HTTP and
  INSERT grants, the token scope, and token refresh for services
- Document token-mode validation (kid, aud, exp, non-empty sub) and its log
  messages under Rejected logins, and add sub to the app-only payload
- Describe token mode as reading the JWT the client presents, not only the
  ID token
- App-only tokens can carry a groups claim, so list roles before groups
  (acl.oidc.groups.claim=roles,groups) and reject only tokens with
  neither a role nor a group
- State that the principal must be unique and use preferred_username
  for Entra ID users instead of the display name
- List every behavior that is new in QuestDB Enterprise 4.0.2,
  including exp validation, and gate the config page statements
- Add a Token validation section and extend Rejected logins
- Reorder the Entra app-only setup into steps, select a single-tenant
  account type at registration, and rewrite the Python example with
  the current client over TLS
- Correct the acl.oidc.pkce.enabled note, file the new sections under
  New in the changelog, and link the managed identity section from the
  Entra intro and the Azure deployment page
…he Entra app-only guide

- An app-only token without a role falls back to the groups claim. Document
  it, and how to keep app roles the only way in: keep services out of mapped
  security groups, or enable Assignment required.
- QuestDB cannot create a table on ingestion for an external user. Replace
  the CREATE TABLE advice with creating tables in advance, and link OWNED BY.
- Promote the managed identity guide to an H3 with step H4s, keeping its
  anchor. Add prerequisites, the PGWIRE requirements, rejection handling, a
  token renewal example for long-running services, and a verify step.
- With acl.oidc.groups.encoded.in.token=true, the Web Console and ROPC use
  the ID token. Correct steps 7-8, non-interactive clients and the
  configuration reference.
- Move the claim rules and the pre-4.0.2 differences into their own
  sections. Document that exp is required, and that a token can be accepted
  for up to 60 seconds plus acl.oidc.cache.ttl after it expires.
- Configuration reference: single audience, what acl.oidc.cache.ttl caches,
  and links to the claim rules.
- RBAC: QWP uses the HTTP endpoint permission, and OIDC authenticates
  external users and services.
- Spell Entra ID consistently, and update the changelog.
Point the OIDC links of the Python, .NET, C and C++, Go and QWP pages at
the non-interactive clients section of the OIDC guide, and the Python page
at the Entra ID managed identity example.
…he Entra app-only guide

- Principal claim: add "Choose the principal claim" with the oid versus
  preferred_username trade-off. Entra ID documents preferred_username as
  mutable and reusable, so the Entra guide and the configuration reference
  no longer call it unique, and say when to use oid.
- External users cannot create a table or add a column on ingestion. State
  it for every OIDC user in "Tables created by external users", note that
  the non-interactive examples need the table to exist, and create
  fx_trades in the managed identity guide.
- Step 7: in token mode the Web Console sends the ID token, a readable JWT,
  so replace the "opaque token" reassurance with a TLS note.
- Managed identity guide: open with what and why, the 4.0.2 warning and the
  prerequisites. Move Assignment required into its own step, and require
  tenant-wide admin consent with it.
- Make Token validation, Troubleshooting OIDC logins and Versions before
  4.0.2 their own sections, add an upgrade checklist, rename "Active
  Directory" to "Identity provider guides" with its anchor kept, and link
  the managed identity guide from the intro.
- Make the token expiry wording consistent, document that the Web Console's
  HTTP session is not tied to the token, and state that a token whose aud
  list contains acl.oidc.audience is accepted.
- Renewal example: connect with the new token before closing the old
  handle, and keep both when Entra ID or QuestDB cannot be reached.
- Client pages, failover and RBAC: say which token to send, add QWP to the
  second endpoint table, and describe external users.
Split the Microsoft Entra ID and PingFederate guides out of the OIDC page
into security/oidc-entra-id and security/oidc-pingfederate, grouped under
OpenID Connect (OIDC) in the sidebar. Update links from the client,
configuration, deployment and SQL reference pages, document
acl.oidc.public.keys.expiry and acl.oidc.state.required, and correct when
the Python client raises on bad credentials.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…claim lists on 4.0.2

- Entra app-only guide: a missing INSERT grant or a type mismatch is a
  terminal rejection, so flush(wait=True) raises
  QuestDBServerRejectionError as well as calling the error handler. Say
  that the renewal example stops on it. python.md said that the wait never
  raises on a server rejection; correct it there too.
- Mapping user permissions and Token validation: QuestDB keeps the groups
  of the last token that it accepted. A rejected token falls back to the
  Web Console's session cookie, the Web Console's logout ends the session,
  and open connections outlive their token. Revoking the QuestDB group's
  permissions is the way to cut off access at once, also for a service
  whose app role was removed.
- External users: ingestion fails when it has to create a table or add a
  column, QuestDB rolls back the table but keeps the column, and
  Troubleshooting lists the errors that the client and the log show.
- Claim lists: state the 4.0.2 requirement in Fallback claim lists,
  Non-interactive clients, the Entra Configure QuestDB step and RBAC, and
  scope the expired-token grace period to 4.0.2.
- Non-interactive clients: acl.oidc.groups.encoded.in.token=true also
  makes the Web Console send the ID token, which must then carry the
  groups.
- Add a minimal server.conf to the OIDC page and the QuestDB
  configuration to the PingFederate guide.
- Logs can hold rejected tokens, token responses and claims: drop the
  "guaranteed" wording, warn in the token note, and add the Invalid token
  format error to Troubleshooting.
- Entra guide: describe the Token configuration settings in the text, and
  say that app-only tokens can, not always, carry a groups claim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en lifetime

- ROPC logins: the principal is the username that the client sends, not
  the claim that acl.oidc.sub.claim names, and group changes take effect
  after acl.oidc.cache.ttl. Say so in User and group claims, Choose the
  principal claim, the sub.claim and cache.ttl reference entries, and both
  provider guides.
- Token validation: with acl.oidc.groups.encoded.in.token=false, QuestDB
  checks neither the audience nor the client of a token, and
  acl.oidc.audience does not apply. Note it in the audience reference too.
- Non-interactive clients: add a client credentials example over QWP,
  move the password grant examples from ILP/HTTP and questdb.ingress to
  QWP and fx_trades, use the client ID questdb in every example, and add
  Token lifetime, the provider-neutral rules for expiring tokens that the
  client pages link to.
- Mapping user permissions: list the endpoint permission of each
  interface with a GRANT example, and state that external aliases match
  exactly. Move group changes, sessions and revocation to their own
  section, and put the minimal configuration right after the intro.
- python.md: connect() is eager by default everywhere on the page, and a
  terminal rejection raises from flush(wait=True). Move the barrier rules
  out of Value types into Flushing and acknowledgements.
- C/C++, .NET and Go clients: build the new client before the token
  expires, then close the old one.
- Entra guide: one app role and one QuestDB group per service, with
  QuestDB.FxIngest as the example, and REVOKE statements to cut a service
  off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g grants

- Token rotation: with sf_dir, two clients cannot hold the same
  store-and-forward slot, so building the new client before closing the
  old one fails. Token lifetime, the Entra renewal example and the
  Python, Go, C/C++ and .NET pages now say to flush and close the old
  client first, then reuse the sender_id so that the new client replays
  the slot. .NET: call SendAsync() before DisposeAsync(), which does not
  send.
- C/C++, .NET and Go: the client sends the token on every connection
  that it opens, and QuestDB rejects an expired token on new connections
  only on 4.0.2 and later or with the User Info endpoint, shortly after
  it expires.
- Non-interactive clients: create fx_trades and grant HTTP and INSERT to
  the client's group before the ingestion examples.
- Entra guide: emitting groups as role claims removes the app roles from
  the roles claim; it does not mix them.
- Changelog: add Python and the sf_dir order to the token rotation entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… findings

- Entra ID guide: with sf_dir, renew() never switches tokens, because the
  old handle holds the store-and-forward slot. Say so, and add a variant
  that closes the old handle first, then retries the connection with a
  current token instead of reusing the closed handle. Token lifetime
  states the retry rule for every client.
- Entra ID guide: QuestDB rejects a login without a groups claim. Drop the
  statement that users authenticate without customized tokens.
- Title the provider guides "Microsoft Entra ID (Azure AD) OIDC setup" and
  "PingFederate OIDC setup", and add OIDC and Azure AD to the Entra ID
  description.
- OIDC page: add "Where QuestDB reads the user information", comparing the
  User Info endpoint with reading the claims from the token, and add
  acl.oidc.groups.encoded.in.token=true to the fallback claim list
  snippets. The minimal configuration says Entra ID needs it.
- Document that QuestDB downloads the discovery document at startup and
  does not start without it, that with acl.oidc.host it starts while the
  provider is unreachable, and that the two settings cannot be combined.
- C/C++ client: the pool connects eagerly by default, as the 7.0.0 headers
  say; lazy_connect=on defers the errors to the first borrow.
- Changelog entries for the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ired Web Console ID tokens

- Move the OIDC overview and the Entra ID and PingFederate guides to
  security/oidc/{index,entra-id,pingfederate}.mdx, the paths that #522
  uses, so neither PR moves a published URL again. The overview stays at
  /docs/security/oidc/.
- Add "When QuestDB stops accepting a token" under Token validation as
  the one place for the expiry window, and link to it from Token
  lifetime, Group changes and the Entra guide instead of restating it.
- Document that the Web Console refreshes on the access token's expiry
  but sends the ID token when the groups are in the token, so with Entra
  ID's default lifetimes 4.0.2 logs ExpiredSignature and accepts the
  requests through the session. Covered in the new section, step 6,
  Troubleshooting, When you upgrade, and the Entra guide.
- Give the Python client page the same 4.0.2 / User Info endpoint
  qualifier as the C/C++, .NET and Go pages.
- Fix the Enable ROPC example, which used parse and json without
  importing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Wire and claim order

- Token lifetime and the Python, C/C++ and Go pages: wait until QuestDB
  has acknowledged what the old client sent before closing it, because a
  closed client discards the rows that are not acknowledged within
  close_flush_timeout_millis.
- Web Console: it refreshes its tokens before a query only when it holds
  a refresh token, and logs the user out when the provider refuses.
  Without a refresh token, a disabled user keeps working through the
  session. Document offline_access in the minimal configuration and in
  acl.oidc.scope.
- PGWire: which token to send in each mode, that the principal and the
  groups come from the token, the setting as a server.conf block, and TLS.
- Fallback claim lists: a user token that carries roles takes its groups
  from roles alone, so put the Keycloak realm-role mapper on the client of
  the service only. Entra guide: user-assignable app roles do the same.
- sf_dir: a terminally rejected batch stays in the slot and is sent again
  before later batches, so don't send it again (Entra guide, Python page).
  Describe what an ingesting service sees during a revoke.
- Entra guide: finish the setup before a managed identity requests its
  first token, because Azure caches its tokens for about 24 hours.
- PingFederate: the LDAP source is Microsoft Entra Domain Services or
  Active Directory, not Entra ID. Keep the old data source anchor, and fix
  userPrincipalName.
- Rust client: connect is eager by default, as in the 7.x core, and
  lazy_connect=on defers the errors.
- Changelog entries for the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant