Add simple auth
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# Tasks: Basic Login Protection
|
||||
|
||||
**Input**: Design documents from `/specs/003-basic-login/`
|
||||
**Prerequisites**: plan.md ✅ spec.md ✅ research.md ✅ data-model.md ✅ contracts/ ✅ quickstart.md ✅
|
||||
|
||||
**Tests**: Not requested — no test tasks included.
|
||||
|
||||
**Organization**: Tasks grouped by user story to enable independent implementation and testing.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies on incomplete tasks)
|
||||
- **[Story]**: Which user story this task belongs to (US1, US2, US3)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: No new project initialization needed — existing backend and frontend projects are in place. Phase 1 confirms the entry points for changes.
|
||||
|
||||
- [x] T001 Verify `spring-boot-starter-security` is present in `backend/pom.xml` (already included — confirm, no change needed)
|
||||
- [x] T002 Verify Pinia is listed in `frontend/package.json` dependencies (already included — confirm, no change needed)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Backend credential endpoint and frontend auth store — required by all three user stories.
|
||||
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
|
||||
|
||||
- [x] T003 Add `app.auth.username` property to `backend/src/main/resources/application.yaml` with value `${APP_AUTH_USERNAME:neurosurgeon}` alongside the existing `app.auth.password` entry
|
||||
- [x] T004 Update `backend/src/main/java/com/aiteacher/config/SecurityConfig.java` to inject `@Value("${app.auth.username}")` and pass it to `User.builder().username(username)` instead of the hardcoded string `"neurosurgeon"`
|
||||
- [x] T005 Create `backend/src/main/java/com/aiteacher/auth/AuthController.java` — `@RestController` at `/api/v1/auth`, with a single `GET /check` method that accepts a `Principal` argument and returns `ResponseEntity.ok(Map.of("username", principal.getName()))`
|
||||
- [x] T006 Create `frontend/src/stores/authStore.ts` — Pinia store with `username` and `password` refs (initially `null`), `isAuthenticated` computed, `setCredentials(u, p)` action, and `clearCredentials()` action (sessionStorage persistence added in Phase 5 / US3)
|
||||
|
||||
**Checkpoint**: Backend exposes `GET /api/v1/auth/check`; `authStore` is callable from any Vue component.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Authenticate to Access the App (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Unauthenticated users are redirected to `/login`; successful credential entry grants full app access.
|
||||
|
||||
**Independent Test**: Open the app in incognito → redirected to `/login`. Enter wrong credentials → error shown. Enter correct credentials → land on Library (`/`). Refresh the page → stays on Library (credentials held in-memory for now; persistence comes in US3).
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [x] T007 [US1] Create `frontend/src/views/LoginView.vue` — centered card with username input, password input, submit button, and an error message area; on submit, call `authStore.setCredentials(u, p)`, then call `GET /api/v1/auth/check` via the `api` service; on 200 navigate to `/`; on failure call `authStore.clearCredentials()` and display the error
|
||||
- [x] T008 [US1] Update `frontend/src/services/api.ts` — remove the hardcoded `auth: { username, password }` field from the axios instance; add a **request interceptor** that reads `authStore.username` and `authStore.password` and sets `config.auth` dynamically; add a **response interceptor** that on `401` calls `authStore.clearCredentials()` and redirects to `/login` (replace the existing error-normalisation interceptor rather than adding a second one — keep error normalisation intact)
|
||||
- [x] T009 [US1] Update `frontend/src/router/index.ts` — add a `{ path: '/login', name: 'login', component: LoginView }` route; add a `router.beforeEach` guard that redirects to `{ name: 'login' }` when `to.name !== 'login'` and `!authStore.isAuthenticated`
|
||||
|
||||
**Checkpoint**: US1 fully functional — incognito flow, failed login, and successful login all work independently.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Log Out of the App (Priority: P2)
|
||||
|
||||
**Goal**: Authenticated users can log out, terminating their session and returning to `/login`.
|
||||
|
||||
**Independent Test**: Log in → click "Sign out" in the navbar → redirected to `/login`; navigating back to any protected route redirects to `/login` again.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [x] T010 [US2] Update `frontend/src/App.vue` — import `useAuthStore` and `useRouter`; add a "Sign out" button to the navbar (visible only when `authStore.isAuthenticated`); clicking it calls `authStore.clearCredentials()` then `router.push({ name: 'login' })`; hide the navbar links (`RouterLink` items) when on the login page by checking `$route.name !== 'login'`
|
||||
|
||||
**Checkpoint**: US2 fully functional — logout clears session and blocks re-entry without credentials.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Session Persistence Across Browser Refresh (Priority: P3)
|
||||
|
||||
**Goal**: Authenticated users survive a page refresh without re-logging in; expired/invalid stored credentials redirect to `/login`.
|
||||
|
||||
**Independent Test**: Log in → refresh the browser → remain on the same page without re-entering credentials. Manually clear `sessionStorage` and refresh → redirected to `/login`.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [x] T011 [US3] Update `frontend/src/stores/authStore.ts` — in `setCredentials`, write `{ username, password }` to `sessionStorage` under a key (e.g. `'auth'`); in `clearCredentials`, call `sessionStorage.removeItem('auth')`; on store initialization (module load), read from `sessionStorage` and pre-populate `username` and `password` refs if present
|
||||
- [x] T012 [US3] Update `frontend/src/main.ts` — after creating the app and mounting Pinia, call `authStore.restoreSession()` (or inline the check): if `authStore.isAuthenticated`, call `GET /api/v1/auth/check`; if the response is `401`, call `authStore.clearCredentials()` so stale stored credentials are evicted before the router guard fires
|
||||
|
||||
**Checkpoint**: US3 fully functional — refresh persists login; stale or invalidated credentials are detected on load.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Documentation and cleanup that spans all user stories.
|
||||
|
||||
- [x] T013 [P] Update `frontend/.env.example` — remove `VITE_APP_PASSWORD` (the frontend no longer reads a password from env; add a comment explaining credentials are now entered via the login form)
|
||||
- [x] T014 [P] Update `README.md` — add or update the Mermaid architecture diagram to show the login flow: browser → login form → `/api/v1/auth/check` → app; this satisfies Constitution Principle IV (diagram must be updated in the same PR as any architectural change)
|
||||
|
||||
**Checkpoint**: Feature complete — all three user stories functional, documentation current, obsolete env var removed.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies — confirm immediately
|
||||
- **Foundational (Phase 2)**: Depends on Phase 1 — **BLOCKS all user stories**
|
||||
- **US1 (Phase 3)**: Depends on Phase 2 completion
|
||||
- **US2 (Phase 4)**: Depends on Phase 2 completion; integrates with authStore from Phase 2 and router from US1
|
||||
- **US3 (Phase 5)**: Depends on Phase 2 completion; extends authStore from Phase 2
|
||||
- **Polish (Phase 6)**: Depends on all desired stories complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (P1)**: Depends only on Foundational — the primary blocker for all UI work
|
||||
- **US2 (P2)**: Depends on Foundational and US1 (logout button lives in App.vue which needs the router guard from US1)
|
||||
- **US3 (P3)**: Depends on Foundational only — authStore persistence is independent of the login form; can be developed in parallel with US1 if desired
|
||||
|
||||
### Within Each User Story
|
||||
|
||||
- Foundational tasks (T003–T006) must all complete before US1 starts
|
||||
- T007 (LoginView) and T008 (api.ts) can be developed in parallel within US1; T009 (router guard) depends on T007 existing
|
||||
- US2 is a single task (T010) with no internal ordering complexity
|
||||
- T012 (main.ts restore check) depends on T011 (authStore persistence) within US3
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: Foundational Phase
|
||||
|
||||
```
|
||||
Parallelizable within Phase 2:
|
||||
T003 — application.yaml update
|
||||
T004 — SecurityConfig.java update
|
||||
T005 — AuthController.java (new file)
|
||||
T006 — authStore.ts (new file)
|
||||
All four touch different files with no shared dependencies.
|
||||
```
|
||||
|
||||
## Parallel Example: User Story 1
|
||||
|
||||
```
|
||||
Parallelizable within Phase 3:
|
||||
T007 — LoginView.vue (new file)
|
||||
T008 — api.ts update (different file)
|
||||
Then sequentially:
|
||||
T009 — router/index.ts (depends on LoginView existing)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1: Confirm existing deps
|
||||
2. Complete Phase 2: Foundational — backend endpoint + auth store
|
||||
3. Complete Phase 3: US1 — login page, axios interceptors, router guard
|
||||
4. **STOP and VALIDATE**: Open incognito, verify redirect → login → success flow
|
||||
5. Demo / merge if MVP is sufficient
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Phase 1 + Phase 2 → Foundation ready
|
||||
2. Phase 3 (US1) → Login gate works — **MVP** ✅
|
||||
3. Phase 4 (US2) → Logout works ✅
|
||||
4. Phase 5 (US3) → Session survives refresh ✅
|
||||
5. Phase 6 → Documentation and cleanup ✅
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] tasks touch different files and have no dependency on an incomplete sibling task in the same phase
|
||||
- No tests included (not requested in the spec)
|
||||
- `VITE_APP_PASSWORD` should be removed from `.env.example` once T013 is done — do **not** remove it from any local `.env` file before the login form is working (T007–T009 complete)
|
||||
- The 401 response interceptor in T008 handles the edge case where stored credentials become invalid server-side — no additional handling needed
|
||||
- Constitution IV requires the README Mermaid diagram to be updated in the **same PR** — T014 must not be skipped
|
||||
Reference in New Issue
Block a user