- π Authentication & Sessions
- Register β email verification (secure token link)
- Login with OAuth2 password flow
- Short-lived access JWT + long-lived refresh with rotation & JTI persistence
- Logout (revokes refresh), token versioning for global invalidation
- π Password Flows
- Forgot password: email OTP (bcrypt-hashed) β verify β short-lived reset token β set new password
- Change password: requires old password
- π€ Profile
- Update full name, update email (optional followβup reβverification)
- π§ AI Text Tools (Gemini 1.5 Flash via
google-generative-ai)- Fix Grammar, Translate, Improve, Humanize
- Protected endpoints β require
Beareraccess token
- π§© Templates
- Default templates (shared to all users,
isDefault: true) - Custom templates (per-user, CRUD)
- Default templates (shared to all users,
- π§± MongoDB (Atlas/Local)
- Async driver (Motor), strict indexes, TTL cleanup for OTP and refresh tokens
- FastAPI / Uvicorn
- Pydantic v2
- MongoDB + Motor (async)
- python-jose (JWT), passlib[bcrypt] (password/OTP hashing)
- google-generative-ai (Gemini 1.5 Flash)
- smtplib (Gmail SMTP w/ App Password) for email
app/
ββ main.py # App factory, CORS, routers, startup DB connect
ββ config.py # Settings (reads .env)
ββ database.py # Motor client, indexes, collection getters
ββ routes/
β ββ auth.py # Register, verify-email, login, refresh, logout,
β β # forgot-password, verify-otp, reset-password,
β β # change-password
β ββ actions.py # AI tools: fix-grammar, translate, improve, humanize
β ββ templates.py # Default + custom templates CRUD
ββ auth_dependencies.py # get_current_user() (Bearer token -> user)
ββ security/
β ββ crypto.py # hash/verify password + OTP with bcrypt
β ββ tokens.py # mint/validate access/refresh/reset tokens
ββ security/session_store.py # persist/rotate/revoke refresh tokens (JTI)
ββ email_service.py # Email utility (Gmail SMTP)
ββ validation.py # Validation helpers & error codes
ββ auth_models.py # Pydantic models (requests/responses/DB)
ββ exceptions.py # AuthException + typed error responses
ββ ...
git clone https://github.com/Psycho-Poodle/sahienglish-backend.git
cd sahienglish-backend
python -m venv .venv && source .venv/Scripts/activate # on Windows PowerShell: .venv\Scripts\Activate.ps1
pip install -r requirements.txtNever commit real secrets. Use placeholders locally and real secrets in deployment.
# MongoDB
MONGODB_URL=mongodb+srv://<user>:<pass>@cluster0.xxx.mongodb.net/sahienglish_db?retryWrites=true&w=majority
DATABASE_NAME=sahienglish_db
# JWT
JWT_SECRET_KEY=change-me-min-32-chars
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=30
RESET_TOKEN_EXPIRE_MINUTES=15
# Email (Gmail SMTP with App Password)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=[email protected]
SMTP_PASSWORD=your-app-password
SMTP_FROM_EMAIL=[email protected]
# App
APP_NAME=SahiEnglish
APP_URL=http://localhost:8000 # use http locally
ENVIRONMENT=development
# Security / OTP
PASSWORD_MIN_LENGTH=8
MAX_LOGIN_ATTEMPTS=5
ACCOUNT_LOCKOUT_DURATION_MINUTES=30
OTP_EXPIRE_MINUTES=10 # you can set 1 for testing (60s)
OTP_MAX_ATTEMPTS=5
# Rate limits (soft logic level)
LOGIN_RATE_LIMIT_PER_MINUTE=5
LOGIN_RATE_LIMIT_PER_HOUR=20
FORGOT_PASSWORD_RATE_LIMIT_PER_15MIN=3
FORGOT_PASSWORD_RATE_LIMIT_PER_HOUR=10
REGISTER_RATE_LIMIT_PER_HOUR=3
VERIFY_OTP_RATE_LIMIT_PER_HOUR=10
# Gemini (text tools)
GEMINI_API_KEY=your-gemini-api-keyuvicorn app.main:app --reload
# visit http://127.0.0.1:8000/docs- Register
POST /auth/registerβ creates user (inactive by default for email verification) - Verify Email
GET /auth/verify-email?token=...
Token is a short-lived signed JWT (type=verify_email). On success βis_email_verified=true. - Login
POST /auth/login(OAuth2 password form:grant_type=password):
Returns{ access_token, refresh_token, token_type="bearer", expires_in }. - Use API with
Authorization: Bearer <access_token>. - Refresh
POST /auth/refreshwithrefresh_token. Server rotates & revokes old, returns new pair. - Logout
POST /auth/logoutto revoke current refresh (server-side). - Forgot password
POST /auth/forgot-passwordβ email OTP (6 digits, bcryptβhashed in DB) - Verify OTP
POST /auth/verify-otpβ returns shortβlivedreset_token - Reset password
POST /auth/reset-passwordwithreset_token+ new password - Change password
POST /auth/change-password(requires valid access token & old password) - Profile updates
PATCH /auth/profile(e.g., change name/email)
Access tokens are shortβlived (default 15 min). Refresh tokens are longβlived (default 30 days) and stored with JTI in MongoDB for revocation/rotation. Passwords & OTPs use bcrypt via
passlib.
All require Authorization: Bearer <access_token> and call Gemini 1.5 Flash through google-generative-ai.
POST /actions/fix-grammarPOST /actions/translate(fields:text,target_language)POST /actions/improvePOST /actions/humanize
Request example
POST /actions/fix-grammar
Authorization: Bearer <access_token>
Content-Type: application/json
{ "text": "i has a apple, it was red" }Response
{ "result": "I have an apple; it was red." }Two kinds of templates live in the single templates collection:
- Default (system) templates:
{ isDefault: true }β visible to all users - Custom templates:
{ isDefault: false, userId: <ObjectId> }β only visible to the owner
Endpoints (require auth):
GET /templates/β lists (default βͺ userβs custom) templatesPOST /templates/β create custom templatePUT /templates/{id}β update your custom templateDELETE /templates/{id}β delete your custom template
You can insert a few defaults directly or via a small script. Example docs:
[
{
"title": "Professional Email (General)",
"category": "email",
"content": "Dear Hiring Manager,\n\nIβm writing to express my interest in the [Position] role at [Company Name]. ...\n\nBest regards,\n[Your Name]",
"isDefault": true
},
{
"title": "Job Application β Short",
"category": "job-application",
"content": "Dear [Hiring Manager],\n\nPlease find my application for [Role] attached. ...\n\nSincerely,\n[Your Name]",
"isDefault": true
},
{
"title": "Meeting Request",
"category": "email",
"content": "Hi [Name],\n\nCould we schedule a quick call to discuss [topic]? ...\n\nThanks,\n[Your Name]",
"isDefault": true
},
{
"title": "Wedding Invitation",
"category": "invitation",
"content": "Dear [Guest],\n\nYouβre cordially invited to celebrate our wedding on [Date] at [Venue]. ...\n\nWith love,\n[Names]",
"isDefault": true
},
{
"title": "ThankβYou Email",
"category": "email",
"content": "Hello [Name],\n\nThank you for your time today. I appreciated learning more about [topic]. ...\n\nBest,\n[Your Name]",
"isDefault": true
}
]If you want a helper script, create
app/scripts/seed_templates.pythat inserts the JSON above withisDefault: trueand run:python -m app.scripts.seed_templates
- Mongo connectivity check:
python -m app.test_connection - Session store smoke test:
python -m app.scratch_session_check - Email test (Gmail SMTP):
python -m app.scratch_send_email
Open http://127.0.0.1:8000/docs β click Authorize β use OAuth2PasswordBearer:
- username: your account email
- password: your account password
- No client id/secret is used (password grant), just βAuthorizeβ, then try protected routes.
- Never log raw passwords/OTPs or tokens.
- bcrypt for password & OTP hashing.
- Refresh tokens stored serverβside with JTI & TTL, rotated on each refresh.
token_versionincrements can invalidate all previous tokens for a user.- CORS is open by default for local testing β restrict in production.
- Use a real domain + HTTPS for
APP_URLin production (affects email links). - Store secrets in a secure vault (not in
.envcommitted to git). - Set strong
JWT_SECRET_KEY(β₯ 32 chars) and rotate periodically. - Use a dedicated email sender account with Gmail App Password or a transactional provider.