Define and validate restored-session setup state
API dependency and security checks / audit (push) Canceled after 0s
API checks / checks (push) Canceled after 0s

This commit is contained in:
2026-10-25 17:56:08 +01:00
parent 2957524af9
commit 54141e9ccb
7 changed files with 53 additions and 4 deletions
+7
View File
@@ -14,6 +14,13 @@ jobs:
node-version: 22 node-version: 22
- name: Validate repository documentation - name: Validate repository documentation
run: test -f README.md && test -f AGENTS.md && test -f docs/versioning.md run: test -f README.md && test -f AGENTS.md && test -f docs/versioning.md
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Validate restored-session schema and negative cases
run: |
python -m pip install jsonschema==4.26.0
python tests/session_contract.py
- name: Validate JSON contracts and fixtures - name: Validate JSON contracts and fixtures
run: | run: |
node --input-type=module <<'NODE' node --input-type=module <<'NODE'
+5 -1
View File
@@ -16,6 +16,11 @@ request and response envelopes; bearer values in fixtures are synthetic only.
Twitch OAuth link and completion response envelopes are also versioned here; Twitch OAuth link and completion response envelopes are also versioned here;
fixtures contain no real credentials or tokens. fixtures contain no real credentials or tokens.
`GET /api/v1/auth/session` returns `authenticated: true` and the current
`must_replace_credentials` flag from the administrator account. Restoring a
session must not bypass first-run setup. Older servers omit this flag; clients
must not interpret an absent flag as completed credential replacement.
The shared boundary vocabulary includes the ordered permission levels in The shared boundary vocabulary includes the ordered permission levels in
`contracts/v1/permissions.json` and the client/server compatibility result in `contracts/v1/permissions.json` and the client/server compatibility result in
`contracts/v1/version-negotiation.json`. Server-owned state is delivered as `contracts/v1/version-negotiation.json`. Server-owned state is delivered as
@@ -49,4 +54,3 @@ only synthetic Twitch IDs and literal response templates.
Typed command arguments use `bot-command-argument-definition.json` and remain Typed command arguments use `bot-command-argument-definition.json` and remain
literal validated data. literal validated data.
+3 -2
View File
@@ -4,8 +4,9 @@
"title": "Trixie Session Response", "title": "Trixie Session Response",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["authenticated"], "required": ["authenticated", "must_replace_credentials"],
"properties": { "properties": {
"authenticated": { "const": true } "authenticated": { "const": true },
"must_replace_credentials": { "type": "boolean" }
} }
} }
+8
View File
@@ -16,3 +16,11 @@ than in a URL query parameter. The server closes the connection when the frame
is missing, malformed, or invalid, and sends `connection.ready` only after is missing, malformed, or invalid, and sends `connection.ready` only after
successful validation. successful validation.
The restored-session contract has Draft 2020-12 validation for both credential
replacement states, missing fields, wrong types, and unexpected properties:
```sh
uv run --with jsonschema==4.26.0 python tests/session_contract.py
```
This isolated validation dependency is tooling only, not an application dependency.
+2 -1
View File
@@ -1,3 +1,4 @@
{ {
"authenticated": true "authenticated": true,
"must_replace_credentials": false
} }
+4
View File
@@ -0,0 +1,4 @@
{
"authenticated": true,
"must_replace_credentials": true
}
+24
View File
@@ -0,0 +1,24 @@
"""Validate restored-session fixtures and rejection cases against Draft 2020-12."""
import json
from pathlib import Path
from jsonschema import Draft202012Validator
root = Path(__file__).resolve().parents[1]
schema = json.loads((root / "contracts/v1/auth-session-response.json").read_text())
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
for name in ["auth-session.authenticated.json", "auth-session.bootstrap.json"]:
validator.validate(json.loads((root / "fixtures/v1" / name).read_text()))
for invalid in [
{"authenticated": True},
{"authenticated": False, "must_replace_credentials": False},
{"authenticated": True, "must_replace_credentials": "false"},
{"authenticated": True, "must_replace_credentials": False, "unexpected": True},
]:
assert not validator.is_valid(invalid), "Invalid session response was accepted"
print("Session schema: two valid fixtures and four rejection cases passed")