Phase 2 + 3 complete
This commit is contained in:
parent
9a3fc97a34
commit
6d24f3c112
25 changed files with 3275 additions and 98 deletions
|
|
@ -71,7 +71,7 @@ Add user accounts via Authentik (OIDC) and persist quizzes to SQLite database. U
|
|||
## Phase 2: Backend API Development
|
||||
|
||||
### 2.1 Project Setup
|
||||
- [ ] Create `server/` directory structure:
|
||||
- [x] Create `server/` directory structure:
|
||||
```
|
||||
server/
|
||||
├── Dockerfile
|
||||
|
|
@ -84,110 +84,110 @@ Add user accounts via Authentik (OIDC) and persist quizzes to SQLite database. U
|
|||
├── routes/
|
||||
└── services/
|
||||
```
|
||||
- [ ] Initialize `package.json` with dependencies:
|
||||
- [ ] `express` - Web framework
|
||||
- [ ] `better-sqlite3` - SQLite driver
|
||||
- [ ] `jsonwebtoken` - JWT verification
|
||||
- [ ] `jwks-rsa` - JWKS client for Authentik
|
||||
- [ ] `uuid` - ID generation
|
||||
- [ ] `cors` - CORS middleware
|
||||
- [ ] Dev deps: `typescript`, `@types/*`, `tsx`
|
||||
- [ ] Create `tsconfig.json` for Node.js
|
||||
- [ ] Create `Dockerfile` for backend container
|
||||
- [x] Initialize `package.json` with dependencies:
|
||||
- [x] `express` - Web framework
|
||||
- [x] `better-sqlite3` - SQLite driver
|
||||
- [x] `jsonwebtoken` - JWT verification
|
||||
- [x] `jwks-rsa` - JWKS client for Authentik
|
||||
- [x] `uuid` - ID generation
|
||||
- [x] `cors` - CORS middleware
|
||||
- [x] Dev deps: `typescript`, `@types/*`, `tsx`
|
||||
- [x] Create `tsconfig.json` for Node.js
|
||||
- [x] Create `Dockerfile` for backend container
|
||||
|
||||
### 2.2 Database Layer
|
||||
- [ ] Create `server/src/db/schema.sql`:
|
||||
- [ ] `users` table (synced from OIDC claims)
|
||||
- [ ] `quizzes` table (with `user_id` foreign key)
|
||||
- [ ] `questions` table (with `quiz_id` foreign key)
|
||||
- [ ] `answer_options` table (with `question_id` foreign key)
|
||||
- [ ] Indexes for foreign keys
|
||||
- [ ] Create `server/src/db/connection.ts`:
|
||||
- [ ] Initialize better-sqlite3 connection
|
||||
- [ ] Run schema on startup
|
||||
- [ ] Export db instance
|
||||
- [x] Create `server/src/db/schema.sql`:
|
||||
- [x] `users` table (synced from OIDC claims)
|
||||
- [x] `quizzes` table (with `user_id` foreign key)
|
||||
- [x] `questions` table (with `quiz_id` foreign key)
|
||||
- [x] `answer_options` table (with `question_id` foreign key)
|
||||
- [x] Indexes for foreign keys
|
||||
- [x] Create `server/src/db/connection.ts`:
|
||||
- [x] Initialize better-sqlite3 connection
|
||||
- [x] Run schema on startup
|
||||
- [x] Export db instance
|
||||
|
||||
### 2.3 Authentication Middleware
|
||||
- [ ] Create `server/src/middleware/auth.ts`:
|
||||
- [ ] JWKS client setup pointing to Authentik
|
||||
- [ ] `requireAuth` middleware function:
|
||||
- [ ] Extract Bearer token from Authorization header
|
||||
- [ ] Verify JWT signature against JWKS
|
||||
- [ ] Validate issuer matches Authentik
|
||||
- [ ] Attach decoded user to request
|
||||
- [ ] Define `AuthenticatedRequest` interface
|
||||
- [x] Create `server/src/middleware/auth.ts`:
|
||||
- [x] JWKS client setup pointing to Authentik
|
||||
- [x] `requireAuth` middleware function:
|
||||
- [x] Extract Bearer token from Authorization header
|
||||
- [x] Verify JWT signature against JWKS
|
||||
- [x] Validate issuer matches Authentik
|
||||
- [x] Attach decoded user to request
|
||||
- [x] Define `AuthenticatedRequest` interface
|
||||
|
||||
### 2.4 API Routes
|
||||
- [ ] Create `server/src/routes/quizzes.ts`:
|
||||
- [ ] `GET /api/quizzes` - List user's quizzes (with question count)
|
||||
- [ ] `GET /api/quizzes/:id` - Get full quiz with questions and options
|
||||
- [ ] `POST /api/quizzes` - Save new quiz (upsert user from token)
|
||||
- [ ] `PUT /api/quizzes/:id` - Update existing quiz
|
||||
- [ ] `DELETE /api/quizzes/:id` - Delete quiz (verify ownership)
|
||||
- [ ] Create `server/src/routes/users.ts`:
|
||||
- [ ] `GET /api/users/me` - Get current user profile
|
||||
- [ ] Create `server/src/index.ts`:
|
||||
- [ ] Express app setup
|
||||
- [ ] CORS configuration (allow frontend origin)
|
||||
- [ ] JSON body parser
|
||||
- [ ] Mount routes
|
||||
- [ ] Error handling middleware
|
||||
- [ ] Start server on port 3001
|
||||
- [x] Create `server/src/routes/quizzes.ts`:
|
||||
- [x] `GET /api/quizzes` - List user's quizzes (with question count)
|
||||
- [x] `GET /api/quizzes/:id` - Get full quiz with questions and options
|
||||
- [x] `POST /api/quizzes` - Save new quiz (upsert user from token)
|
||||
- [x] `PUT /api/quizzes/:id` - Update existing quiz
|
||||
- [x] `DELETE /api/quizzes/:id` - Delete quiz (verify ownership)
|
||||
- [x] Create `server/src/routes/users.ts`:
|
||||
- [x] `GET /api/users/me` - Get current user profile
|
||||
- [x] Create `server/src/index.ts`:
|
||||
- [x] Express app setup
|
||||
- [x] CORS configuration (allow frontend origin)
|
||||
- [x] JSON body parser
|
||||
- [x] Mount routes
|
||||
- [x] Error handling middleware
|
||||
- [x] Start server on port 3001
|
||||
|
||||
### 2.5 Backend Testing
|
||||
- [ ] Test API manually with curl/Postman:
|
||||
- [ ] Verify 401 without token
|
||||
- [ ] Verify endpoints work with valid Authentik token
|
||||
- [ ] Verify quiz CRUD operations
|
||||
- [ ] Verify user sync from token claims
|
||||
- [x] Test API with automated test suite:
|
||||
- [x] Verify 401 without token
|
||||
- [x] Verify endpoints work with valid Authentik token
|
||||
- [x] Verify quiz CRUD operations
|
||||
- [x] Verify user sync from token claims
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Frontend Authentication
|
||||
|
||||
### 3.1 Dependencies
|
||||
- [ ] Add to `package.json`:
|
||||
- [ ] `react-oidc-context` - React OIDC hooks
|
||||
- [ ] `oidc-client-ts` - Underlying OIDC client
|
||||
- [x] Add to `package.json`:
|
||||
- [x] `react-oidc-context` - React OIDC hooks
|
||||
- [x] `oidc-client-ts` - Underlying OIDC client
|
||||
- [ ] Run `npm install`
|
||||
|
||||
### 3.2 OIDC Configuration
|
||||
- [ ] Create `src/config/oidc.ts`:
|
||||
- [ ] Define `oidcConfig` object:
|
||||
- [ ] `authority` - Authentik issuer URL
|
||||
- [ ] `client_id` - `kaboot-spa`
|
||||
- [ ] `redirect_uri` - `${origin}/callback`
|
||||
- [ ] `post_logout_redirect_uri` - `${origin}`
|
||||
- [ ] `response_type` - `code` (PKCE)
|
||||
- [ ] `scope` - `openid profile email offline_access`
|
||||
- [ ] `automaticSilentRenew` - `true`
|
||||
- [ ] `userStore` - `WebStorageStateStore` with localStorage
|
||||
- [ ] `onSigninCallback` - Clean URL after redirect
|
||||
- [x] Create `src/config/oidc.ts`:
|
||||
- [x] Define `oidcConfig` object:
|
||||
- [x] `authority` - Authentik issuer URL
|
||||
- [x] `client_id` - `kaboot-spa`
|
||||
- [x] `redirect_uri` - `${origin}/callback`
|
||||
- [x] `post_logout_redirect_uri` - `${origin}`
|
||||
- [x] `response_type` - `code` (PKCE)
|
||||
- [x] `scope` - `openid profile email offline_access`
|
||||
- [x] `automaticSilentRenew` - `true`
|
||||
- [x] `userStore` - `WebStorageStateStore` with localStorage
|
||||
- [x] `onSigninCallback` - Clean URL after redirect
|
||||
|
||||
### 3.3 Auth Provider Setup
|
||||
- [ ] Modify `src/main.tsx`:
|
||||
- [ ] Import `AuthProvider` from `react-oidc-context`
|
||||
- [ ] Import `oidcConfig`
|
||||
- [ ] Wrap `<App />` with `<AuthProvider {...oidcConfig}>`
|
||||
- [x] Modify `index.tsx`:
|
||||
- [x] Import `AuthProvider` from `react-oidc-context`
|
||||
- [x] Import `oidcConfig`
|
||||
- [x] Wrap `<App />` with `<AuthProvider {...oidcConfig}>`
|
||||
|
||||
### 3.4 Auth UI Components
|
||||
- [ ] Create `src/components/AuthButton.tsx`:
|
||||
- [ ] Use `useAuth()` hook
|
||||
- [ ] Show loading state while auth initializing
|
||||
- [ ] Show "Sign In" button when unauthenticated
|
||||
- [ ] Show username + "Sign Out" button when authenticated
|
||||
- [ ] Modify `src/components/Landing.tsx`:
|
||||
- [ ] Add `<AuthButton />` to top-right corner
|
||||
- [ ] Style consistently with existing design
|
||||
- [x] Create `components/AuthButton.tsx`:
|
||||
- [x] Use `useAuth()` hook
|
||||
- [x] Show loading state while auth initializing
|
||||
- [x] Show "Sign In" button when unauthenticated
|
||||
- [x] Show username + "Sign Out" button when authenticated
|
||||
- [x] Modify `components/Landing.tsx`:
|
||||
- [x] Add `<AuthButton />` to top-right corner
|
||||
- [x] Style consistently with existing design
|
||||
|
||||
### 3.5 Authenticated Fetch Hook
|
||||
- [ ] Create `src/hooks/useAuthenticatedFetch.ts`:
|
||||
- [ ] Use `useAuth()` to get access token
|
||||
- [ ] Create `authFetch` wrapper that:
|
||||
- [ ] Adds `Authorization: Bearer <token>` header
|
||||
- [ ] Adds `Content-Type: application/json`
|
||||
- [ ] Handles 401 by triggering silent renew
|
||||
- [ ] Export `{ authFetch, isAuthenticated }`
|
||||
- [x] Create `hooks/useAuthenticatedFetch.ts`:
|
||||
- [x] Use `useAuth()` to get access token
|
||||
- [x] Create `authFetch` wrapper that:
|
||||
- [x] Adds `Authorization: Bearer <token>` header
|
||||
- [x] Adds `Content-Type: application/json`
|
||||
- [x] Handles 401 by triggering silent renew
|
||||
- [x] Export `{ authFetch, isAuthenticated }`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -401,8 +401,8 @@ kaboot/
|
|||
| Phase | Status | Notes |
|
||||
|-------|--------|-------|
|
||||
| Phase 1 | **COMPLETE** | Docker Compose, .env, setup script, Authentik docs |
|
||||
| Phase 2 | Not Started | |
|
||||
| Phase 3 | Not Started | |
|
||||
| Phase 2 | **COMPLETE** | Backend API with Express, SQLite, JWT auth, Quiz CRUD |
|
||||
| Phase 3 | **COMPLETE** | OIDC config, AuthProvider, AuthButton, useAuthenticatedFetch |
|
||||
| Phase 4 | Not Started | |
|
||||
| Phase 5 | Not Started | |
|
||||
| Phase 6 | Not Started | |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue