A screen somewhere this installation is not reachable from asks the portal for a code instead, and the portal mints its credential — because a token signed here is one such a device could never present. Where it was minted changes nothing about what it may do. The panel gate moved off the branch that decodes a local panel token and onto whatever claims name a panel, so the portal's and this installation's are bounded by the same check against the same panel's dashboards. A token of that scope naming no panel is refused rather than left holding the account it borrows. The connector marks what arrives on its socket, since that is the only thing that makes it true, and the approval screen now names what is holding a code — approving adopts whatever answers, so it is worth a look first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017F9RnYCJgASuBTcAjxmnsp
54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
"""A stand-in portal: its key, its JWKS, and the tokens it would mint.
|
|
|
|
Shared by the remote-access tests and the panel ones, since a screen paired
|
|
through a portal is a portal token that happens to name a panel.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import UTC, datetime, timedelta
|
|
from typing import Any
|
|
|
|
import jwt
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
|
INSTALLATION_ID = "6f1c2d3e-4a5b-4c6d-8e9f-0a1b2c3d4e5f"
|
|
ISSUER = "https://hub.example.test"
|
|
|
|
|
|
def jwks(key: rsa.RSAPrivateKey) -> dict[str, Any]:
|
|
jwk = json.loads(jwt.algorithms.RSAAlgorithm.to_jwk(key.public_key()))
|
|
jwk.update({"use": "sig", "alg": "RS256", "kid": "test-portal"})
|
|
return {"keys": [jwk]}
|
|
|
|
|
|
def portal_token(
|
|
key: rsa.RSAPrivateKey,
|
|
*,
|
|
subject: str = "portal-user-1",
|
|
audience: str = INSTALLATION_ID,
|
|
issuer: str = ISSUER,
|
|
scope: str = "proxy",
|
|
) -> str:
|
|
now = datetime.now(UTC)
|
|
pem = key.private_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PrivateFormat.PKCS8,
|
|
encryption_algorithm=serialization.NoEncryption(),
|
|
)
|
|
return jwt.encode(
|
|
{
|
|
"sub": subject,
|
|
"iss": issuer,
|
|
"aud": audience,
|
|
"iat": now,
|
|
"exp": now + timedelta(hours=1),
|
|
"scope": scope,
|
|
},
|
|
pem,
|
|
algorithm="RS256",
|
|
headers={"kid": "test-portal"},
|
|
)
|