Updated Auth

This commit is contained in:
Chandresh Kerkar 2025-11-30 01:28:58 +05:30
parent 9dd376df76
commit 22ed0bfeb3
8 changed files with 1528 additions and 408 deletions

48
.env.example Normal file
View File

@ -0,0 +1,48 @@
# ============================================
# REQUIRED ENVIRONMENT VARIABLES
# ============================================
# Copy this file to .env and fill in your values
# Database Connection (PostgreSQL)
DATABASE_URL=postgres://username:password@localhost:5432/database_name
# JWT Secrets (use strong random strings)
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
JWT_ACCESS_SECRET=your-access-token-secret-here-minimum-32-characters
JWT_REFRESH_SECRET=your-refresh-token-secret-here-minimum-32-characters
# ============================================
# OPTIONAL ENVIRONMENT VARIABLES
# ============================================
# Server Configuration
PORT=3000
NODE_ENV=development
# CORS Configuration (comma-separated list, required in production)
# Example: https://yourdomain.com,https://www.yourdomain.com
CORS_ALLOWED_ORIGINS=
# JWT Token Expiration (default values shown)
JWT_ACCESS_TTL=15m
JWT_REFRESH_TTL=7d
# Refresh Token Inactivity Timeout (in minutes, default: 4320 = 3 days)
REFRESH_MAX_IDLE_MINUTES=4320
# OTP Configuration
OTP_MAX_ATTEMPTS=5
# ============================================
# TWILIO SMS CONFIGURATION (Optional)
# ============================================
# Required for sending OTP via SMS
# If not configured, OTP will be logged to console in development
TWILIO_ACCOUNT_SID=your-twilio-account-sid
TWILIO_AUTH_TOKEN=your-twilio-auth-token
# Use either TWILIO_MESSAGING_SERVICE_SID (recommended) OR TWILIO_FROM_NUMBER
TWILIO_MESSAGING_SERVICE_SID=your-messaging-service-sid
# OR
TWILIO_FROM_NUMBER=+1234567890

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ build/
# Temporary files
*.tmp
*.temp

File diff suppressed because it is too large Load Diff

92
SETUP.md Normal file
View File

@ -0,0 +1,92 @@
# Environment Variables Setup
## Required Variables (MUST provide)
These are **mandatory** - the service will not start without them:
```env
DATABASE_URL=postgres://username:password@localhost:5432/database_name
JWT_ACCESS_SECRET=your-secret-here-minimum-32-characters
JWT_REFRESH_SECRET=your-secret-here-minimum-32-characters
```
### How to generate JWT secrets:
```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
Run this twice to get two different secrets.
---
## Optional Variables (Can skip)
### Twilio SMS Configuration
**You DO NOT need to provide Twilio credentials** - the service will work without them!
If Twilio is **NOT configured**:
- ✅ Service starts normally
- ✅ OTP codes are logged to console for testing
- ⚠️ SMS will not be sent (OTP shown in server logs)
If Twilio **IS configured**:
- ✅ OTP codes sent via SMS automatically
```env
# Twilio (Optional - only if you want SMS delivery)
TWILIO_ACCOUNT_SID=your-twilio-account-sid
TWILIO_AUTH_TOKEN=your-twilio-auth-token
TWILIO_MESSAGING_SERVICE_SID=your-messaging-service-sid
# OR
TWILIO_FROM_NUMBER=+1234567890
```
### Other Optional Variables
```env
PORT=3000 # Server port (default: 3000)
NODE_ENV=development # Environment (development/production)
CORS_ALLOWED_ORIGINS= # Comma-separated origins (required in production)
JWT_ACCESS_TTL=15m # Access token expiry (default: 15m)
JWT_REFRESH_TTL=7d # Refresh token expiry (default: 7d)
REFRESH_MAX_IDLE_MINUTES=4320 # Refresh token inactivity timeout (default: 3 days)
OTP_MAX_ATTEMPTS=5 # Max OTP verification attempts (default: 5)
```
---
## Quick Setup
1. **Copy the example file:**
```bash
cp .env.example .env
```
2. **Fill in REQUIRED variables only:**
```env
DATABASE_URL=postgres://postgres:password123@localhost:5433/farmmarket
JWT_ACCESS_SECRET=<generate-with-node-command>
JWT_REFRESH_SECRET=<generate-with-node-command>
```
3. **Skip Twilio** (optional - for development, OTP will show in console)
4. **Start the service:**
```bash
npm run dev
```
---
## Testing Without Twilio
When Twilio is not configured:
- Request OTP: `POST /auth/request-otp`
- Check server console - OTP code will be logged: `📱 DEBUG OTP: +919876543210 Code: 123456`
- Use that code to verify: `POST /auth/verify-otp`
This is perfect for local development!

201
TWILIO_SETUP.md Normal file
View File

@ -0,0 +1,201 @@
# Twilio SMS Setup
## Required Twilio Variables
Add these to your `.env` file:
```env
# Twilio SMS Configuration
TWILIO_ACCOUNT_SID=your-account-sid-here
TWILIO_AUTH_TOKEN=your-auth-token-here
# Use EITHER Messaging Service (recommended) OR From Number
TWILIO_MESSAGING_SERVICE_SID=your-messaging-service-sid
# OR
TWILIO_FROM_NUMBER=+1234567890
```
## Twilio Setup Options
### Option 1: Using Messaging Service (Recommended)
```env
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_MESSAGING_SERVICE_SID=MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### Option 2: Using Phone Number
```env
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_FROM_NUMBER=+1234567890
```
## How to Get Twilio Credentials
1. Sign up at https://www.twilio.com/
2. Get your Account SID and Auth Token from the Twilio Console Dashboard
3. For Messaging Service:
- Go to Messaging > Services in Twilio Console
- Create or select a Messaging Service
- Copy the Service SID (starts with MG)
4. For Phone Number:
- Get a Twilio phone number from Phone Numbers section
- Format: +1234567890 (E.164 format)
## Important Notes
- Use **Messaging Service** (Option 1) if you have one - it's recommended
- Use **Phone Number** (Option 2) if you don't have a Messaging Service
- You only need **ONE** of: `TWILIO_MESSAGING_SERVICE_SID` or `TWILIO_FROM_NUMBER`
- The phone number must be in E.164 format: `+[country code][number]`
- Example: `+919876543210` (India)
## Testing
After adding Twilio credentials:
1. Restart your server: `npm run dev`
2. Request OTP: `POST /auth/request-otp`
3. Check server logs - should see: `✅ Twilio SMS sent, SID: SMxxxxx`
4. User receives SMS with OTP code
---
## Troubleshooting Common Errors
### Error: "Short Code" Error
**Error Message:**
```
❌ Failed to send SMS via Twilio: 'To' number cannot be a Short Code: +9174114XXXX
```
**Cause:**
- You're trying to send SMS to a short code (5-6 digit number)
- Twilio doesn't allow sending to short codes
- Often happens with test numbers or invalid phone numbers
**Solution:**
- Use valid full-length phone numbers in E.164 format (e.g., `+919876543210`)
- Short codes are typically 5-6 digits total
- The service now validates phone numbers and rejects short codes
**Fallback:**
- OTP is automatically logged to console: `📱 DEBUG OTP (fallback): +91741147986 Code: 153502`
- Check server logs for the OTP code during development/testing
---
### Error: "Unverified Number" Error (Trial Account)
**Error Message:**
```
❌ Failed to send SMS via Twilio: The number +91741114XXXX is unverified. Trial accounts cannot send messages to unverified numbers
```
**Cause:**
- You're using a **Twilio Trial Account**
- Trial accounts can only send SMS to verified phone numbers
- This is a security feature to prevent abuse
**Solutions:**
**Option 1: Verify the Phone Number (Free)**
1. Go to [Twilio Console - Verified Numbers](https://console.twilio.com/us1/develop/phone-numbers/manage/verified)
2. Click "Add a new number"
3. Enter the phone number and verify via SMS/call
4. You can verify up to 10 numbers on a trial account
**Option 2: Upgrade to Paid Account (Recommended for Production)**
1. Add payment method in Twilio Console
2. Upgrade your account (minimum $20 credit required)
3. Once upgraded, you can send SMS to any valid phone number
4. Pay only for messages sent (per SMS pricing)
**Option 3: Use Console Logs (Development Only)**
- For testing/development, check server console logs
- OTP codes are logged: `📱 DEBUG OTP (fallback): +919876543210 Code: 123456`
- This allows testing without SMS delivery
---
### Error: "Failed to send OTP"
**Error Message:**
```
❌ Failed to send SMS via Twilio: [any error]
```
**General Troubleshooting:**
1. **Check Twilio Credentials:**
- Verify `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` in `.env`
- Credentials should start with `AC` and be valid
2. **Check From Number/Service:**
- Ensure `TWILIO_FROM_NUMBER` or `TWILIO_MESSAGING_SERVICE_SID` is set
- Phone number must be in E.164 format: `+1234567890`
3. **Check Account Status:**
- Login to [Twilio Console](https://console.twilio.com/)
- Verify account is active (not suspended)
- Check if you have credits/balance
4. **Check Phone Number Format:**
- Must be E.164 format: `+[country code][number]`
- Example: `+919876543210` (India), `+1234567890` (US)
- No spaces or special characters
5. **Development Fallback:**
- If SMS fails, OTP is always logged to console
- Check server logs: `📱 DEBUG OTP (fallback): [phone] Code: [otp]`
---
## Understanding the Logs
**✅ Success:**
```
✅ Twilio SMS sent, SID: SMa7e2f23b3bdb05be1a275f43b71b2209
```
- SMS was successfully sent via Twilio
- User should receive SMS with OTP
**❌ Failure (with fallback):**
```
❌ Failed to send SMS via Twilio: [error message]
📱 DEBUG OTP (fallback): +919876543210 Code: 123456
```
- SMS sending failed, but OTP was generated
- OTP code is logged to console for development/testing
- User can still verify using the OTP code from logs
**⚠️ Warning (Twilio not configured):**
```
⚠️ Twilio credentials are not set. SMS sending will be disabled. OTP will be logged to console.
📱 DEBUG OTP (Twilio not configured): +919876543210 Code: 123456
```
- Twilio credentials missing in `.env`
- Service still works, but OTPs only appear in logs
- Add Twilio credentials to enable SMS delivery
---
## Best Practices
1. **Development:**
- Use console logs for testing
- Verify test numbers in Twilio Console
2. **Production:**
- Upgrade to paid Twilio account
- Use Messaging Service (recommended)
- Monitor SMS delivery rates
- Implement proper error handling in frontend
3. **Security:**
- Never log OTPs in production
- Use environment variables for credentials
- Rotate Twilio credentials periodically

View File

@ -16,13 +16,32 @@ const router = express.Router();
// helper: normalize phone
function normalizePhone(phone) {
const p = phone.trim();
const p = phone.trim().replace(/\s+/g, ''); // Remove spaces
if (p.startsWith('+')) return p;
// if user enters 10-digit, prepend +91
if (p.length === 10) return '+91' + p;
return p; // fallback
}
// helper: validate phone number format
function isValidPhoneNumber(phone) {
// E.164 format: + followed by 1-15 digits
// Reject short codes (5-6 digits)
const e164Pattern = /^\+[1-9]\d{1,14}$/;
if (!e164Pattern.test(phone)) {
return false;
}
// Reject short codes (typically 5-6 digits total)
const digitsOnly = phone.replace(/\D/g, '');
if (digitsOnly.length <= 6) {
return false; // Likely a short code
}
return true;
}
function sanitizeDeviceId(deviceId) {
if (!deviceId || typeof deviceId !== 'string') {
@ -50,16 +69,28 @@ router.post('/request-otp', async (req, res) => {
const normalizedPhone = normalizePhone(phone_number);
// Validate phone number format
if (!isValidPhoneNumber(normalizedPhone)) {
return res.status(400).json({
error: 'Invalid phone number format. Please use E.164 format (e.g., +919876543210)'
});
}
// TODO: rate limiting per phone / IP
const { code } = await createOtp(normalizedPhone);
try {
await sendOtpSms(normalizedPhone, code);
} catch (err) {
console.error('Failed to send OTP SMS via Twilio', err);
// you can choose whether to fail here or still return ok
return res.status(500).json({ error: 'Failed to send OTP' });
// Attempt to send SMS (will fallback to console log if Twilio fails)
const smsResult = await sendOtpSms(normalizedPhone, code);
// Even if SMS fails, we still return success because OTP is generated
// The OTP code is logged to console for testing/development
// In production, you may want to return an error if SMS fails
if (!smsResult || !smsResult.success) {
console.warn('⚠️ SMS sending failed, but OTP was generated and logged to console');
// Option 1: Still return success (current behavior - allows testing)
// Option 2: Return error (uncomment below for production)
// return res.status(500).json({ error: 'Failed to send OTP via SMS' });
}
return res.json({ ok: true });

View File

@ -8,8 +8,9 @@ const router = express.Router();
// GET /users/me
router.get('/me', auth, async (req, res) => {
try {
// Get user basic information
const { rows } = await db.query(
`SELECT id, phone_number, name, role, user_type, created_at, last_login_at
`SELECT id, phone_number, name, role, user_type, created_at, last_login_at, avatar_url, language, timezone
FROM users
WHERE id = $1`,
[req.user.id]
@ -28,9 +29,65 @@ router.get('/me', auth, async (req, res) => {
);
const activeDevicesCount = parseInt(deviceCountResult.rows[0].count, 10);
// Get user's saved locations (addresses)
const locationsResult = await db.query(
`SELECT
id,
country,
state,
district,
city_village,
pincode,
lat,
lng,
location_type,
is_saved_address,
source_type,
source_confidence,
created_at,
updated_at
FROM locations
WHERE user_id = $1 AND is_saved_address = true
ORDER BY updated_at DESC`,
[req.user.id]
);
const locations = locationsResult.rows.map(loc => ({
id: loc.id,
country: loc.country,
state: loc.state,
district: loc.district,
city_village: loc.city_village,
pincode: loc.pincode,
coordinates: loc.lat && loc.lng ? {
latitude: parseFloat(loc.lat),
longitude: parseFloat(loc.lng)
} : null,
location_type: loc.location_type,
is_saved_address: loc.is_saved_address,
source_type: loc.source_type,
source_confidence: loc.source_confidence,
created_at: loc.created_at,
updated_at: loc.updated_at
}));
// Get primary location (most recent saved address, or first if none)
const primaryLocation = locations.length > 0 ? locations[0] : null;
return res.json({
...user,
id: user.id,
phone_number: user.phone_number,
name: user.name,
role: user.role,
user_type: user.user_type,
avatar_url: user.avatar_url,
language: user.language,
timezone: user.timezone,
created_at: user.created_at,
last_login_at: user.last_login_at,
active_devices_count: activeDevicesCount,
location: primaryLocation, // Primary/saved location for convenience
locations: locations // All saved locations
});
} catch (err) {
console.error('get me error', err);

View File

@ -8,11 +8,13 @@ const {
TWILIO_MESSAGING_SERVICE_SID,
} = process.env;
if (!TWILIO_ACCOUNT_SID || !TWILIO_AUTH_TOKEN) {
console.warn('⚠️ Twilio credentials are not set. SMS sending will fail.');
}
let client = null;
const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
if (TWILIO_ACCOUNT_SID && TWILIO_AUTH_TOKEN) {
client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
} else {
console.warn('⚠️ Twilio credentials are not set. SMS sending will be disabled. OTP will be logged to console.');
}
/**
* Send OTP SMS using Twilio
@ -20,12 +22,12 @@ const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
* @param {string} code OTP code
*/
async function sendOtpSms(toPhone, code) {
if (!TWILIO_ACCOUNT_SID || !TWILIO_AUTH_TOKEN) {
console.log('DEBUG OTP (no Twilio configured):', toPhone, code);
if (!client) {
console.log('📱 DEBUG OTP (Twilio not configured):', toPhone, 'Code:', code);
return;
}
const messageBody = `Your verification code is ${code}. It will expire in 5 minutes.`;
const messageBody = `Your verification code is ${code}. It will expire in 10 minutes.`;
const msgConfig = {
body: messageBody,
@ -38,11 +40,34 @@ async function sendOtpSms(toPhone, code) {
} else if (TWILIO_FROM_NUMBER) {
msgConfig.from = TWILIO_FROM_NUMBER;
} else {
throw new Error('Neither TWILIO_MESSAGING_SERVICE_SID nor TWILIO_FROM_NUMBER is set');
console.warn('⚠️ Neither TWILIO_MESSAGING_SERVICE_SID nor TWILIO_FROM_NUMBER is set. OTP logged to console.');
console.log('📱 DEBUG OTP:', toPhone, 'Code:', code);
return;
}
try {
const msg = await client.messages.create(msgConfig);
console.log('Twilio SMS sent, SID:', msg.sid);
console.log('✅ Twilio SMS sent, SID:', msg.sid);
return { success: true, sid: msg.sid };
} catch (err) {
const errorMessage = err.message || 'Unknown error';
console.error('❌ Failed to send SMS via Twilio:', errorMessage);
// Check for specific Twilio error types
const isShortCodeError = errorMessage.includes('Short Code');
const isUnverifiedNumberError = errorMessage.includes('unverified');
const isTrialAccountError = errorMessage.includes('Trial account');
if (isShortCodeError) {
console.warn('⚠️ Cannot send to short codes (5-6 digit numbers). OTP logged to console for testing.');
} else if (isUnverifiedNumberError || isTrialAccountError) {
console.warn('⚠️ Trial account limitation: Verify the number at https://console.twilio.com/us1/develop/phone-numbers/manage/verified');
console.warn('⚠️ Or upgrade to a paid account to send to any number.');
}
console.log('📱 DEBUG OTP (fallback):', toPhone, 'Code:', code);
return { success: false, error: errorMessage };
}
}
module.exports = {