feat: on_behalf_of — Bascule asserts operator identity in AC requests
When a trusted caller (Bascule SA) sets on_behalf_of in the authorize request, the AC principal is the specified operator DID instead of the SA's JWT identity. Display name extracted from last DID segment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
295fe55bf2
commit
590c267f03
2 changed files with 10 additions and 3 deletions
|
|
@ -76,6 +76,9 @@ class AuthorizeRequest(BaseModel):
|
||||||
accord_template: str
|
accord_template: str
|
||||||
driver_id: str
|
driver_id: str
|
||||||
session_mode: bool = False # When true, AC stays active across multiple CRs
|
session_mode: bool = False # When true, AC stays active across multiple CRs
|
||||||
|
# When set by a trusted caller (e.g. Bascule), overrides the driver-derived principal.
|
||||||
|
# The SA authenticates the request; this field says who the AC is FOR.
|
||||||
|
on_behalf_of: Optional[str] = None
|
||||||
|
|
||||||
class AuthorizeResponse(BaseModel):
|
class AuthorizeResponse(BaseModel):
|
||||||
status: str
|
status: str
|
||||||
|
|
|
||||||
|
|
@ -67,23 +67,27 @@ async def authorize(body: AuthorizeRequest, http_request: Request, db: AsyncSess
|
||||||
expires = now + timedelta(minutes=settings.ac_ttl_minutes)
|
expires = now + timedelta(minutes=settings.ac_ttl_minutes)
|
||||||
ctx_id = uuid.uuid4()
|
ctx_id = uuid.uuid4()
|
||||||
|
|
||||||
|
# on_behalf_of: trusted caller (Bascule SA) asserts who the AC is for
|
||||||
|
principal_did = request.on_behalf_of or auth_result.principal_did
|
||||||
|
display_name = request.on_behalf_of.rsplit("/", 1)[-1] if request.on_behalf_of else auth_result.display_name
|
||||||
|
|
||||||
ac = AuthorizationContext(
|
ac = AuthorizationContext(
|
||||||
context_id=ctx_id, issued_at=now, expires_at=expires,
|
context_id=ctx_id, issued_at=now, expires_at=expires,
|
||||||
principal=Principal(did=auth_result.principal_did, display_name=auth_result.display_name, driver_id=request.driver_id),
|
principal=Principal(did=principal_did, display_name=display_name, driver_id=request.driver_id),
|
||||||
accord=Accord(template=request.accord_template),
|
accord=Accord(template=request.accord_template),
|
||||||
operation=Operation(playbook=request.playbook, corpus_entry_cid=request.corpus_entry_cid, parameters_cid=request.parameters_cid),
|
operation=Operation(playbook=request.playbook, corpus_entry_cid=request.corpus_entry_cid, parameters_cid=request.parameters_cid),
|
||||||
identity_proof=IdentityProof(token_jti=auth_result.token_jti, elevation_active=auth_result.elevation_active, mfa_satisfied=auth_result.mfa_satisfied),
|
identity_proof=IdentityProof(token_jti=auth_result.token_jti, elevation_active=auth_result.elevation_active, mfa_satisfied=auth_result.mfa_satisfied),
|
||||||
broker={"did": settings.broker_did, "name": settings.broker_name})
|
broker={"did": settings.broker_did, "name": settings.broker_name})
|
||||||
|
|
||||||
ac_db = AuthorizationContextDB(
|
ac_db = AuthorizationContextDB(
|
||||||
context_id=ctx_id, principal_did=auth_result.principal_did, driver_id=request.driver_id,
|
context_id=ctx_id, principal_did=principal_did, driver_id=request.driver_id,
|
||||||
playbook=request.playbook, corpus_entry_cid=request.corpus_entry_cid,
|
playbook=request.playbook, corpus_entry_cid=request.corpus_entry_cid,
|
||||||
parameters_cid=request.parameters_cid, accord_template=request.accord_template,
|
parameters_cid=request.parameters_cid, accord_template=request.accord_template,
|
||||||
token_jti=auth_result.token_jti, elevation_active=auth_result.elevation_active,
|
token_jti=auth_result.token_jti, elevation_active=auth_result.elevation_active,
|
||||||
mfa_satisfied=auth_result.mfa_satisfied, status="authorized", issued_at=now, expires_at=expires,
|
mfa_satisfied=auth_result.mfa_satisfied, status="authorized", issued_at=now, expires_at=expires,
|
||||||
session_mode=request.session_mode)
|
session_mode=request.session_mode)
|
||||||
db.add(ac_db)
|
db.add(ac_db)
|
||||||
cid = await chronicle.emit("GSAP_AC_ISSUED", {"event_code": "0x2704", "context_id": str(ctx_id), "principal_did": auth_result.principal_did, "playbook": request.playbook})
|
cid = await chronicle.emit("GSAP_AC_ISSUED", {"event_code": "0x2704", "context_id": str(ctx_id), "principal_did": principal_did, "playbook": request.playbook})
|
||||||
ac_db.chronicle_event_cid = cid
|
ac_db.chronicle_event_cid = cid
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue