finsight1 / backend /documentation.md
Samarth Naik
init
1a2b901
# FinSight Backend API Documentation
**Generated:** February 9, 2026
**Version:** 1.0
**Framework:** Django 6.0.2 with Django REST Framework
---
## Table of Contents
1. [Overview](#overview)
2. [API Routes & Endpoints](#api-routes--endpoints)
3. [Models](#models)
4. [Views & Endpoints Detail](#views--endpoints-detail)
5. [Serializers](#serializers)
6. [Utility Functions](#utility-functions)
7. [Configuration](#configuration)
8. [Authentication](#authentication)
---
## Overview
FinSight is a Django-based REST API backend that provides user authentication, email verification via OTP, and JWT-based token management. The backend is organized into modular Django apps:
- **`core`**: Main project configuration and routing
- **`accounts`**: User authentication, registration, and OTP verification
- **`api`**: Placeholder app for future API endpoints
---
## API Routes & Endpoints
### Base URL Structure
All authentication routes are prefixed with `/api/auth/`
### Complete Endpoint List
| HTTP Method | Endpoint Path | View Class | Authentication Required | Description |
| ----------- | -------------------------- | ------------------ | ----------------------- | -------------------------------------------- |
| **POST** | `/api/auth/register/` | `RegisterView` | No | Register a new user account |
| **POST** | `/api/auth/login/` | `LoginView` | No | Login with username/email and password |
| **GET** | `/api/auth/me/` | `MeView` | Yes (JWT) | Get current authenticated user profile |
| **POST** | `/api/auth/send-otp/` | `SendOTPView` | No | Send OTP to user's email for verification |
| **POST** | `/api/auth/verify-otp/` | `VerifyOTPView` | No | Verify OTP to activate email verification |
| **POST** | `/api/auth/logout/` | `LogoutView` | Yes (JWT) | Logout and blacklist refresh token |
| **POST** | `/api/auth/token/refresh/` | `TokenRefreshView` | No | Refresh JWT access token using refresh token |
| **POST** | `/api/auth/google/` | `GoogleAuthView` | No | Authenticate with Google OAuth token |
---
## Models
### `User` Model
**Location:** `backend/accounts/models.py`
Custom user model extending Django's `AbstractBaseUser` and `PermissionsMixin`.
#### Fields
| Field Name | Type | Properties | Description |
| ------------------- | ------------- | ----------------------------------- | -------------------------------- |
| `name` | CharField | max_length=100 | User's full name |
| `username` | CharField | max_length=50, unique=True | Unique username for login |
| `email` | EmailField | - | User's email address |
| `is_email_verified` | BooleanField | default=False | Email verification status |
| `otp` | CharField | max_length=6, blank=True, null=True | Current OTP for verification |
| `otp_created_at` | DateTimeField | blank=True, null=True | Timestamp when OTP was generated |
| `is_active` | BooleanField | default=True | Account active status |
| `is_staff` | BooleanField | default=False | Staff/admin status |
| `created_at` | DateTimeField | auto_now_add=True | Account creation timestamp |
#### Configuration
- **USERNAME_FIELD:** `username`
- **REQUIRED_FIELDS:** `["email"]`
- **Manager:** `UserManager`
#### Methods
- `__str__(self)` → Returns the username as string representation
### `UserManager` Class
**Location:** `backend/accounts/models.py`
Custom manager for the User model.
#### Methods
##### `create_user(username, email, password=None, **extra_fields)`
**Purpose:** Creates and saves a regular user with hashed password
**Parameters:**
- `username` (str, required): Unique username
- `email` (str, required): User's email address
- `password` (str, optional): Plain text password (will be hashed)
- `**extra_fields`: Additional user fields
**Returns:** User instance
**Raises:**
- `ValueError`: If username or email is not provided
##### `create_superuser(username, email, password=None, **extra_fields)`
**Purpose:** Creates and saves a superuser with staff and superuser privileges
**Parameters:**
- `username` (str, required): Unique username
- `email` (str, required): User's email address
- `password` (str, optional): Plain text password (will be hashed)
- `**extra_fields`: Additional user fields
**Returns:** User instance with `is_staff=True` and `is_superuser=True`
---
## Views & Endpoints Detail
### `RegisterView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `AllowAny` (public access)
#### POST `/api/auth/register/`
**Purpose:** Register a new user account
**Request Body:**
```json
{
"name": "string (required)",
"username": "string (required, unique)",
"email": "string (required)",
"password": "string (required)"
}
```
**Success Response (201 CREATED):**
```json
{
"success": true,
"message": "User registered successfully"
}
```
**Error Response (400 BAD REQUEST):**
```json
{
"field_name": ["error message"]
}
```
**Implementation Details:**
- Uses `RegisterSerializer` for validation and user creation
- Password is automatically hashed during creation
- Email verification is NOT required at registration
---
### `LoginView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `AllowAny` (public access)
#### POST `/api/auth/login/`
**Purpose:** Authenticate user and return JWT tokens
**Request Body:**
```json
{
"identifier": "string (username or email)",
"password": "string"
}
```
**Success Response (200 OK):**
```json
{
"access": "eyJhbGc...",
"refresh": "eyJhbGc..."
}
```
**Error Responses:**
**400 BAD REQUEST:**
```json
{
"error": "Identifier and password are required"
}
```
**401 UNAUTHORIZED:**
```json
{
"error": "Invalid username/email or password"
}
```
**403 FORBIDDEN:**
```json
{
"error": "Email not verified"
}
```
**Implementation Details:**
- Accepts either username or email as identifier
- Checks password using Django's `check_password()` method
- Validates email verification status before issuing tokens
- Returns both access and refresh JWT tokens
---
### `MeView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `IsAuthenticated` (requires JWT token)
#### GET `/api/auth/me/`
**Purpose:** Retrieve current authenticated user's profile information
**Request Headers:**
```
Authorization: Bearer <access_token>
```
**Success Response (200 OK):**
```json
{
"id": 1,
"name": "John Doe",
"username": "johndoe",
"email": "john@example.com"
}
```
**Error Response (401 UNAUTHORIZED):**
```json
{
"detail": "Authentication credentials were not provided."
}
```
**Implementation Details:**
- Extracts user from `request.user` (populated by JWT authentication)
- Returns user ID, name, username, and email
- Does not expose sensitive fields like password or OTP
---
### `SendOTPView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `AllowAny` (public access)
#### POST `/api/auth/send-otp/`
**Purpose:** Generate and send a 6-digit OTP to user's email for verification
**Request Body:**
```json
{
"email": "string (required)"
}
```
**Success Response (200 OK):**
```json
{
"success": true,
"message": "OTP sent successfully"
}
```
**Error Responses:**
**400 BAD REQUEST:**
```json
{
"error": "Email is required"
}
```
**404 NOT FOUND:**
```json
{
"error": "User not found"
}
```
**Implementation Details:**
- Generates a 6-digit random OTP using `generate_otp()` utility
- Stores OTP and creation timestamp in user record
- Sends email via Django's `send_mail()` with:
- Subject: "FinSight AI - Email Verification OTP"
- Message: "Your OTP is {otp}. It is valid for 5 minutes."
- From: "no-reply@finsight.ai"
- OTP is valid for 5 minutes
---
### `VerifyOTPView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `AllowAny` (public access)
#### POST `/api/auth/verify-otp/`
**Purpose:** Verify user's email using the OTP sent via email
**Request Body:**
```json
{
"email": "string (required)",
"otp": "string (required, 6 digits)"
}
```
**Success Response (200 OK):**
```json
{
"success": true,
"message": "Email verified successfully"
}
```
**Error Responses:**
**400 BAD REQUEST (missing fields):**
```json
{
"error": "Email and OTP are required"
}
```
**400 BAD REQUEST (invalid OTP):**
```json
{
"error": "Invalid OTP"
}
```
**400 BAD REQUEST (expired OTP):**
```json
{
"error": "OTP expired"
}
```
**Implementation Details:**
- Validates OTP matches the one stored in user record
- Checks OTP expiration (5 minutes from creation)
- Sets `is_email_verified = True` on success
- Clears OTP and OTP creation timestamp after verification
- Users can login only after email is verified
---
### `LogoutView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `IsAuthenticated` (requires JWT token)
#### POST `/api/auth/logout/`
**Purpose:** Logout user by blacklisting their refresh token
**Request Headers:**
```
Authorization: Bearer <access_token>
```
**Request Body:**
```json
{
"refresh": "string (required, refresh token)"
}
```
**Success Response (200 OK):**
```json
{
"success": true,
"message": "Logged out successfully"
}
```
**Error Responses:**
**400 BAD REQUEST (missing token):**
```json
{
"error": "Refresh token is required"
}
```
**400 BAD REQUEST (invalid token):**
```json
{
"error": "Invalid or expired refresh token"
}
```
**Implementation Details:**
- Uses `rest_framework_simplejwt.token_blacklist` to blacklist tokens
- Prevents reuse of the refresh token after logout
- Requires both access token (in header) and refresh token (in body)
---
### `TokenRefreshView`
**Location:** `rest_framework_simplejwt.views` (third-party)
**Base Class:** `TokenRefreshView`
**Permission:** `AllowAny`
#### POST `/api/auth/token/refresh/`
**Purpose:** Obtain a new access token using a valid refresh token
**Request Body:**
```json
{
"refresh": "string (required, refresh token)"
}
```
**Success Response (200 OK):**
```json
{
"access": "eyJhbGc..."
}
```
**Error Response (401 UNAUTHORIZED):**
```json
{
"detail": "Token is invalid or expired",
"code": "token_not_valid"
}
```
**Implementation Details:**
- Provided by `djangorestframework-simplejwt` library
- Validates refresh token and issues new access token
- Does not issue a new refresh token (single refresh token lifecycle)
---
### `GoogleAuthView`
**Location:** `backend/accounts/views.py`
**Base Class:** `APIView`
**Permission:** `AllowAny` (public access)
#### POST `/api/auth/google/`
**Purpose:** Authenticate user using Google OAuth token and return JWT tokens
**Request Body:**
```json
{
"token": "string (required, Google OAuth ID token)"
}
```
**Success Response (200 OK):**
```json
{
"access": "eyJhbGc...",
"refresh": "eyJhbGc..."
}
```
**Error Responses:**
**400 BAD REQUEST:**
```json
{
"error": "Token is required"
}
```
**401 UNAUTHORIZED:**
```json
{
"error": "Invalid token"
}
```
**Implementation Details:**
- Verifies Google OAuth ID token using `google.oauth2.id_token`
- Extracts user information (email, name) from verified token
- Creates new user if email doesn't exist, or retrieves existing user
- Automatically sets `is_email_verified=True` for Google-authenticated users
- Returns both access and refresh JWT tokens
- Requires valid Google OAuth Client ID configured in settings
---
## Serializers
### `RegisterSerializer`
**Location:** `backend/accounts/serializers.py`
**Base Class:** `serializers.ModelSerializer`
**Purpose:** Validate and create new user accounts
**Model:** `User`
**Fields:**
- `name` (CharField)
- `username` (CharField)
- `email` (EmailField)
- `password` (CharField, write-only)
**Extra Configuration:**
```python
extra_kwargs = {
"password": {"write_only": True}
}
```
#### Methods
##### `create(validated_data)`
**Purpose:** Create a new user with hashed password
**Parameters:**
- `validated_data` (dict): Validated user data
**Returns:** User instance
**Implementation:**
- Calls `User.objects.create_user()` to ensure password is hashed
- Extracts username, email, password, and name from validated data
---
## Utility Functions
### `generate_otp()`
**Location:** `backend/accounts/utils.py`
**Purpose:** Generate a random 6-digit OTP for email verification
**Parameters:** None
**Returns:** `str` - 6-digit numeric string (e.g., "123456")
**Implementation:**
```python
return str(random.randint(100000, 999999))
```
**Usage:** Called by `SendOTPView` to create verification codes
---
## Configuration
### Django Settings
**Location:** `backend/core/settings.py`
#### Installed Apps
- `django.contrib.admin`
- `django.contrib.auth`
- `django.contrib.contenttypes`
- `django.contrib.sessions`
- `django.contrib.messages`
- `django.contrib.staticfiles`
- `rest_framework`
- `corsheaders`
- `rest_framework_simplejwt.token_blacklist`
- `accounts`
#### Database
- **Engine:** SQLite3
- **Location:** `backend/db.sqlite3`
#### Email Configuration
- **Backend:** `django.core.mail.backends.smtp.EmailBackend`
- **Host:** `smtp.gmail.com`
- **Port:** `465`
- **SSL:** `True`
- **TLS:** `False`
- **From Email:** `FinSight AI <oracleredbullracing.devsoc@gmail.com>`
#### CORS Settings
- **CORS_ALLOW_ALL_ORIGINS:** `True`
#### Custom User Model
- **AUTH_USER_MODEL:** `accounts.User`
---
## Authentication
### JWT Configuration
**Location:** `backend/core/settings.py`
#### REST Framework Settings
```python
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
}
```
#### JWT Token Settings
```python
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"AUTH_HEADER_TYPES": ("Bearer",),
}
```
**Token Lifetime:**
- **Access Token:** 15 minutes
- **Refresh Token:** 1 day
**Header Format:**
```
Authorization: Bearer <access_token>
```
### Authentication Flow
1. **Registration:**
- User registers with name, username, email, and password
- Account created but email NOT verified (`is_email_verified=False`)
2. **Email Verification:**
- User requests OTP via `/api/auth/send-otp/`
- OTP sent to registered email (valid for 5 minutes)
- User submits OTP via `/api/auth/verify-otp/`
- Email verified (`is_email_verified=True`)
3. **Login:**
- User submits username/email and password
- System checks credentials and email verification status
- Returns access and refresh tokens if successful
4. **Authenticated Requests:**
- Client includes access token in `Authorization` header
- Access token valid for 15 minutes
5. **Token Refresh:**
- Client submits refresh token to `/api/auth/token/refresh/`
- Receives new access token (refresh token valid for 1 day)
6. **Logout:**
- Client submits refresh token to `/api/auth/logout/`
- Refresh token blacklisted and cannot be reused
---
## Application Configuration Files
### WSGI Application
**Location:** `backend/core/wsgi.py`
**Purpose:** WSGI callable for production deployment
**Exposed Variable:** `application` (WSGI callable)
**Environment Variable:** `DJANGO_SETTINGS_MODULE=core.settings`
---
### ASGI Application
**Location:** `backend/core/asgi.py`
**Purpose:** ASGI callable for asynchronous deployment
**Exposed Variable:** `application` (ASGI callable)
**Environment Variable:** `DJANGO_SETTINGS_MODULE=core.settings`
---
## App Configurations
### Accounts App
**Location:** `backend/accounts/apps.py`
**Class:** `AccountsConfig`
**Name:** `accounts`
---
### API App
**Location:** `backend/api/apps.py`
**Class:** `ApiConfig`
**Name:** `api`
**Status:** Placeholder app with no models or views currently defined
---
## URL Routing Structure
### Root URLs
**Location:** `backend/core/urls.py`
```python
urlpatterns = [
path("api/auth/", include("accounts.urls")),
]
```
**Note:** Django admin is not currently exposed in URL configuration
---
### Accounts URLs
**Location:** `backend/accounts/urls.py`
All routes under `/api/auth/` prefix:
```python
urlpatterns = [
path("register/", RegisterView.as_view(), name="register"),
path("login/", LoginView.as_view(), name="login"),
path("me/", MeView.as_view(), name="me"),
path("send-otp/", SendOTPView.as_view()),
path("verify-otp/", VerifyOTPView.as_view()),
path("logout/", LogoutView.as_view(), name="logout"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("google/", GoogleAuthView.as_view(), name="google-auth"),
]
```
---
## Dependencies
**Location:** `backend/requirements.txt`
- `django`
- `djangorestframework`
- `django-cors-headers`
- `djangorestframework-simplejwt`
- `python-decouple`
- `requests`
- `google-auth`
- `google-auth-oauthlib`
---
## Notes and Considerations
1. **Email Verification Required:** Users cannot login until their email is verified via OTP
2. **OTP Expiration:** OTPs are valid for 5 minutes only
3. **Token Expiration:** Access tokens expire after 15 minutes; refresh tokens after 1 day
4. **Security:** Passwords are hashed using Django's default password hasher
5. **CORS:** Currently allows all origins (should be restricted in production)
6. **Database:** Using SQLite for development (consider PostgreSQL for production)
7. **Email:** Using Gmail SMTP (credentials exposed in settings.py - move to environment variables)
8. **Admin Panel:** Not currently configured in URLs
---
**End of Documentation**