Files
HIPCTF2/docs/guides/authenticated-shell.md
T
2026-07-22 09:37:07 +00:00

116 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: guide
title: Authenticated Shell
description: How a signed-in user experiences the post-login shell, including authenticated event streaming.
tags: [guide, shell, header, sse, navigation, tester]
timestamp: 2026-07-22T09:35:00Z
---
# When this view is available
The authenticated shell is the post-login UI rendered by `HomeComponent`
(`frontend/src/app/features/home/home.component.ts`). It is gated by the client
`authGuard` and the JWT-protected `/api/v1/events/status` stream.
A user who is not signed in, or whose session has expired, is sent to `/login`
before the shell mounts.
# How to access (tester steps)
1. Ensure the instance is initialized. If not, follow [First-Run Bootstrap](/guides/bootstrap.md).
2. Sign in via `/login` as an admin or player.
3. Confirm the browser lands on `/challenges` and renders the shell.
# Expected behavior
## Header
| Element | Selector | Expected |
|---|---|---|
| Page title | `[data-testid="shell-title"]` | Shows the bootstrap `pageTitle`; clicking navigates to `/challenges`. |
| Active section | `[data-testid="shell-active-section"]` | Shows `Challenges`, `Scoreboard`, `Blog`, or `Admin` based on the URL. |
| Event LED | `[data-testid="shell-led"]` | A 10×10 circular dot whose color reflects the current event state. See [Event LED colors](#event-led-colors) below. |
| Countdown | `[data-testid="shell-countdown"]` | Displays `DD:HH:mm`, `Event ended`, or empty for an unconfigured event. |
| User menu | `[data-testid="user-menu-trigger"]` | Toggles rank, change-password, optional admin, and logout entries. |
# Event LED colors
The LED is rendered by `ShellHeaderComponent` as a `<span class="led">` with one of four state classes attached via `[class.led-<state>]` and a `[style.background-color]` bound directly from a pure TypeScript helper.
| Event state | CSS class | Color token (`[style.background-color]` value) | Approx. hex | Visual |
|-------------------|-----------------------|------------------------------------------------|-------------|--------|
| `countdown` | `led led-countdown` | `var(--color-warning)` | `#f59e0b` | Yellow filled dot |
| `running` | `led led-running` | `var(--color-success)` | `#10b981` | Green filled dot |
| `stopped` | `led led-stopped` | `var(--color-danger)` | `#ef4444` | Red filled dot |
| `unconfigured` | `led led-unconfigured`| `var(--color-secondary)` | `#64748b` | Neutral gray filled dot |
The mapping is defined in [`frontend/src/app/core/services/event-status.pure.ts`](/architecture/frontend-structure.md#event-status-pure-helpers) as the constant `LED_COLOR_BY_STATE` and exposed through the helper `ledBackgroundColor(state)`. `ShellHeaderComponent` exposes it on the DOM via a `computed` signal called `ledColor()`, so the active theme token resolves at runtime and the LED is never painted with `transparent`.
The base `.led` rule paints a 10×10 circular `inline-block`, so the LED
is always the same size and shape — only the background color changes
between states. The element also carries
`aria-label="Event status: <state>"` for assistive tech.
# What the tester should see
* The 10×10 LED is visible (not transparent, not invisible) in every state.
* The LED color matches the table above for the active `EventState`.
* Switching the event window (countdown → running → stopped → unconfigured) flips the dot color in real time.
* The `aria-label` updates to `Event status: <state>` and never relies on color alone.
## Quick tabs
The tab strip `[data-testid="quick-tabs"]` contains `Challenges`, `Scoreboard`, and
`Blog`. Clicking a tab navigates to its matching route; the active tab is marked
with the active class.
# Live event window
1. `HomeComponent.ngOnInit()` starts `EventStatusStore` with
`AuthenticatedEventSourceService.open('/api/v1/events/status')`.
2. The transport uses `fetch`, sends `Accept: text/event-stream`, includes
`Authorization: Bearer <access-token>` when available, and sends credentials.
3. It parses SSE `event:` and `data:` lines, buffers frames until a message listener
is registered, and dispatches `MessageEvent` payloads.
4. The store applies each state frame and advances countdown values with a local
one-second tick between server pushes.
5. Leaving the shell closes the source, aborts the fetch, and clears the tick.
A failed HTTP response, missing stream body, or streaming failure dispatches an
error event. The shell retains the last event state until a later connection succeeds.
# Architecture map
| File | Responsibility |
|---|---|
| `frontend/src/app/features/home/home.component.ts` | Hosts the shell and starts/stops the authenticated event stream. |
| `frontend/src/app/core/services/authenticated-event-source.service.ts` | Fetch-based authenticated SSE transport with abort and frame parsing. |
| `frontend/src/app/core/services/event-status.store.ts` | Applies event frames and derives the live countdown. |
| `frontend/src/app/features/shell/header/shell-header.component.ts` | Renders event LED, countdown, navigation, and user menu; owns the component-scoped LED shape and state color rules. |
| `frontend/src/styles.css` | Defines the shared theme color tokens consumed by the shell LED. |
| `frontend/src/app/features/shell/tabs/quick-tabs.component.ts` | Renders the main navigation tabs. |
| `tests/frontend/authenticated-event-source.spec.ts` | Verifies authorization headers, SSE frame delivery, and anonymous header omission. |
# Tester flows
## Live event window
1. Configure a future start time and confirm the countdown LED and text appear.
2. Move the start time into the past and the end time into the future; confirm the
LED changes to running and the countdown counts toward the end.
3. Move the end time into the past; confirm `Event ended` and the stopped LED.
4. Remove either setting; confirm the unconfigured LED and empty countdown.
## Navigation and logout
1. Click each quick tab and confirm the matching route and active styling.
2. Open the user menu and click **Logout**.
3. Confirm navigation to `/login` and that the event stream is closed.
# See also
- [Event Window](/guides/event-window.md)
- [System Endpoints](/api/system.md)
- [Frontend Structure](/architecture/frontend-structure.md)
- [Change Password](/guides/change-password.md)