feat: Add complete user authentication system with JWT and mandatory login

Implemented a comprehensive authentication system requiring all users to be
logged in to access the web interface. Features include:

Backend:
- JWT-based authentication with 7-day token expiration
- bcrypt password hashing with 72-byte limit handling
- User management with JSON file storage (config/users.json)
- Pydantic models for validation (UserCreate, UserLogin, User, Token)
- Authentication endpoints: register, login, me, logout
- Protected route dependency with HTTPBearer security

Frontend:
- Login/register page with dual-tab interface (/login)
- Client-side authentication check with automatic redirect
- All content hidden by default, shown only after auth validation
- User info display with logout button
- Main content and tabs hidden when not authenticated
- Auto-redirect to /login if token missing or invalid

Security:
- Password truncation to 72 bytes (bcrypt limitation)
- Token verification on each page load
- Automatic logout and redirect on token expiry
- Username-to-SHA256 user ID generation

Dependencies:
- passlib[bcrypt]==1.7.4
- python-jose[cryptography]==3.3.0
- bcrypt<4.0

Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
root
2026-01-29 17:25:50 +00:00
parent c1c31d7685
commit ef72e221be
10 changed files with 974 additions and 14 deletions
+72
View File
@@ -0,0 +1,72 @@
{
"testuser": {
"id": "ae5deb822e0d71992900471a7199d0d9",
"username": "testuser",
"email": "test@example.com",
"full_name": "Test User",
"hashed_password": "$2b$12$gDgt6xCBS4y2FgNrCk0JU.cn8SPwrNo6vIebDSQlkfeDmvP43safy",
"is_active": true,
"created_at": "2026-01-26T11:32:14.262592",
"last_login": "2026-01-26T12:18:26.818435"
},
"apitest": {
"id": "e81cbf18a5239377aa4972773d34cc2b",
"username": "apitest",
"email": "apitest@example.com",
"full_name": "API Test User",
"hashed_password": "$2b$12$sJWQhQ0S/rMX3VJiEOMstuusfPgCvXN8zq/lCnKocL28PRomX9RJ6",
"is_active": true,
"created_at": "2026-01-26T11:32:46.943188",
"last_login": "2026-01-26T11:32:47.140656"
},
"testuser_final": {
"id": "2b4aade7e46060f88e36ae92ba767545",
"username": "testuser_final",
"email": "final@test.com",
"full_name": "Final Test User",
"hashed_password": "$2b$12$wN7Saj99c4B39O5Y2XNQ4eVuPm7o6b8eeJ1TxFrvy5.g7ycyh9rKm",
"is_active": true,
"created_at": "2026-01-26T11:33:45.726090",
"last_login": "2026-01-26T11:33:46.548491"
},
"webtest": {
"id": "2cae3fde0b88cf1274fe58ec039302cc",
"username": "webtest",
"email": null,
"full_name": null,
"hashed_password": "$2b$12$2Rr32QkYCj05GGAOQGua0umCHYRyPnvcDVXPbYaSu5SmYaohXi08a",
"is_active": true,
"created_at": "2026-01-26T11:44:09.995999",
"last_login": "2026-01-26T11:44:10.190329"
},
"roman": {
"id": "4eaae75f1df2f52bda44f6b18a400542",
"username": "roman",
"email": null,
"full_name": null,
"hashed_password": "$2b$12$IC9kz7kxf1mQPhsdveFnyOX3V5Q1.pB9/uqCKWI7nhn.SYamtvxCC",
"is_active": true,
"created_at": "2026-01-26T12:15:58.008205",
"last_login": "2026-01-29T17:23:44.242173"
},
"testuser999": {
"id": "f9abf4b8aa96d5116807ac1cf8540418",
"username": "testuser999",
"email": null,
"full_name": null,
"hashed_password": "$2b$12$y2uy62IR0xVmCcUmQ8gL6.nkvFthjyuRGxtSKh6CD5soey6T/IFu6",
"is_active": true,
"created_at": "2026-01-26T12:18:26.623497",
"last_login": null
},
"flowtest": {
"id": "4b797133389d3f5042f13aac323a8840",
"username": "flowtest",
"email": "flow@test.com",
"full_name": null,
"hashed_password": "$2b$12$Dcb7fKZPycLRsW851m9pk.1ZeyHcX65PAnb5HqLY74cJKonUfDDOC",
"is_active": true,
"created_at": "2026-01-26T12:18:50.138613",
"last_login": "2026-01-26T12:18:50.332004"
}
}