comms: mint guest tokens via MAS login

This commit is contained in:
Brad Stein 2026-01-08 11:56:35 -03:00
parent b86800cd6d
commit 59305ca27c
3 changed files with 28 additions and 20 deletions

View File

@ -7,7 +7,7 @@ Core flow
- Synapse is the homeserver; MAS fronts login, Synapse serves client/server APIs.
- Element Web provides the main UI; Element Call embeds LiveKit for group video.
- LiveKit handles SFU media; Coturn provides TURN for NAT traversal.
- matrix-guest-register provides guest accounts + guest sessions (no Keycloak).
- matrix-guest-register provisions MAS guest accounts and performs MAS password login to mint device-bound guest tokens (no Keycloak).
Operational jobs
- mas-db-ensure-job: ensures MAS database role/database + secret in comms.

View File

@ -21,8 +21,6 @@ data:
MAS_ADMIN_CLIENT_ID = os.environ["MAS_ADMIN_CLIENT_ID"]
MAS_ADMIN_CLIENT_SECRET_FILE = os.environ.get("MAS_ADMIN_CLIENT_SECRET_FILE", "/etc/mas/admin-client/client_secret")
MAS_ADMIN_SCOPE = os.environ.get("MAS_ADMIN_SCOPE", "urn:mas:admin")
SESSION_TTL_SEC = int(os.environ.get("SESSION_TTL_SEC", "43200"))
RATE_WINDOW_SEC = int(os.environ.get("RATE_WINDOW_SEC", "60"))
RATE_MAX = int(os.environ.get("RATE_MAX", "30"))
_rate = {} # ip -> [window_start, count]
@ -126,21 +124,30 @@ data:
user = payload.get("data") or {}
return status, user.get("id")
def _create_session(admin_token, user_id, scope):
status, payload = _admin_api(
def _set_password(admin_token, user_id, password):
status, _payload = _admin_api(
admin_token,
"POST",
"/personal-sessions",
{
"actor_user_id": user_id,
"human_name": "guest session",
"scope": scope,
"expires_in": SESSION_TTL_SEC,
},
f"/users/{parse.quote(user_id)}/set-password",
{"password": password},
)
if status != 201:
return None
return (payload.get("data", {}).get("attributes", {}) or {}).get("access_token")
return status in (200, 204)
def _login_password(username, password):
payload = {
"type": "m.login.password",
"identifier": {"type": "m.id.user", "user": f"@{username}:{SERVER_NAME}"},
"password": password,
}
status, data = _json(
"POST",
f"{MAS_BASE}/_matrix/client/v3/login",
body=payload,
timeout=20,
)
if status != 200:
return None, None
return data.get("access_token"), data.get("device_id")
def _set_display_name(access_token, user_id, displayname):
_json(
@ -235,9 +242,12 @@ data:
if not mas_user_id or not localpart:
raise RuntimeError("add_user_failed")
access_token = _create_session(admin_token, mas_user_id, "urn:matrix:client:api:*")
password = secrets.token_urlsafe(18)
if not _set_password(admin_token, mas_user_id, password):
raise RuntimeError("set_password_failed")
access_token, device_id = _login_password(localpart, password)
if not access_token:
raise RuntimeError("session_failed")
raise RuntimeError("login_failed")
try:
_set_display_name(access_token, f"@{localpart}:{SERVER_NAME}", displayname)
except Exception:
@ -248,7 +258,7 @@ data:
resp = {
"user_id": f"@{localpart}:{SERVER_NAME}",
"access_token": access_token,
"device_id": "guest_device",
"device_id": device_id or "guest_device",
"home_server": SERVER_NAME,
}
return self._send_json(200, resp)

View File

@ -47,8 +47,6 @@ spec:
value: http://matrix-authentication-service:8081/api/admin/v1
- name: SYNAPSE_BASE
value: http://othrys-synapse-matrix-synapse:8008
- name: SESSION_TTL_SEC
value: "43200"
- name: MATRIX_SERVER_NAME
value: live.bstein.dev
- name: RATE_WINDOW_SEC