Fix missing live Informed Decision browser registration

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
tegwick 2026-09-14 02:54:52 +02:00
parent 651625c547
commit b854e1ed0c
2 changed files with 119 additions and 0 deletions

View file

@ -0,0 +1,56 @@
{
"observed_at": "2026-09-14T00:52:49.599981+00:00",
"status": "applied_verified",
"scope": "public browser registration only; no tokens or approval entries issued by this tool",
"client_id": "informed-decision-approver",
"existing_registration": false,
"before": {
"status": 400,
"error": "invalid_profile_usage",
"description": "unknown client_id",
"feature": "client_id"
},
"phase": "config_applied",
"deployment": {
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
"generation": 52,
"pod_uid": "0da55639-8139-4119-95f5-7415ca2a3052",
"single_ready_replica": true
},
"image_unchanged": "forgejo.coulomb.social/coulomb/key-cape@sha256:db2c5a13a47839049349e881c8d19bc39f720ee69d8518f9f2eba2b1f98af9d5",
"unrelated_config_preserved": true,
"checks": {
"valid_pkce": {
"status": 302,
"redirect_host": "auth.coulomb.social"
},
"wrong_redirect": {
"status": 400,
"error": "invalid_profile_usage",
"description": "redirect_uri does not match any registered URI",
"feature": "redirect_uri"
},
"consume_scope": {
"status": 400,
"error": "invalid_profile_usage",
"description": "requested scope is not allowed",
"feature": "scope"
},
"missing_pkce": {
"status": 400,
"error": "invalid_profile_usage",
"description": "Requests to /authorize must include a code_challenge (PKCE S256 required).",
"feature": "missing_pkce"
}
},
"review_site_login_chain": [
{
"status": 303,
"redirect_host": "kc.coulomb.social"
},
{
"status": 302,
"redirect_host": "auth.coulomb.social"
}
]
}

View file

@ -0,0 +1,63 @@
"""Add the admitted public browser client without exposing or replacing other config."""
import argparse,copy,importlib.util,json,secrets,hashlib,base64
from pathlib import Path
from datetime import datetime,timezone
from urllib.request import build_opener,ProxyHandler,HTTPRedirectHandler
from urllib.parse import urlencode,urlparse
from urllib.error import HTTPError
import yaml
spec=importlib.util.spec_from_file_location('rollout','/home/worsch/net-kingdom/sso-mfa/k8s/keycape/approval-clients-rollout.py');r=importlib.util.module_from_spec(spec);spec.loader.exec_module(r)
CLIENT='informed-decision-approver';r.IDS=(CLIENT,)
KUBE=['kubectl','--kubeconfig','/home/worsch/.kube/config-railiance01']
EXPECTED={'clientId':CLIENT,'displayName':'informed-decision approver surface','audience':'approval-engine','redirectUris':['https://decisions.coulomb.social/auth/callback'],'allowedScopes':['openid','approval:read','approval:approve'],'grantTypes':['authorization_code'],'clientType':'public','tenant':'tenant:platform','mfaRequired':True}
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self,*args,**kwargs):return None
def probe(**overrides):
query={'response_type':'code','client_id':CLIENT,'redirect_uri':EXPECTED['redirectUris'][0],'scope':' '.join(EXPECTED['allowedScopes']),'state':secrets.token_urlsafe(32),'nonce':secrets.token_urlsafe(32),'code_challenge':base64.urlsafe_b64encode(hashlib.sha256(secrets.token_bytes(48)).digest()).rstrip(b'=').decode(),'code_challenge_method':'S256'};query.update(overrides)
try:
response=build_opener(ProxyHandler({}),NoRedirect()).open('https://kc.coulomb.social/authorize?'+urlencode(query),timeout=20)
except HTTPError as e:response=e
with response:
result={'status':response.status}
if response.status==302:result['redirect_host']=urlparse(response.headers['Location']).hostname
elif response.headers.get('Content-Type','').startswith('application/json'):
d=json.load(response);result.update({k:d.get(k) for k in ('error','description','feature')})
return result
def main(args,receipt):
r.assert_cluster(KUBE)
candidates=[c for c in yaml.safe_load(Path('/home/worsch/key-cape/config/service-clients.example.yaml').read_text())['clients'] if c['clientId']==CLIENT]
r.require(candidates==[EXPECTED],'public_registration_contract_drift')
secret=r.get(KUBE,'secret','keycape-config');deployment=r.get(KUBE,'deployment','keycape')
raw,config,_=r.pin.issuer_document(secret)
existing=[c for c in config['clients'] if c['clientId']==CLIENT]
receipt.update(client_id=CLIENT,existing_registration=bool(existing),before=probe())
if existing:
r.require(existing==[EXPECTED],'existing_registration_mismatch');receipt['status']='already_registered';return
config_value=r.replacement(secret,[EXPECTED]);candidate=copy.deepcopy(deployment['spec'])
image=next(c['image'] for c in candidate['template']['spec']['containers'] if c['name']=='keycape')
r.require('@sha256:' in image and candidate['strategy']['type']=='Recreate' and candidate.get('replicas',1)==1,'deployment_pin_required')
candidate['template'].setdefault('metadata',{}).setdefault('annotations',{})['key-cape.coulomb.social/informed-decision-registration']=datetime.now(timezone.utc).isoformat()
r.patch_object(KUBE,'secret',secret,'/data/config.yaml',config_value,dry=True)
r.patch_object(KUBE,'deployment',deployment,'/spec',candidate,dry=True)
receipt['status']='preflight_passed'
if not args.apply:return
r.patch_object(KUBE,'secret',secret,'/data/config.yaml',config_value)
receipt['phase']='config_applied'
after=r.get(KUBE,'secret','keycape-config');r.require(after['data']==dict(secret['data'],**{'config.yaml':config_value}),'unrelated_config_changed')
r.patch_object(KUBE,'deployment',deployment,'/spec',candidate)
receipt['deployment']=r.ready(KUBE,image)
receipt.update(image_unchanged=image,unrelated_config_preserved=True)
checks={'valid_pkce':probe(),'wrong_redirect':probe(redirect_uri='https://example.invalid/callback'),'consume_scope':probe(scope='openid approval:consume'),'missing_pkce':probe(code_challenge='',code_challenge_method='')}
receipt['checks']=checks
r.require(checks['valid_pkce']['status']==302,'authorization_did_not_reach_login')
r.require(all(checks[k]['status'] in (400,403) for k in ('wrong_redirect','consume_scope','missing_pkce')),'authorization_negative_failed')
receipt['status']='applied_verified'
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--apply',action='store_true');p.add_argument('--receipt',type=Path,required=True);args=p.parse_args()
receipt={'observed_at':datetime.now(timezone.utc).isoformat(),'status':'failed','scope':'public browser registration only; no tokens or approval entries issued by this tool'}
try:main(args,receipt)
except Exception:print('Registration operation failed; inspect the metadata-only receipt.');raise SystemExit(1) from None
finally:args.receipt.write_text(json.dumps(receipt,indent=2)+'\n');print(json.dumps(receipt))