From d56b83219783296225d332f8d10f171fb92d7a4c Mon Sep 17 00:00:00 2001 From: James Maes Date: Mon, 12 Oct 2026 06:04:56 -0500 Subject: [PATCH 1/2] docs(readme): add Adopting in another app; neutral examples (#3) Adds the adoption table proposed in #3, verified against the config and metadata producers, linking the QQQ adoption checklist. README and Javadoc examples use neutral names (my-app-admin, my-app-prod); the kof22 test blueprints are described as one host's example. Co-Authored-By: Claude Opus 5.5 --- README.md | 24 ++++++++++++++++--- .../qbits/authentik/AuthentikQBitConfig.java | 2 +- .../authentik/client/TokenBucketThrottle.java | 4 ++-- .../AuthentikAppMetaDataProducer.java | 4 ++-- .../AuthentikGroupPermissionMapper.java | 2 +- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 716e127..e2b3d0c 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ Every write is refused by `AuthentikGuard` in this release. AuthentikQBitConfig config = new AuthentikQBitConfig() .withBaseUrl("https://auth.example.com") // host root; /api/v3 is appended .withApiToken("${env.AUTHENTIK_API_TOKEN}") // service-account token, resolved by the qbit - .withAppKey("kof22-admin") // service account: qqq-kof22-admin-idadmin - .withApplicationsInScope(List.of("kof22-website-prod")); + .withAppKey("my-app-admin") // service account: qqq-my-app-admin-idadmin + .withApplicationsInScope(List.of("my-app-prod")); new AuthentikQBitProducer().withQBitConfig(config).produce(qInstance); ``` @@ -44,7 +44,25 @@ The full `identity.*` set is in DESIGN.md section 5.2. ### Authentik side -The qbit's API token belongs to a dedicated Authentik service account whose role holds only the read permissions it needs. The bootstrap blueprint that creates it lives in the host's GitOps repo. The kof22 version is [`src/test/resources/blueprints/kof22-identity-bootstrap.yaml`](src/test/resources/blueprints/kof22-identity-bootstrap.yaml), which the contract tests exercise. +The qbit's API token belongs to a dedicated Authentik service account whose role holds only the read permissions it needs. The bootstrap blueprint that creates it lives in the host's GitOps repo. One host's version, which the contract tests exercise, is [`src/test/resources/blueprints/kof22-identity-bootstrap.yaml`](src/test/resources/blueprints/kof22-identity-bootstrap.yaml); treat it as an example, not something to apply as is. + +## Adopting in another app + +See also the QQQ [adoption checklist](https://github.com/QRun-IO/qqq/blob/develop/docs/migration/adoption-checklist.adoc) (section 5 covers this QBit). + +| Item | What to do | +|---|---| +| Coordinates | `com.kingsrook.qbits:qbit-authentik`. No release candidate or release is on Maven Central yet; CircleCI publishes snapshots from `develop`. Build from source or use a snapshot until `0.1.0` ships. | +| QQQ version | QQQ 4.1 (see `pom.xml` for the exact RC). Java 21. | +| Infrastructure | Authentik, contract-tested against 2026.8.3. The API must be reachable from the app at `baseUrl` (`/api/v3` is appended). | +| Authentik setup | A dedicated service account (default username `qqq--idadmin`) whose role holds only the read permissions the QBit needs, created by a bootstrap blueprint in the host's GitOps repository. `src/test/resources/blueprints/kof22-identity-bootstrap.yaml` is a worked example from one host, not something to apply as is. | +| Registration | `new AuthentikQBitProducer().withQBitConfig(config).produce(qInstance)`. Produces the `authentikApi` API backend, read-only tables (`authentikUser`, `authentikGroup`, `authentikGroupMember`, `authentikSession`, `authentikEvent`), the drift report widget, and the `authentikAdmin` ("Identity") app, which the host may nest. | +| Required config | `baseUrl`, `apiToken`, `appKey` (lower-case letters, digits, dashes; chooses the service-account name), `applicationsInScope` (Authentik application slugs). Optional: `userScopePathStartsWith`, `groupScopeNamePrefix`, `mirrorBackendName` (phase 1 `MIRROR`), `throttleRequestsPerSecond` (default 10). | +| Secrets / env | `apiToken` as a `${env.X}` reference (the example uses `AUTHENTIK_API_TOKEN`); `bootstrapOperators` defaults to `${env.AUTHENTIK_QBIT_BOOTSTRAP_OPERATORS}` and `webhookSecret` to `${env.AUTHENTIK_WEBHOOK_SECRET}`. Keep them in the host's secret store, never in config files. | +| Permissions | `identity.users.read` (Identity app and tables), `identity.audit.read` (sensitive fields and tables); the full `identity.*` set is in DESIGN.md section 5.2. Granted through the Authentik group attribute `qqq.io/permissions`, read by `AuthentikGroupPermissionMapper`. | +| Storage | None for the read-only line, except the mirror tables when `MIRROR` is enabled on `mirrorBackendName`. | +| Known limitations | Read-only: every write is refused by `AuthentikGuard` until phases 2-3. `RBAC`, `WEBHOOK` and `SNAPSHOT_EXPORT` are refused by config validation. | +| Upgrading | First release line; nothing to migrate. | ## Build diff --git a/src/main/java/com/kingsrook/qbits/authentik/AuthentikQBitConfig.java b/src/main/java/com/kingsrook/qbits/authentik/AuthentikQBitConfig.java index 8203664..71229cf 100644 --- a/src/main/java/com/kingsrook/qbits/authentik/AuthentikQBitConfig.java +++ b/src/main/java/com/kingsrook/qbits/authentik/AuthentikQBitConfig.java @@ -121,7 +121,7 @@ public class AuthentikQBitConfig implements QBitConfig if(!StringUtils.hasContent(appKey) || !APP_KEY_PATTERN.matcher(appKey).matches()) { - errors.add("appKey is required: lower-case letters, digits and dashes, e.g. kof22-admin"); + errors.add("appKey is required: lower-case letters, digits and dashes, e.g. my-app-admin"); } if(tableNamePrefix != null && !tableNamePrefix.isEmpty() && !PREFIX_PATTERN.matcher(tableNamePrefix).matches()) diff --git a/src/main/java/com/kingsrook/qbits/authentik/client/TokenBucketThrottle.java b/src/main/java/com/kingsrook/qbits/authentik/client/TokenBucketThrottle.java index 2dedc92..eded2c1 100644 --- a/src/main/java/com/kingsrook/qbits/authentik/client/TokenBucketThrottle.java +++ b/src/main/java/com/kingsrook/qbits/authentik/client/TokenBucketThrottle.java @@ -23,8 +23,8 @@ import java.util.function.LongSupplier; /******************************************************************************* ** Self-imposed client-side rate limit (DESIGN.md section 9.3). Authentik does - ** not throttle authenticated API calls at all, and kof22's Authentik shares the - ** website's Postgres, so the qbit limits itself: a token bucket refilled at + ** not throttle authenticated API calls at all, and an Authentik may share the + ** Postgres of other applications, so the qbit limits itself: a token bucket refilled at ** requestsPerSecond, holding at most burst tokens. acquire() blocks until a ** token is available. Shared by all requests of one qbit instance. *******************************************************************************/ diff --git a/src/main/java/com/kingsrook/qbits/authentik/metadata/AuthentikAppMetaDataProducer.java b/src/main/java/com/kingsrook/qbits/authentik/metadata/AuthentikAppMetaDataProducer.java index 19c6251..f48642e 100644 --- a/src/main/java/com/kingsrook/qbits/authentik/metadata/AuthentikAppMetaDataProducer.java +++ b/src/main/java/com/kingsrook/qbits/authentik/metadata/AuthentikAppMetaDataProducer.java @@ -32,8 +32,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; /******************************************************************************* ** The "Identity" app (DESIGN.md section 8.1): the qbit's tables (and, from - ** phase 1, its widgets), gated by identity.users.read. Hosts re-parent it, e.g. - ** kof22 nests it under Admin; its name stays the same. Processes are only + ** phase 1, its widgets), gated by identity.users.read. Hosts may re-parent it, + ** for example under an Admin app; its name stays the same. Processes are only ** ever table or record actions, never navigation entries. *******************************************************************************/ public class AuthentikAppMetaDataProducer extends QBitComponentMetaDataProducer diff --git a/src/main/java/com/kingsrook/qbits/authentik/permissions/AuthentikGroupPermissionMapper.java b/src/main/java/com/kingsrook/qbits/authentik/permissions/AuthentikGroupPermissionMapper.java index a022106..aec328f 100644 --- a/src/main/java/com/kingsrook/qbits/authentik/permissions/AuthentikGroupPermissionMapper.java +++ b/src/main/java/com/kingsrook/qbits/authentik/permissions/AuthentikGroupPermissionMapper.java @@ -44,7 +44,7 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; ** Sign-in permissions from Authentik groups (DESIGN.md section 5.6). Use as ** (or extend in) the host's OAuth2 authentication customizer. ** - ** 1. Group names come from the token's "groups" claim (kof22's profile + ** 1. Group names come from the token's "groups" claim (a typical profile ** mapping emits direct groups only). ** 2. Each group is resolved with its ancestors through the cached group ** graph; the union of qqq.io/permissions becomes session permissions, -- 2.54.0 From 3cfde2104acdffe9818501e208481558abc40366 Mon Sep 17 00:00:00 2001 From: James Maes Date: Mon, 12 Oct 2026 06:08:48 -0500 Subject: [PATCH 2/2] docs(changelog): record the adoption section (#3) Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fd1df5..53f86ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project are documented here. Format: [Keep a Changel ## [Unreleased] +### Documentation +- README: "Adopting in another app" section (coordinates, configuration, secrets, permissions, limitations) linking the QQQ adoption checklist; examples use neutral names instead of one host's (#3). + ### Added (phase 1, read-only MVP) - Tables: `authentikGroupMember` (virtual membership table: by group, by user, by key, or a bounded sweep), `authentikSession` and `authentikEvent` (both need `identity.audit.read`). - `GroupGraph` and `EffectivePermissionResolver`: groups, including inherited parents, become QQQ permissions. `identity.*` and catalog wildcards are expanded; unknown names are reported, never granted. Groups show inherited superuser status and effective permissions; users show effective permissions. -- 2.54.0