feat: add optional authentication with login, registration, and admin dashboard#252
feat: add optional authentication with login, registration, and admin dashboard#252
Conversation
|
Thanks for your proposal. This is something we wanna DeepTutor have in the future. |
|
Understood, thanks, I would advise taking PocketBase in consideration too in case you decide on expanding on this feature from my experience its quite reliable, lightweight and has nice authentication module, the codebase would need refactoring though especially the data storage part |
Yeah please go ahead and updating anything that comes in your mind. I'll review this pr several days later. Thanks! |
Closes HKUDS#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
Introduces PocketBase as an optional sidecar for authentication and session/KB storage, activated only when POCKETBASE_URL is set in .env. Falls back to the existing SQLite/JSON backend when not configured. Backend: - SessionStoreProtocol (typing.Protocol) + get_session_store() factory - PocketBaseSessionStore: JSONL write-ahead buffer, batch-flush on turn end - pocketbase_client.py: singleton admin client, 60s in-memory token cache - auth.py: additive PocketBase path (email-based login/register) - CORS: explicit origins instead of wildcard (wildcard+credentials fails) - WebSocket: validate token at connect time when AUTH_ENABLED Infrastructure: - docker-compose: pocketbase service with healthcheck + depends_on - scripts/pb_setup.py: idempotent PocketBase collection bootstrap - requirements/server.txt: pocketbase>=0.12.0 Frontend: - Register/login: username field -> email field for PocketBase mode - auth.ts: normalise FastAPI 422 errors to plain strings - agents page: guard bots.map() against non-array responses - settings page: graceful fetch error handling - *.env.local added to .gitignore Docs: README PocketBase sidecar setup section added
6814332 to
859f560
Compare
|
In case you choose to commence with this feature, it will be optional for users to run PocketBase. If a user chooses not to, they will still have the option to use simple Auth (the one I provided before) or no Auth at all. Should a user choose to run PocketBase, however, they will benefit from highly scalable multi-user concurrency, despite a slight increase in non-streaming transactions. This also opens up future possibility easier OAuth integration and more sophisticated user management through PocketBase. Additionally, data could potentially be shared across multiple DeepTutor instances via a shared PocketBase instance. It still needs testing in terms of AI features. If you have any questions, please let me know. |
Summary
Closes #227.
Implements optional authentication for public deployments. Auth is disabled by default (
AUTH_ENABLED=false), so all existing localhost users are completely unaffected. Enabling it requires two env vars (AUTH_ENABLED=true+NEXT_PUBLIC_AUTH_ENABLED=true).python-jose), file-based multi-user store (data/user/auth_users.json) with role/created_at schema,require_auth/require_adminFastAPI dependencies applied to all 14 protected routers and the WebSocket endpoint, new public endpoints (/register,/is_first_user) and admin-only endpoints (/users,/users/{u}/role)/registerwhen no users exist, registration page with first-user admin notice,/admin/usersdashboard for role management and deletion,AdminLinkandLogoutButtonhidden when auth is disabled,credentials: includeadded to all API fetches,expectJsonredirects to/loginon 401 instead of throwing.env.exampleandREADME.mdupdated with auth variables and setup guideFirst-time setup (when auth is enabled)
AUTH_ENABLED=trueandNEXT_PUBLIC_AUTH_ENABLED=true/register— the first user automatically becomes admin/admin/usersSingle-user alternative: set
AUTH_USERNAME+AUTH_PASSWORD_HASHin.env(no registration needed).Test plan
AUTH_ENABLED=false(default): app loads normally, no login prompt, no logout/admin buttons visibleAUTH_ENABLED=trueand no users: visiting/redirects to/registeruserroledt_tokencookie and redirects to app/admin/userscan promote/demote and delete other users (not themselves)/admin/users(redirected)/loginMade with Cursor