feat(auth): add server-controlled student and admin sessions
Replaces the Phase 2A pilot ?sid= query param with an HttpOnly, SameSite=Lax session cookie. Gateway now derives student identity from the session (via Platform API's /api/session/whoami) instead of trusting a client-suppliable id, and checks ownership on every proxied HTTP and WebSocket Lab request, not just the initial navigation. Fixes a routing bug found during testing: the fallback route was using 'does this session own a running environment' to decide whether an unprefixed /api/* call belonged to the Lab UI or the dashboard — true in both places. Replaced with an explicit LAB_OWNED_PATHS allowlist of the exact unprefixed paths the existing Lab UI actually calls. e2e-phase2a.js rewritten to use a cookie jar instead of headers/query params, with explicit impersonation-rejection tests added. 10/10 passing.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { api, clearStudent } from './api'
|
||||
import { api } from './api'
|
||||
import styles from './Platform.module.css'
|
||||
|
||||
const POLL_MS = 2500
|
||||
@@ -44,9 +44,10 @@ export default function StudentDashboard({ student, onLogout }) {
|
||||
const result = await api.launchEnvironment()
|
||||
setEnvStatus(result)
|
||||
if (result.status === 'running' && result.launch_path) {
|
||||
// sid is a defense-in-depth ownership check the Gateway verifies on this specific
|
||||
// navigation — see docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md "Routing design."
|
||||
window.location.href = `${result.launch_path}?sid=${encodeURIComponent(student.student_id)}`
|
||||
// No ?sid= needed anymore (Phase 2B) — the Gateway verifies ownership from the
|
||||
// rn_session cookie itself, which travels with this navigation automatically. See
|
||||
// docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md §3.
|
||||
window.location.href = result.launch_path
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e.message)
|
||||
@@ -67,7 +68,7 @@ export default function StudentDashboard({ student, onLogout }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = () => { clearStudent(); onLogout() }
|
||||
const handleLogout = () => { api.logout().then(onLogout) }
|
||||
|
||||
const nextStage = curriculum?.stages?.find((s) => s.id === curriculum.next_stage)
|
||||
const nextScenario = nextStage?.scenarios?.find((sc) => sc.status !== 'completed')
|
||||
|
||||
Reference in New Issue
Block a user