feat: JWT token extraction for Keycloak driver + deploy fix
Added _extract_token_data() to authorize endpoint. Extracts JWT from Authorization header and passes it to the Keycloak identity driver as _token_data. This was the missing link — the driver needs the token to resolve the principal DID. Verified on Hetzner: AC issued for tyler@bxnet.io → did:web:bxnet.capstone.guildhouse.dev/principal/tyler@bxnet.io Chronicle event emitted (GSAP_AC_ISSUED) Known issue: CR endpoint has SQLAlchemy async greenlet bug (MissingGreenlet on the select+update in complete handler). AC issuance works. CR needs async session fix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0c77943ceb
commit
4c58a4414b
1 changed files with 21 additions and 2 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"""POST /governance/authorize/ — GSAP §5.2"""
|
||||
import secrets, uuid
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from gsap_broker.db import get_session
|
||||
from gsap_broker.db_models import AuthorizationContextDB
|
||||
|
|
@ -14,14 +14,33 @@ from gsap_broker import chronicle
|
|||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _extract_token_data(http_request: Request) -> dict:
|
||||
"""Extract and decode JWT from Authorization header (unverified — driver validates)."""
|
||||
auth = http_request.headers.get("authorization", "")
|
||||
if not auth.startswith("Bearer "):
|
||||
return {}
|
||||
token = auth[7:]
|
||||
try:
|
||||
import base64, json
|
||||
payload = token.split(".")[1]
|
||||
payload += "=" * (4 - len(payload) % 4)
|
||||
return json.loads(base64.urlsafe_b64decode(payload))
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
@router.post("/authorize/", response_model=AuthorizeResponse, summary="Issue AC (GSAP §5.2)")
|
||||
async def authorize(request: AuthorizeRequest, db: AsyncSession = Depends(get_session)):
|
||||
async def authorize(body: AuthorizeRequest, http_request: Request, db: AsyncSession = Depends(get_session)):
|
||||
request = body
|
||||
token_data = _extract_token_data(http_request)
|
||||
try:
|
||||
driver = DriverRegistry.get(request.driver_id, config={
|
||||
"requested_accord": request.accord_template,
|
||||
"domain": settings.keycloak_domain,
|
||||
"did_template": settings.keycloak_did_template,
|
||||
"elevated_suffix": settings.keycloak_elevated_role_suffix,
|
||||
"_token_data": token_data,
|
||||
})
|
||||
except KeyError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
|
|
|||
Loading…
Reference in a new issue