Skip to content

Message Flows

This page traces the two critical request paths through the system step by step. Read these carefully — they show exactly what every module does and why it exists.

Flow A — Account Linking (OAuth2)

Account Linking happens once, when a user enables the Alexa Skill for the first time. The goal: exchange a username/password for a pair of JWT tokens that Alexa will attach to every future directive.

Phase status

The OAuth2 server on the home server (right half of the diagram) is fully implemented (Phase 4). The OAuth proxy (left half) runs on a Lambda Function URL (Phase 5). During development you can test OAuth directly at http://localhost:8080/oauth/....

Key security mechanisms

MechanismWhat it does
PKCE (S256)Alexa sends a code_challenge (SHA-256 hash of code_verifier) during authorize. At token exchange it sends the raw code_verifier. The server recomputes the hash and compares — ensures only the original caller can exchange the code.
Claims validated before consumeAt token exchange the redirect_uri and client_id are checked against the stored entry, then PKCE is verified, and only then is auth_codes.redeem(code) called. A bad redirect_uri/client_id therefore cannot consume the code and lock out the legitimate client.
Auth code is single-useauth_codes.redeem() atomically consumes the entry. A replayed code returns invalid_grant.
Refresh token rotationOn every /oauth/token?grant_type=refresh_token, the old token is atomically consumed (pop_refresh_token) before the new pair is issued.
bcrypt (via PasswordHasherPort)Passwords are never stored in plain text — only the hash. Verification runs in a thread executor (bcrypt is CPU-bound) and checks unknown users against a dummy hash so response timing does not reveal whether a username exists.
Rate limitingPOST /oauth/authorize is throttled per-IP and per-IP:username (HTTP 429); POST /oauth/token is throttled per-IP.
JWT expiryAccess tokens expire after 60 minutes (configurable via JwtService). Alexa uses the refresh token to get new ones automatically.

Flow B — Voice Command

This is the hot path — everything that happens between "Alexa, switch to ZDF" and the TV changing channel.

Phase status

The home server portion (FastAPI → Router → Handler → Command → Adapter → Device) is fully implemented (Phase 3 + 2). The Lambda proxy + S3 beacon (the top section) is planned (Phase 5). During development, POST directly to http://localhost:8080/alexa/directive.

What each layer does in this flow

LayerResponsibility in this flow
LambdaLooks up current home server URL (S3 beacon); signs the request with X-Tiberio-Timestamp + X-Tiberio-Signature; forwards raw directive
FastAPI routeValidates the HMAC signature (when a shared secret is configured); extracts and validates the Bearer JWT via TokenValidatorPort (401 if invalid); enforces scope == "alexa" (403 if not)
AlexaDirectiveRouterParses the Alexa JSON into a typed model; dispatches to the correct handler by (namespace, name)
PowerHandlerExtracts endpoint ID and correlation token; calls TurnOnCommand/TurnOffCommand; builds the Alexa response
TurnOnCommand / TurnOffCommandResolve the device and its PowerablePort adapter via _find_and_resolve(endpoint_id, PowerablePort), then call adapter.turn_on(device) / adapter.turn_off(device); raise DeviceNotFoundError when the endpoint is unknown
DeviceRegistryPortfind_device(endpoint_id) returns the Device (e.g. a TvChannel) or None; the command raises DeviceNotFoundError(endpoint_id) on None
HarmonyTvAdapter (PowerablePort)turn_on(device): when isinstance(device, TvChannel) it runs ensure_activity(device.watch_activity) then set_channel(device.channel_number); otherwise a no-op. WebSocket calls to the Harmony Hub map Hub exceptions to DeviceUnavailableError

Error handling

Every handler wraps the command call in a try/except block and maps domain errors to Alexa error responses:

ExceptionAlexa error typeAlexa behavior
DeviceNotFoundErrorNO_SUCH_ENDPOINT"That device is not available"
DeviceUnavailableErrorENDPOINT_UNREACHABLE"That device is not responding"
ValueErrorVALUE_OUT_OF_RANGE"That value is out of range"
Any other exceptionINTERNAL_ERROR"Sorry, something went wrong"

Flow C — Token Refresh

Alexa automatically refreshes the access token when it expires (every 60 minutes). The refresh token rotates on each use.

Refresh token rotation means a stolen refresh token can only be used once. The atomic pop_refresh_token (single check-and-revoke) also guarantees that two concurrent refresh requests with the same token cannot both succeed — the legitimate user's next refresh will fail, alerting them to the compromise.

Tiberio — self-hosted Alexa Smart Home backend