Closes#227. Auth is disabled by default (AUTH_ENABLED=false) so
localhost usage is unaffected. Set AUTH_ENABLED=true +
NEXT_PUBLIC_AUTH_ENABLED=true to require login when hosting publicly.
Backend
- New deeptutor/services/auth.py: bcrypt password hashing, JWT
create/decode, multi-user JSON store with role + created_at schema,
auto-migration of old flat-hash format, first-user → admin bootstrap
- New deeptutor/api/routers/auth.py: require_auth / require_admin
FastAPI dependencies; public endpoints /login /logout /status
/register /is_first_user; admin-only /users /users/{u}/role
- deeptutor/api/main.py: Depends(require_auth) applied to all 14
protected routers
- deeptutor/api/routers/unified_ws.py: cookie-based JWT check before
ws.accept() when AUTH_ENABLED
- Added bcrypt>=4.0.0 and python-jose[cryptography]>=3.3.0 to
requirements/server.txt and pyproject.toml extras
Frontend
- web/middleware.ts: route protection; /login and /register are public
- web/lib/api.ts: apiFetch wrapper — credentials:include + 401→login
- web/lib/auth.ts: login/logout/fetchAuthStatus + register() +
checkIsFirstUser()
- web/lib/admin-api.ts: listUsers / deleteUser / setUserRole
- web/lib/session-api.ts: credentials:include on all fetches;
expectJson redirects to /login on 401 instead of throwing
- web/app/(auth)/login/page.tsx: auto-redirects to /register when no
users exist; shows success banner after registration
- web/app/(auth)/register/page.tsx: new registration page with
first-user admin notice and password confirmation
- web/app/(admin)/admin/users/page.tsx: admin dashboard — user table
with role toggle and delete; guards against self-demotion/deletion
- AdminLink and LogoutButton hidden when AUTH_ENABLED=false
- .env.example and README.md updated with auth vars and setup guide
Made-with: Cursor