Fix plus-address sign-in and finish NK-WP-0041
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e31f-2bcc-7051-a050-70d8cb2dfa49
This commit is contained in:
parent
6700d8f995
commit
2f1e6c3369
7 changed files with 306 additions and 17 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
# Custodian Brief — net-kingdom
|
# Custodian Brief — net-kingdom
|
||||||
|
|
||||||
**Domain:** infotech
|
**Domain:** infotech
|
||||||
**Last synced:** 2026-09-27 11:08 UTC
|
**Last synced:** 2026-09-27 14:02 UTC
|
||||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||||
|
|
||||||
## Active Workstreams
|
## Active Workstreams
|
||||||
|
|
@ -14,12 +14,6 @@ Progress: 0/2 done | workplan_id: `e2533f3a-aa43-59b3-bff3-8e64b6149487`
|
||||||
- · Draft the receipt fields as a Playbook Capability Contract amendment `9d02685a`
|
- · Draft the receipt fields as a Playbook Capability Contract amendment `9d02685a`
|
||||||
- · Agree the evidence holder and schema with audit-core and Railiance `963120c1`
|
- · Agree the evidence holder and schema with audit-core and Railiance `963120c1`
|
||||||
|
|
||||||
### Fix onboarding-journey defects found in the 2026-09-23 human run
|
|
||||||
Progress: 2/3 done | workplan_id: `98168f50-7a4d-5bb5-a462-1e031563b89f`
|
|
||||||
|
|
||||||
**Open tasks:**
|
|
||||||
- ! Plus-addressed email sign-in fails with an LDAP filter error `83633649`
|
|
||||||
|
|
||||||
### Take in the flex-auth to access-engine repository-coordinate rename
|
### Take in the flex-auth to access-engine repository-coordinate rename
|
||||||
Progress: 2/4 done | workplan_id: `284a8ac2-61dc-5bee-b74a-0a62d9808edb`
|
Progress: 2/4 done | workplan_id: `284a8ac2-61dc-5bee-b74a-0a62d9808edb`
|
||||||
|
|
||||||
|
|
@ -60,11 +54,6 @@ Progress: 7/8 done | workplan_id: `d76ddccc-00c8-548a-b141-2cd660fa38da`
|
||||||
**Open tasks:**
|
**Open tasks:**
|
||||||
- ! T08 - Final deletion and closure `42a3b4c0`
|
- ! T08 - Final deletion and closure `42a3b4c0`
|
||||||
|
|
||||||
## Inbox Hygiene
|
|
||||||
|
|
||||||
**Stale unread:** 3 message(s) older than 3 day(s) — triage at session start.
|
|
||||||
**Missing thread_id:** 2 unread message(s) lack supersession chains.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## MCP Orientation (when available)
|
## MCP Orientation (when available)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
| workplan | NK-WP-0038 | finished | — | workplans/NK-WP-0038-tenant-scoped-identity-lifecycle.md |
|
| workplan | NK-WP-0038 | finished | — | workplans/NK-WP-0038-tenant-scoped-identity-lifecycle.md |
|
||||||
| workplan | NK-WP-0039 | active | — | workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md |
|
| workplan | NK-WP-0039 | active | — | workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md |
|
||||||
| workplan | NK-WP-0040 | ready | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
| workplan | NK-WP-0040 | ready | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
||||||
| workplan | NK-WP-0041 | active | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
| workplan | NK-WP-0041 | finished | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
||||||
| workplan | NK-WP-0042 | backlog | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
| workplan | NK-WP-0042 | backlog | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
||||||
| task | NK-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | NK-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
| task | NK-WP-ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | NK-WP-ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
| task | NK-WP-0040-T01 | todo | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
| task | NK-WP-0040-T01 | todo | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
||||||
| task | NK-WP-0040-T02 | todo | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
| task | NK-WP-0040-T02 | todo | — | workplans/NK-WP-0040-execution-attribution-receipt.md |
|
||||||
| task | NK-WP-0041-T01 | done | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
| task | NK-WP-0041-T01 | done | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
||||||
| task | NK-WP-0041-T02 | wait | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
| task | NK-WP-0041-T02 | done | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
||||||
| task | NK-WP-0041-T03 | done | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
| task | NK-WP-0041-T03 | done | — | workplans/NK-WP-0041-onboarding-journey-usability.md |
|
||||||
| task | NK-WP-0042-T01 | todo | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
| task | NK-WP-0042-T01 | todo | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
||||||
| task | NK-WP-0042-T02 | todo | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
| task | NK-WP-0042-T02 | todo | — | workplans/NK-WP-0042-workload-mfa-step-up.md |
|
||||||
|
|
|
||||||
|
|
@ -117,3 +117,32 @@ kubectl run -n sso --rm -it auth-test --image=busybox --restart=Never \
|
||||||
# OIDC discovery (should return issuer + endpoints)
|
# OIDC discovery (should return issuer + endpoints)
|
||||||
curl -s https://auth.coulomb.social/.well-known/openid-configuration | jq .
|
curl -s https://auth.coulomb.social/.well-known/openid-configuration | jq .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 4.39 compatibility and regression check
|
||||||
|
|
||||||
|
KeyCape needs `preferred_username` in the signed upstream ID token for its
|
||||||
|
directory and MFA lookup. Authelia 4.39 requires the explicit `keycape`
|
||||||
|
claims policy in `configmap.yaml`; deploy it together with the pinned image.
|
||||||
|
KeyCape's in-cluster token requests must also carry the public HTTPS forwarded
|
||||||
|
scheme and host (KeyCape `3b0446e` or later). Neither a health check nor an
|
||||||
|
authorization redirect exercises these requirements.
|
||||||
|
|
||||||
|
Run the isolated token regression with an Authelia 4.39.28 binary and Python
|
||||||
|
`requests`, `PyYAML`, and `cryptography` installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 sso-mfa/k8s/authelia/tests/probe_claims.py --authelia-bin /path/to/authelia
|
||||||
|
```
|
||||||
|
|
||||||
|
It uses disposable local users, keys and SQLite databases, checks real signed
|
||||||
|
authorization-code tokens with and without the policy, and terminates the
|
||||||
|
scratch processes. It uses a file backend, so it does not prove LDAP email
|
||||||
|
lookup. Live acceptance also requires a fresh plus-addressed email sign-in
|
||||||
|
through KeyCape to both Vergabe and the account portal, with successful
|
||||||
|
callback and token issuance evidence.
|
||||||
|
|
||||||
|
Before a 4.38 → 4.39 rollout, stop Authelia and preserve the SQLite database
|
||||||
|
and any sidecar files. Keep the previous ConfigMap and image reference too.
|
||||||
|
Rollback requires stopping 4.39 and restoring all three: database, ConfigMap,
|
||||||
|
and image. The migrated database and 4.39 claims policy cannot be used by
|
||||||
|
4.38. See `NK-WP-0041` for the exercised rollout and backup receipt.
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,17 @@ data:
|
||||||
# KeyCape is the only registered client.
|
# KeyCape is the only registered client.
|
||||||
identity_providers:
|
identity_providers:
|
||||||
oidc:
|
oidc:
|
||||||
|
# Authelia 4.39 no longer includes profile claims in ID tokens by
|
||||||
|
# default. KeyCape uses this verified claim for directory/MFA lookup;
|
||||||
|
# its opaque OIDC subject is not a directory username (NK-WP-0041).
|
||||||
|
claims_policies:
|
||||||
|
keycape:
|
||||||
|
id_token: [preferred_username]
|
||||||
# hmac_secret: injected via AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE
|
# hmac_secret: injected via AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE
|
||||||
# issuer_private_key: injected via AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE
|
# issuer_private_key: injected via AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE
|
||||||
clients:
|
clients:
|
||||||
- id: keycape
|
- id: keycape
|
||||||
|
claims_policy: keycape
|
||||||
description: "KeyCape IAM Orchestration Layer"
|
description: "KeyCape IAM Orchestration Layer"
|
||||||
# OIDC clients are a list, so Authelia's *_FILE environment
|
# OIDC clients are a list, so Authelia's *_FILE environment
|
||||||
# mechanism cannot override this field. The template filter reads
|
# mechanism cannot override this field. The template filter reads
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ spec:
|
||||||
containers:
|
containers:
|
||||||
- name: authelia
|
- name: authelia
|
||||||
# Pin to a specific 4.x release. Check https://hub.docker.com/r/authelia/authelia
|
# Pin to a specific 4.x release. Check https://hub.docker.com/r/authelia/authelia
|
||||||
image: docker.io/authelia/authelia@sha256:46021dc20efdcc5cdc38a29e3050b8835429a155ae6215388ed3b793a02eb0ab # 4.38 (rolled back from 4.39.28, NK-WP-0041-T02)
|
# Requires the KeyCape claims policy in configmap.yaml (NK-WP-0041).
|
||||||
|
image: docker.io/authelia/authelia@sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54 # 4.39.28
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
214
sso-mfa/k8s/authelia/tests/probe_claims.py
Normal file
214
sso-mfa/k8s/authelia/tests/probe_claims.py
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
"""Integration regression for NK-WP-0041; needs requests, PyYAML, cryptography."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding, rsa
|
||||||
|
|
||||||
|
|
||||||
|
def decode(value):
|
||||||
|
return base64.urlsafe_b64decode(value + "=" * (-len(value) % 4))
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Exercise real Authelia ID tokens with and without the KeyCape claims policy; scratch data only."
|
||||||
|
)
|
||||||
|
parser.add_argument("--authelia-bin", required=True)
|
||||||
|
binary = str(pathlib.Path(parser.parse_args().authelia_bin).resolve())
|
||||||
|
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
|
pem = key.private_bytes(
|
||||||
|
serialization.Encoding.PEM,
|
||||||
|
serialization.PrivateFormat.PKCS8,
|
||||||
|
serialization.NoEncryption(),
|
||||||
|
).decode()
|
||||||
|
source = yaml.safe_load(
|
||||||
|
yaml.safe_load(
|
||||||
|
(pathlib.Path(__file__).resolve().parents[1] / "configmap.yaml").read_text()
|
||||||
|
)["data"]["configuration.yml"]
|
||||||
|
)["identity_providers"]["oidc"]
|
||||||
|
for enabled in (False, True):
|
||||||
|
with tempfile.TemporaryDirectory(prefix="nk-claims-") as tmp:
|
||||||
|
p = pathlib.Path(tmp)
|
||||||
|
with socket.socket() as s:
|
||||||
|
s.bind(("127.0.0.1", 0))
|
||||||
|
port = s.getsockname()[1]
|
||||||
|
password = "scratch-password-only"
|
||||||
|
digest = (
|
||||||
|
subprocess.check_output(
|
||||||
|
[
|
||||||
|
binary,
|
||||||
|
"crypto",
|
||||||
|
"hash",
|
||||||
|
"generate",
|
||||||
|
"argon2",
|
||||||
|
"--password",
|
||||||
|
password,
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
.strip()
|
||||||
|
.split("Digest: ")[1]
|
||||||
|
)
|
||||||
|
(p / "users.yml").write_text(
|
||||||
|
yaml.safe_dump(
|
||||||
|
{
|
||||||
|
"users": {
|
||||||
|
"nk-probe": {
|
||||||
|
"displayname": "Scratch User",
|
||||||
|
"password": digest,
|
||||||
|
"email": "nk-probe+x@example.com",
|
||||||
|
"groups": [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
client = dict(source["clients"][0])
|
||||||
|
client.update(
|
||||||
|
secret="scratch-client-secret-only",
|
||||||
|
redirect_uris=["https://client.example.com/callback"],
|
||||||
|
)
|
||||||
|
if not enabled:
|
||||||
|
client.pop("claims_policy")
|
||||||
|
oidc = {
|
||||||
|
"hmac_secret": "h" * 64,
|
||||||
|
"jwks": [{"key": pem, "algorithm": "RS256", "use": "sig"}],
|
||||||
|
"clients": [client],
|
||||||
|
}
|
||||||
|
if enabled:
|
||||||
|
oidc["claims_policies"] = source["claims_policies"]
|
||||||
|
cfg = {
|
||||||
|
"ntp": {"disable_startup_check": True},
|
||||||
|
"server": {"address": f"tcp://127.0.0.1:{port}/"},
|
||||||
|
"log": {"level": "info"},
|
||||||
|
"authentication_backend": {"file": {"path": str(p / "users.yml")}},
|
||||||
|
"session": {
|
||||||
|
"secret": "s" * 64,
|
||||||
|
"cookies": [
|
||||||
|
{
|
||||||
|
"domain": "example.com",
|
||||||
|
"authelia_url": "https://auth.example.com",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"encryption_key": "e" * 64,
|
||||||
|
"local": {"path": str(p / "db.sqlite3")},
|
||||||
|
},
|
||||||
|
"notifier": {"filesystem": {"filename": str(p / "notifications")}},
|
||||||
|
"access_control": {"default_policy": "one_factor"},
|
||||||
|
"identity_validation": {"reset_password": {"jwt_secret": "j" * 64}},
|
||||||
|
"identity_providers": {"oidc": oidc},
|
||||||
|
}
|
||||||
|
(p / "config.yml").write_text(yaml.safe_dump(cfg))
|
||||||
|
with (p / "log").open("w") as log:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
[binary, "--config", str(p / "config.yml")], stdout=log, stderr=log
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
base = f"http://127.0.0.1:{port}"
|
||||||
|
session.headers.update(
|
||||||
|
{
|
||||||
|
"Host": "auth.example.com",
|
||||||
|
"X-Forwarded-Proto": "https",
|
||||||
|
"X-Forwarded-Host": "auth.example.com",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for _ in range(100):
|
||||||
|
if proc.poll() is not None:
|
||||||
|
raise RuntimeError((p / "log").read_text())
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
session.get(base + "/api/health", timeout=1).status_code
|
||||||
|
== 200
|
||||||
|
):
|
||||||
|
break
|
||||||
|
except requests.ConnectionError:
|
||||||
|
pass
|
||||||
|
time.sleep(0.1)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Scratch Authelia did not become healthy")
|
||||||
|
r = session.post(
|
||||||
|
base + "/api/firstfactor",
|
||||||
|
json={
|
||||||
|
"username": "nk-probe",
|
||||||
|
"password": password,
|
||||||
|
"keepMeLoggedIn": False,
|
||||||
|
},
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200, (r.status_code, r.text)
|
||||||
|
session.headers["Cookie"] = "; ".join(
|
||||||
|
c.name + "=" + c.value for c in session.cookies
|
||||||
|
)
|
||||||
|
r = session.get(
|
||||||
|
base + "/api/oidc/authorization",
|
||||||
|
params={
|
||||||
|
"client_id": "keycape",
|
||||||
|
"redirect_uri": "https://client.example.com/callback",
|
||||||
|
"response_type": "code",
|
||||||
|
"scope": "openid profile email groups",
|
||||||
|
"state": "scratch-state-long-enough",
|
||||||
|
"nonce": "scratch-nonce-long-enough",
|
||||||
|
},
|
||||||
|
allow_redirects=False,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
loc = r.headers.get("Location", "")
|
||||||
|
query = urllib.parse.parse_qs(urllib.parse.urlparse(loc).query)
|
||||||
|
assert "code" in query, (
|
||||||
|
r.status_code,
|
||||||
|
loc,
|
||||||
|
r.text,
|
||||||
|
(p / "log").read_text(),
|
||||||
|
)
|
||||||
|
r = session.post(
|
||||||
|
base + "/api/oidc/token",
|
||||||
|
auth=("keycape", "scratch-client-secret-only"),
|
||||||
|
data={
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"code": query["code"][0],
|
||||||
|
"redirect_uri": "https://client.example.com/callback",
|
||||||
|
},
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200, (r.status_code, r.text)
|
||||||
|
parts = r.json()["id_token"].split(".")
|
||||||
|
key.public_key().verify(
|
||||||
|
decode(parts[2]),
|
||||||
|
(".".join(parts[:2])).encode(),
|
||||||
|
padding.PKCS1v15(),
|
||||||
|
hashes.SHA256(),
|
||||||
|
)
|
||||||
|
claims = json.loads(decode(parts[1]))
|
||||||
|
assert claims["iss"] == "https://auth.example.com"
|
||||||
|
assert "keycape" in claims["aud"]
|
||||||
|
assert claims["nonce"] == "scratch-nonce-long-enough"
|
||||||
|
assert claims["exp"] > time.time()
|
||||||
|
assert (claims.get("preferred_username") == "nk-probe") == enabled
|
||||||
|
assert claims["sub"] != "nk-probe"
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"claims_policy": enabled,
|
||||||
|
"signed_id_token_verified": True,
|
||||||
|
"preferred_username_present": "preferred_username"
|
||||||
|
in claims,
|
||||||
|
"subject_is_directory_username": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
proc.terminate()
|
||||||
|
proc.wait(timeout=10)
|
||||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
||||||
title: "Fix onboarding-journey defects found in the 2026-09-23 human run"
|
title: "Fix onboarding-journey defects found in the 2026-09-23 human run"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: net-kingdom
|
repo: net-kingdom
|
||||||
status: active
|
status: finished
|
||||||
flavor: implementation
|
flavor: implementation
|
||||||
owner: claude-code
|
owner: claude-code
|
||||||
topic_slug: netkingdom
|
topic_slug: netkingdom
|
||||||
|
|
@ -59,7 +59,7 @@ to a user; note it here if the password manager still misbehaves.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: NK-WP-0041-T02
|
id: NK-WP-0041-T02
|
||||||
status: wait
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "83633649-3e5c-57e7-8207-609380a29c25"
|
state_hub_task_id: "83633649-3e5c-57e7-8207-609380a29c25"
|
||||||
```
|
```
|
||||||
|
|
@ -260,3 +260,52 @@ explained. The plus-address filter defect remains on 4.38.
|
||||||
`bernd.worsch-99` and could log in. The email address still cannot.
|
`bernd.worsch-99` and could log in. The email address still cannot.
|
||||||
That is the restored 4.38 behavior. The session closed there, with T02
|
That is the restored 4.38 behavior. The session closed there, with T02
|
||||||
still `wait`.
|
still `wait`.
|
||||||
|
|
||||||
|
|
||||||
|
2026-09-27, third attempt: isolated diagnosis and claims-policy correction.
|
||||||
|
Authelia 4.39 intentionally removed profile claims from default ID tokens
|
||||||
|
([release notes](https://www.authelia.com/blog/4.39-release-notes/)). The deployed
|
||||||
|
KeyCape commit `3b0446e` reads `preferred_username` from the verified ID token
|
||||||
|
and falls back to `sub`; that opaque subject is not the LDAP username needed
|
||||||
|
by privacyIDEA. The live config supplied no claims policy. This explains the
|
||||||
|
MFA lookup failure; the old telemetry did not capture the exact lookup error.
|
||||||
|
|
||||||
|
Added a KeyCape-only claims policy emitting `preferred_username`, retaining
|
||||||
|
its existing scopes, audience behavior, one-factor policy and secret template.
|
||||||
|
No MFA bypass was added. The regression probe at
|
||||||
|
`sso-mfa/k8s/authelia/tests/probe_claims.py` runs an isolated real 4.39.28
|
||||||
|
provider with disposable users/keys and completes authorization-code exchanges:
|
||||||
|
without the policy the signed ID token omits the username; with the policy it
|
||||||
|
contains the expected directory username. The subject is distinct in both
|
||||||
|
cases. Both signatures and the public HTTPS issuer are checked. Scratch NTP
|
||||||
|
startup checking is disabled so this local test does not depend on external
|
||||||
|
clock services; production NTP configuration is unchanged. The complete repo
|
||||||
|
config also passes 4.39.28 validation with placeholder secrets (legacy
|
||||||
|
configuration deprecation warnings only).
|
||||||
|
|
||||||
|
The operator confirmed availability for browser acceptance. Codex stopped
|
||||||
|
Authelia before copying SQLite and verified `PRAGMA quick_check = ok`.
|
||||||
|
Fresh rollback copy on the PVC:
|
||||||
|
`backups/pre-4.39.28-claims-20260927T135544Z/db.sqlite3` (2,244,608 bytes).
|
||||||
|
The old ConfigMap and deployment snapshots plus rollback helper are in
|
||||||
|
`/tmp/nk-wp0041/` for this session. Rollback must restore the old config as
|
||||||
|
well as the database and 4.38 image, since claims policies require 4.39.
|
||||||
|
The claims policy and pinned 4.39.28 image were applied together.
|
||||||
|
|
||||||
|
Closure evidence, 2026-09-27 (UTC):
|
||||||
|
|
||||||
|
- Authelia is healthy on the declared 4.39.28 digest. The known LDAP startup
|
||||||
|
race caused two restarts; startup completed at 13:57:00.
|
||||||
|
- At 13:57:44–45 both invalid-address probes (with and without `+`) returned
|
||||||
|
generic HTTP 401 and logged `user not found`, with no filter compile error.
|
||||||
|
- Vergabe: KeyCape `auth_success` at 13:58:38 and authorization-code
|
||||||
|
`token_issued` at 13:58:39 for `vergabe-demo-company`.
|
||||||
|
- Account portal: `auth_success` at 13:59:47 and authorization-code
|
||||||
|
`token_issued` at 13:59:48 for `user-engine-portal`.
|
||||||
|
- The operator explicitly confirmed "Both sign-ins work" when asked to use
|
||||||
|
`bernd.worsch+99@gmail.com` in a fresh private window for both sites.
|
||||||
|
|
||||||
|
T02 is done and NK-WP-0041 is finished. The scoped claims policy fixes the
|
||||||
|
username propagation without changing MFA requirements. Transactional email
|
||||||
|
link delivery remains the separately owned USER-WP-0035-T02 item described
|
||||||
|
under T03; it is not a remaining task in this workplan.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue