OAuth2-protected React UI for managing user records, with real-time WebSocket updates. Uses node-postgres-api, which should be running in a container first.
Built with React 19, Vite, Redux Toolkit + RTK Query, React Hook Form + Zod, and React Router. Deployed as a static asset bundle served by Nginx inside a Chainguard container.
- OAuth2 login via
client_credentialsgrant and an OAuth callback route (/callback) for authorization code exchange flows - Authenticated browser sessions backed by
HttpOnly+Securecookies, checked on app startup - All API requests include cookies via RTK Query
- Real-time push notifications over WebSocket, authenticated by the browser session cookie
- Full user CRUD: list, create, edit, delete
- Protected routes — unauthenticated users are redirected to
/login - Form validation with React Hook Form and Zod schemas
| Layer | Tool |
|---|---|
| UI framework | React 19 + TypeScript |
| Build tool | Vite 8 (SWC) |
| State | Redux Toolkit 2 |
| API / caching | RTK Query |
| Forms | React Hook Form 7 + Zod 4 |
| Routing | React Router 8 |
| Tests | Vitest |
| Container | Chainguard Node (build) + Chainguard Nginx (runtime) |
| Path | Description | Auth required |
|---|---|---|
/login |
OAuth2 login form | No |
/callback |
Authorization code exchange with spinner | No |
/logout |
Clears the session and redirects to /login |
No |
/users |
List all users | Yes |
/users/new |
Create a user | Yes |
/users/:id/edit |
Edit a user | Yes |
- Node.js v20+
- pnpm
- Docker & Docker Compose (for the full stack)
- node-postgres-api running in its container
pnpm installpnpm devThe UI starts at http://localhost:5173. The Vite dev server proxies /oauth, /api, /secure-rest, and /ws to localhost:5000 (configurable via VITE_API_PROXY_TARGET).
docker compose up --buildThe UI is served at http://localhost:3000 via Nginx. The container connects to the backend over the oauth_shared_bridge Docker network.
| Variable | Default | Description |
|---|---|---|
VITE_API_PROXY_TARGET |
http://localhost:5000 |
Backend base URL for the Vite dev proxy |
VITE_API_URL |
http://localhost:5000 |
Backend base URL injected at container runtime |
pnpm dev # Start Vite dev server
pnpm build # Type-check and build for production
pnpm preview # Preview the production build locally
pnpm test # Run Vitest test suite oncesrc/
app/
api.ts # RTK Query API slices (authApi, usersApi)
authSlice.ts # Redux slice for auth token + username
websocketSlice.ts # Redux slice for WebSocket status + messages
websocketClient.ts # WebSocket factory (subprotocol auth)
logout.ts # Shared performLogout helper
store.ts # Redux store
components/
NotificationBanner.tsx # WebSocket status + latest message
ProtectedRoute.tsx # Redirects unauthenticated users to /login
pages/
LoginPage.tsx # OAuth2 login form
CallbackPage.tsx # Authorization code exchange with spinner
UsersPage.tsx # User list with edit/delete
UserFormPage.tsx # Create / edit user form
App.tsx # Router, nav, WebSocket lifecycle
- User submits credentials on
/login - UI posts
grant_type=client_credentialstoPOST /oauth/token(form-encoded) - The backend sets an
HttpOnlysession cookie and the UI marks the session authenticated in Redux - On refresh, the app checks the cookie-backed session before rendering protected routes
- All subsequent API requests include cookies automatically
- WebSocket connects using the browser session cookie
- Logout dispatches
clearAuth, callsPOST /oauth/logout, and redirects to/login
pnpm testTests cover OAuth token exchange format, session checking, auth slice actions, WebSocket connection setup, logout flow, and the callback code exchange.
See CONTRIBUTING.md for architecture conventions, coding standards, smoke test instructions, and the PR checklist.