| # Auth-System-SMTP β Summary of Important Questions & Concepts |
|
|
| --- |
|
|
| ## 1. Project Overview |
|
|
| **Q: What is this project?** |
| A full-stack authentication system with email verification and password reset using SMTP. Built with **FastAPI** (backend) + **React** (frontend). |
|
|
| **Q: What is the tech stack?** |
| | Layer | Technology | |
| |-------|-----------| |
| | Backend | FastAPI, SQLAlchemy, SQLite, bcrypt, JWT (python-jose) | |
| | Frontend | React 18, Vite, Axios, React Router v6 | |
| | Email | Python `smtplib` (STARTTLS on port 587) | |
| | Auth | Dual JWT tokens (access + refresh), bcrypt hashing, OTP verification | |
|
|
| --- |
|
|
| ## 2. Authentication Flow |
|
|
| **Q: What happens during registration?** |
| 1. User submits `username`, `email`, `password` |
| 2. Backend validates password strength (β₯8 chars, 1 letter, 1 digit) |
| 3. Password is hashed with bcrypt |
| 4. A 6-digit OTP is generated and stored in `verification_code` |
| 5. OTP is sent via email (SMTP or console fallback) |
| 6. User is redirected to `/verify-email` |
|
|
| **Q: How does email verification work?** |
| 1. User enters email + 6-digit OTP |
| 2. Backend matches OTP against `verification_code` column |
| 3. On success: `is_verified = True`, `verification_code = None` |
| 4. User can now log in |
|
|
| **Q: What happens during login?** |
| 1. User submits `username` + `password` (OAuth2 form body) |
| 2. Backend validates credentials with `bcrypt.checkpw()` |
| 3. Checks `is_active` and `is_verified` flags |
| 4. Issues two JWT tokens: |
| - **Access token**: 30-minute expiry, type `"access"` |
| - **Refresh token**: 7-day expiry, type `"refresh"` |
| 5. Frontend stores both in `sessionStorage` |
|
|
| **Q: How does token refresh work?** |
| 1. Axios interceptor detects a 401 response |
| 2. Sends refresh token to `/auth/refresh` |
| 3. Backend validates refresh token (not revoked, valid type, user active) |
| 4. **Revokes old refresh token** (adds to `revoked_tokens` table) |
| 5. Issues new access + refresh token pair (token rotation) |
| 6. Original request is retried with new token |
|
|
| **Q: How does logout work?** |
| 1. Frontend sends refresh token to `/auth/logout` |
| 2. Backend inserts both tokens into `revoked_tokens` table |
| 3. Frontend clears `sessionStorage` and resets user state |
|
|
| --- |
|
|
| ## 3. Password Reset Flow |
|
|
| **Q: How does forgot password work?** |
| - **Step 1**: User enters email β backend generates 6-digit OTP β stores in `reset_code` β sends via email |
| - **Step 2**: User enters email + code + new password β backend validates OTP β hashes new password β clears `reset_code` |
|
|
| --- |
|
|
| ## 4. JWT Token Structure |
|
|
| **Q: What claims are in the access token?** |
| ```json |
| { |
| "sub": "<user_id>", |
| "email": "<email>", |
| "type": "access", |
| "exp": "<datetime>" |
| } |
| ``` |
|
|
| **Q: What claims are in the refresh token?** |
| ```json |
| { |
| "sub": "<user_id>", |
| "type": "refresh", |
| "exp": "<datetime>" |
| } |
| ``` |
|
|
| **Q: Why use two token types?** |
| - Access token: Short-lived, used for API requests |
| - Refresh token: Long-lived, used only to get new access tokens |
| - Separation limits exposure if a token is compromised |
|
|
| --- |
|
|
| ## 5. Database Schema |
|
|
| **Q: What tables exist?** |
|
|
| ### `users` Table |
| | Column | Type | Purpose | |
| |--------|------|---------| |
| | `id` | Integer (PK) | User ID | |
| | `username` | String (UNIQUE) | Login username | |
| | `email` | String (UNIQUE) | User email | |
| | `password` | String | bcrypt hashed password | |
| | `is_active` | Boolean | Account enabled/disabled | |
| | `role` | String | `"user"` or `"admin"` | |
| | `created_at` | DateTime | Registration timestamp | |
| | `is_verified` | Boolean | Email verified flag | |
| | `verification_code` | String (nullable) | Active registration OTP | |
| | `reset_code` | String (nullable) | Active password reset OTP | |
|
|
| ### `revoked_tokens` Table |
| | Column | Type | Purpose | |
| |--------|------|---------| |
| | `id` | Integer (PK) | Record ID | |
| | `token` | String (UNIQUE) | Revoked JWT string | |
| | `revoked_at` | DateTime | When it was revoked | |
| | `expires_at` | DateTime | Original token expiry | |
|
|
| --- |
|
|
| ## 6. API Endpoints |
|
|
| **Q: What are all the endpoints?** |
|
|
| ### Auth Routes (`/auth`) |
| | Method | Endpoint | Description | |
| |--------|----------|-------------| |
| | POST | `/auth/register` | Register new user | |
| | POST | `/auth/verify-email` | Verify email with OTP | |
| | POST | `/auth/login` | Login, returns JWT tokens | |
| | POST | `/auth/forgot-password` | Request password reset OTP | |
| | POST | `/auth/reset-password` | Reset password with OTP | |
| | POST | `/auth/refresh` | Refresh access token | |
| | POST | `/auth/logout` | Revoke tokens | |
|
|
| ### User Routes |
| | Method | Endpoint | Description | |
| |--------|----------|-------------| |
| | GET | `/me` | Get current user profile | |
| | PUT | `/me` | Update profile | |
| | DELETE | `/me` | Delete own account | |
| | GET | `/admin/users` | List all users (admin only) | |
|
|
| --- |
|
|
| ## 7. Security Concepts |
|
|
| **Q: How are passwords stored?** |
| - Hashed with **bcrypt** using `bcrypt.gensalt()` + `bcrypt.hashpw()` |
| - Verified with `bcrypt.checkpw()` (constant-time comparison) |
|
|
| **Q: What is token revocation?** |
| - Tokens are inserted into `revoked_tokens` table |
| - Every authenticated request checks this table |
| - If token is found β 401 Unauthorized |
|
|
| **Q: What is token rotation?** |
| - On each refresh, the old refresh token is revoked |
| - A new access + refresh token pair is issued |
| - Prevents long-lived refresh token abuse |
|
|
| **Q: How is rate limiting implemented?** |
| - Custom HTTP middleware in FastAPI |
| - Max 60 requests per 60-second window per client IP |
| - Returns HTTP 429 when exceeded |
|
|
| **Q: How is CORS configured?** |
| - Allows only `http://localhost:5173` and `http://localhost:3000` |
| - Allows credentials, all methods, all headers |
|
|
| **Q: What is the SMTP fallback mechanism?** |
| - If SMTP credentials are empty/missing β prints OTP to console |
| - If SMTP sending fails β falls back to console printing |
| - Allows development without a real mail server |
|
|
| --- |
|
|
| ## 8. Frontend Architecture |
|
|
| **Q: How does the frontend manage auth state?** |
| - `AuthContext` (React Context) holds `user` state |
| - On mount, checks `sessionStorage` for `access_token` |
| - Calls `GET /me` to validate and hydrate user state |
| - If validation fails β clears session |
|
|
| **Q: How does the axios interceptor work?** |
| - **Request interceptor**: Attaches `Authorization: Bearer <token>` from `sessionStorage` |
| - **Response interceptor**: On 401 β attempts refresh β retries request β on failure redirects to `/login` |
|
|
| **Q: What is the ProtectedRoute component?** |
| - Wraps routes that require authentication |
| - Checks `user` from AuthContext |
| - Redirects to `/login` if not authenticated |
| - Shows loading spinner while checking |
|
|
| --- |
|
|
| ## 9. Environment Variables |
|
|
| | Variable | Default | Purpose | |
| |----------|---------|---------| |
| | `SECRET_KEY` | `super_secret_dev_key_change_me_123456789` | JWT signing key | |
| | `ALGORITHM` | `HS256` | JWT algorithm | |
| | `ACCESS_TOKEN_EXPIRE_MINUTES` | `30` | Access token TTL | |
| | `REFRESH_TOKEN_EXPIRE_DAYS` | `7` | Refresh token TTL | |
| | `DATABASE_URL` | `sqlite:///./users.db` | Database connection | |
| | `SMTP_SERVER` | `""` | SMTP host (empty = simulation) | |
| | `SMTP_PORT` | `587` | SMTP port | |
| | `SMTP_USER` | `""` | SMTP login (empty = simulation) | |
| | `SMTP_PASSWORD` | `""` | SMTP password (empty = simulation) | |
|
|
| --- |
|
|
| ## 10. Important Security Notes & Weaknesses |
|
|
| **Q: What security concerns exist?** |
| 1. `.env` contains real Gmail credentials β must not be committed to version control |
| 2. `SECRET_KEY` is a weak dev key β must be changed in production |
| 3. OTP generated with `random.choices` (non-cryptographic PRNG) β should use `secrets` module |
| 4. `forgot_password` endpoint may expose whether an email exists (different error responses) |
| 5. No per-account brute-force protection β only global rate limiter |
| 6. No `.gitignore` file β secrets could be accidentally committed |
|
|
| **Q: What should be improved for production?** |
| 1. Use a production database (PostgreSQL, MySQL) instead of SQLite |
| 2. Add `.gitignore` to exclude `.env`, `users.db`, `__pycache__`, `node_modules` |
| 3. Use `secrets` module for OTP generation |
| 4. Implement account lockout after failed login attempts |
| 5. Add email rate limiting (prevent OTP spam) |
| 6. Use asymmetric JWT signing (RS256) for microservice architectures |
| 7. Add HTTPS enforcement |
| 8. Implement CSRF protection |
| 9. Add comprehensive logging and monitoring |
|
|
| --- |
|
|
| ## 11. Key Files Reference |
|
|
| | File | Purpose | |
| |------|---------| |
| | `backend/main.py` | FastAPI app entry, CORS, rate limiting, middleware | |
| | `backend/auth.py` | Password hashing, JWT creation, SMTP email sending | |
| | `backend/models.py` | SQLAlchemy ORM models (User, RevokedToken) | |
| | `backend/schemas.py` | Pydantic request/response schemas | |
| | `backend/config.py` | Centralized config from `.env` | |
| | `backend/database.py` | SQLAlchemy engine, session, Base | |
| | `backend/routes/auth_routes.py` | Auth endpoints | |
| | `backend/routes/user_routes.py` | User profile + admin endpoints | |
| | `frontend/src/context/AuthContext.jsx` | Auth state management | |
| | `frontend/src/api/axios.js` | Axios instance with interceptors | |
| | `frontend/src/components/ProtectedRoute.jsx` | Route guard | |
| | `frontend/src/pages/Login.jsx` | Login form | |
| | `frontend/src/pages/Register.jsx` | Registration form | |
| | `frontend/src/pages/VerifyEmail.jsx` | OTP verification form | |
| | `frontend/src/pages/Dashboard.jsx` | User dashboard + admin panel | |
|
|