feat: Implement comprehensive authentication system (#99)

* feat: implement JWT-based authentication system

- Add bcrypt password hashing and JWT token generation
- Create blocking auth modals for login and setup
- Add authentication management to General Settings
- Implement API routes for login, verify, setup, and credential management
- Add AuthProvider and AuthGuard components
- Support first-time setup and persistent authentication
- Store credentials securely in .env file

* feat: add option to skip enabling auth during setup

- Add toggle in SetupModal to choose whether to enable authentication immediately
- Users can set up credentials but keep authentication disabled initially
- Authentication can be enabled/disabled later through General Settings
- Maintains flexibility for users who want to configure auth gradually

* fix: allow proceeding without password when auth is disabled

- Make password fields optional when authentication is disabled in setup
- Update button validation to only require password when auth is enabled
- Modify API to handle optional password parameter
- Update hasCredentials logic to work with username-only setup
- Users can now complete setup with just username when auth is disabled
- Password can be added later when enabling authentication

* feat: don't store credentials when authentication is disabled

- When auth is disabled, no username or password is stored
- Setup modal only requires credentials when authentication is enabled
- Disabling authentication clears all stored credentials
- Users can skip authentication entirely without storing any data
- Clean separation between enabled/disabled authentication states

* feat: add setup completed flag to prevent modal on every load

- Add AUTH_SETUP_COMPLETED flag to track when user has completed setup
- Setup modal only appears when setupCompleted is false
- Both enabled and disabled auth setups mark setup as completed
- Clean .env file when authentication is disabled (no empty credential lines)
- Prevents setup modal from appearing on every page load after user decision

* fix: add missing Authentication tab button in settings modal

- Authentication tab button was missing from the tabs navigation
- Users couldn't access authentication settings
- Added Authentication tab button with proper styling and click handler
- Authentication settings are now accessible through the settings modal

* fix: properly load and display authentication settings

- Add setupCompleted state variable to track setup status
- Update loadAuthCredentials to include setupCompleted field
- Fix authentication status display logic to show correct state
- Show proper status when auth is disabled but setup is completed
- Enable toggle only when setup is completed (not just when credentials exist)
- Settings now correctly reflect the actual authentication state

* fix: handle empty FILTERS environment variable

- Add check for empty or invalid FILTERS JSON before parsing
- Prevents 'Unexpected end of JSON input' error when FILTERS is empty
- Return null filters instead of throwing parse error
- Clean up empty FILTERS line from .env file
- Fixes console error when loading settings modal

* fix: load authentication credentials when settings modal opens

- Add loadAuthCredentials() call to useEffect when modal opens
- Authentication settings were not loading because the function wasn't being called
- Now properly loads auth configuration when settings modal is opened
- Settings will display the correct authentication status and state

* fix: prevent multiple JWT secret generation with caching

- Add JWT secret caching to prevent race conditions
- Multiple API calls were generating duplicate JWT secrets
- Now caches secret after first generation/read
- Clean up duplicate JWT_SECRET lines from .env file
- Prevents .env file from being cluttered with multiple secrets

* feat: auto-login user after setup with authentication enabled

- When user sets up authentication with credentials, automatically log them in
- Prevents need to manually log in after setup completion
- Setup modal now calls login API after successful setup when auth is enabled
- AuthGuard no longer reloads page after setup, just refreshes config
- Seamless user experience from setup to authenticated state

* fix: resolve console errors and improve auth flow

- Fix 401 Unauthorized error by checking setup status before auth verification
- AuthProvider now checks if setup is completed before attempting to verify auth
- Prevents unnecessary auth verification calls when no credentials exist
- Add webpack polling configuration to fix WebSocket HMR issues
- Improves development experience when accessing from different IPs
- Eliminates console errors during initial setup flow

* fix: resolve build errors and linting issues

- Fix TypeScript ESLint error: use optional chain expression in auth.ts
- Fix React Hook warning: add missing 'isRunning' dependency to useEffect in Terminal.tsx
- Build now compiles successfully without any errors or warnings
- All linting rules are now satisfied
This commit is contained in:
Michel Roegl-Brunner
2025-10-10 12:45:45 +02:00
committed by GitHub
parent 608a7ac78c
commit 6265ffeab5
18 changed files with 1498 additions and 6 deletions

151
package-lock.json generated
View File

@@ -19,9 +19,11 @@
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0",
"@xterm/xterm": "^5.5.0",
"bcryptjs": "^3.0.2",
"better-sqlite3": "^12.4.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.545.0",
"next": "^15.5.3",
"node-pty": "^1.0.0",
@@ -42,7 +44,9 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/bcryptjs": "^2.4.6",
"@types/better-sqlite3": "^7.6.8",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^24.7.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
@@ -2986,6 +2990,13 @@
"@babel/types": "^7.28.2"
}
},
"node_modules/@types/bcryptjs": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz",
"integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/better-sqlite3": {
"version": "7.6.13",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
@@ -3043,6 +3054,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.10",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ms": "*",
"@types/node": "*"
}
},
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.1.tgz",
@@ -4259,6 +4288,15 @@
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/bcryptjs": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
"integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==",
"license": "BSD-3-Clause",
"bin": {
"bcrypt": "bin/bcrypt"
}
},
"node_modules/better-sqlite3": {
"version": "12.4.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.4.1.tgz",
@@ -4385,6 +4423,12 @@
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/cac": {
"version": "6.7.14",
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
@@ -4954,6 +4998,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.232",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.232.tgz",
@@ -7166,6 +7219,40 @@
"node": ">=6"
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"license": "MIT",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jsonwebtoken/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -7182,6 +7269,27 @@
"node": ">=4.0"
}
},
"node_modules/jwa": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"license": "MIT",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -7481,6 +7589,42 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -7488,6 +7632,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -7731,7 +7881,6 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/nan": {