Authentication System
Complete authentication with JWT tokens, Google OAuth, and password reset.
Features
Email/Password Authentication
- JWT token-based auth
- Bcrypt password hashing
- HTTP-only cookie storage
- 7-day token expiration (configurable)
Google OAuth
- One-click Google sign-in
- Automatic account creation
- Secure OAuth 2.0 flow
Password Reset
- Email-based reset flow
- Secure token generation
- 1-hour token expiration
- Email notifications
Session Management
- Automatic token refresh
- Secure logout
- Protected routes
- Authentication persistence
Quick Start
Create First User
| Bash |
|---|
| # Create superuser (Top G+)
task db:user-create -- \
--email [email protected] \
--password securepass123 \
--full_name "John Doe"
# Or use signup page
# Visit: http://localhost:5173/signup
|
Test Authentication
| Bash |
|---|
| # Login via API
curl -X POST http://localhost:8020/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"securepass123"}'
# Returns JWT token
|
Configuration
Environment Variables
| Text Only |
|---|
| # local.env
# JWT Configuration
secret_key=your-secret-key-min-32-chars
algorithm=HS256
access_token_expire_minutes=10080 # 7 days
# Google OAuth (optional)
google_oauth2_client_id=your-client-id
google_oauth2_secret=your-secret
google_oauth2_redirect_uri=http://localhost:8020/api/auth/google_callback
|
Google OAuth Setup
- Go to Google Cloud Console
- Create project
- Enable Google+ API
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
http://localhost:8020/api/auth/google_callback
- Copy Client ID and Secret to
local.env
Backend Implementation
Controllers
Login (POST /api/auth/login):
| Python |
|---|
| # app/controllers/auth.py
@auth_router.post("/login")
async def login(form_data: LoginForm):
user = await user_service.login(form_data.email, form_data.password)
# Returns JWT token in cookie
|
Signup (POST /api/auth/signup):
| Python |
|---|
| @auth_router.post("/signup")
async def signup(form_data: SignupForm):
user = await user_service.create_user(form_data)
# Auto-login after signup
|
Google OAuth (GET /api/auth/google/authorize):
| Python |
|---|
| @auth_router.get("/google/authorize")
async def google_authorize():
# Redirects to Google OAuth
|
Services
User Authentication (app/services/users_service.py):
| Python |
|---|
| class UserService:
async def authenticate_user(self, email: str, password: str):
user = await self.get_user_by_email(email)
if not user or not self.verify_password(password, user.password_hash):
raise HTTPException(401, "Invalid credentials")
return user
async def login(self, email: str, password: str):
user = await self.authenticate_user(email, password)
token = self.create_access_token({"sub": str(user.id)})
return {"access_token": token}
|
Frontend Implementation
Login Page
| TypeScript |
|---|
| // frontend/src/pages/Login.tsx
import { useLogin } from '@/hooks/api/useAuth';
const Login = () => {
const login = useLogin();
const handleSubmit = (data: LoginForm) => {
login.mutate(data, {
onSuccess: () => navigate('/dashboard')
});
};
return <LoginForm onSubmit={handleSubmit} />;
};
|
Protected Routes
| TypeScript |
|---|
| // frontend/src/components/ProtectedRoute.tsx
const ProtectedRoute = () => {
const { data: user, isLoading } = useCurrentUser();
if (isLoading) return <Loading />;
if (!user) return <Navigate to="/login" />;
return <Outlet />;
};
|
| TypeScript |
|---|
| const GoogleLogin = () => {
const handleGoogleLogin = () => {
window.location.href = 'http://localhost:8020/api/auth/google/authorize';
};
return <Button onClick={handleGoogleLogin}>Sign in with Google</Button>;
};
|
Security
Password Requirements
- Minimum 8 characters (configurable)
- Bcrypt hashing with salt
- Never stored in plain text
JWT Tokens
- Signed with secret key
- Stored in HTTP-only cookies
- Not accessible via JavaScript
- Automatic expiration
OAuth Security
- State parameter validation
- PKCE flow support
- Secure token exchange
Common Issues
Problem: Google OAuth redirect fails
Solution: Verify redirect URI matches exactly:
| Text Only |
|---|
| google_oauth2_redirect_uri=http://localhost:8020/api/auth/google_callback
|
Problem: JWT token expired
Solution: User must login again, or implement refresh token
Problem: Password reset email not sending
Solution: Configure Mailchimp API key in local.env
API Endpoints
POST /api/auth/login - Email/password login
POST /api/auth/signup - Create account
GET /api/auth/logout - Logout user
GET /api/auth/current - Get current user
GET /api/auth/google/authorize - Google OAuth start
GET /api/auth/google_callback - Google OAuth callback
POST /api/auth/forgot-password - Request password reset
POST /api/auth/reset-password - Reset password with token
PUT /api/auth/profile - Update user profile
Full API reference
Files Reference
app/controllers/auth.py - Auth endpoints
app/services/users_service.py - User logic
app/services/oauth_service.py - OAuth logic
app/auth_backend.py - JWT handling
frontend/src/pages/Login.tsx - Login page
frontend/src/pages/SignUp.tsx - Signup page
frontend/src/hooks/api/useAuth.ts - Auth hooks