Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
487
web/maplepress-frontend/docs/API/LOGIN_API.md
Normal file
487
web/maplepress-frontend/docs/API/LOGIN_API.md
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
# Login API Implementation
|
||||
|
||||
This document describes the complete implementation of the user login feature for the MaplePress frontend, integrated with the MaplePress backend API.
|
||||
|
||||
## Overview
|
||||
|
||||
The login feature allows existing users to authenticate with their email and password credentials. Upon successful login, users receive authentication tokens and are automatically logged in to their dashboard.
|
||||
|
||||
## Backend API Endpoint
|
||||
|
||||
**Endpoint**: `POST /api/v1/login`
|
||||
**Authentication**: None required (public endpoint)
|
||||
**Documentation**: `/cloud/maplepress-backend/docs/API.md` (lines 168-228)
|
||||
|
||||
### Request Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "SecurePassword123!"
|
||||
}
|
||||
```
|
||||
|
||||
### Response Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"user_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"user_email": "user@example.com",
|
||||
"user_name": "John Doe",
|
||||
"user_role": "user",
|
||||
"tenant_id": "650e8400-e29b-41d4-a716-446655440000",
|
||||
"session_id": "750e8400-e29b-41d4-a716-446655440000",
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"access_expiry": "2024-10-24T12:15:00Z",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_expiry": "2024-10-31T00:00:00Z",
|
||||
"login_at": "2024-10-24T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Key Differences from Registration
|
||||
|
||||
The login endpoint differs from registration in several ways:
|
||||
- **Simpler Request**: Only email and password required
|
||||
- **No Tenant Details in Response**: Tenant name/slug not included
|
||||
- **Login Timestamp**: Includes `login_at` instead of `created_at`
|
||||
- **Existing Session**: Authenticates existing user, doesn't create new account
|
||||
|
||||
## Frontend Implementation
|
||||
|
||||
### 1. LoginService (`src/services/API/LoginService.js`)
|
||||
|
||||
Handles direct communication with the backend login API.
|
||||
|
||||
**Key Features:**
|
||||
- Request validation (required fields)
|
||||
- Request body formatting (snake_case for backend)
|
||||
- Response transformation (camelCase for frontend)
|
||||
- User-friendly error message mapping
|
||||
- Rate limit error handling
|
||||
- Account lockout detection
|
||||
|
||||
**Methods:**
|
||||
- `login(credentials)` - Main login method
|
||||
|
||||
**Usage:**
|
||||
```javascript
|
||||
import LoginService from './services/API/LoginService';
|
||||
|
||||
const response = await LoginService.login({
|
||||
email: "user@example.com",
|
||||
password: "SecurePassword123!",
|
||||
});
|
||||
```
|
||||
|
||||
### 2. AuthManager Enhancement (`src/services/Manager/AuthManager.js`)
|
||||
|
||||
Updated to support login functionality while maintaining registration support.
|
||||
|
||||
**New/Updated Methods:**
|
||||
- `login(email, password)` - Login and store auth data
|
||||
- `storeAuthData(authResponse)` - Updated to handle optional tenant fields
|
||||
|
||||
**Key Features:**
|
||||
- Handles both registration and login responses
|
||||
- Gracefully handles missing tenant name/slug from login
|
||||
- Maintains same token storage mechanism
|
||||
- Consistent session management
|
||||
|
||||
**Login Flow:**
|
||||
```javascript
|
||||
const authManager = useAuth().authManager;
|
||||
|
||||
// Login
|
||||
await authManager.login("user@example.com", "password123");
|
||||
|
||||
// Check authentication
|
||||
if (authManager.isAuthenticated()) {
|
||||
const user = authManager.getUser();
|
||||
const tenant = authManager.getTenant();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Login Page (`src/pages/Auth/Login.jsx`)
|
||||
|
||||
Simple and clean login form ready for production use.
|
||||
|
||||
**Form Fields:**
|
||||
- Email Address (required)
|
||||
- Password (required)
|
||||
|
||||
**Features:**
|
||||
- Email validation (HTML5 + backend)
|
||||
- Loading state during submission
|
||||
- Error message display
|
||||
- Navigation to registration
|
||||
- Navigation back to home
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User enters credentials
|
||||
↓
|
||||
Login.jsx validates data
|
||||
↓
|
||||
AuthManager.login(email, password)
|
||||
↓
|
||||
LoginService.login(credentials)
|
||||
↓
|
||||
HTTP POST to /api/v1/login
|
||||
↓
|
||||
Backend validates credentials
|
||||
↓
|
||||
Backend returns tokens and user data
|
||||
↓
|
||||
LoginService transforms response
|
||||
↓
|
||||
AuthManager stores tokens in localStorage
|
||||
↓
|
||||
User redirected to /dashboard
|
||||
```
|
||||
|
||||
## Validation Rules
|
||||
|
||||
### Frontend Validation
|
||||
|
||||
1. **Email**: Required, valid email format (HTML5)
|
||||
2. **Password**: Required (no client-side length check for login)
|
||||
|
||||
### Backend Validation
|
||||
|
||||
Backend performs:
|
||||
- Email format validation
|
||||
- Email normalization (lowercase, trim)
|
||||
- Password verification against stored hash
|
||||
- Rate limiting checks
|
||||
- Account lockout checks
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Messages
|
||||
|
||||
Errors are mapped to user-friendly messages:
|
||||
|
||||
| Backend Error | Frontend Message |
|
||||
|--------------|------------------|
|
||||
| "Invalid email or password" | "Invalid email or password. Please try again." |
|
||||
| "X attempts remaining" | Original message with attempt counter |
|
||||
| "locked" or "Too many" | "Account temporarily locked due to too many failed attempts. Please try again later." |
|
||||
| "invalid email" | "Invalid email address." |
|
||||
|
||||
### Rate Limiting & Account Lockout
|
||||
|
||||
The backend implements sophisticated rate limiting:
|
||||
|
||||
**Per-IP Rate Limiting:**
|
||||
- Limit: Multiple attempts per 15 minutes
|
||||
- Response: `429 Too Many Requests`
|
||||
- Header: `Retry-After: 900` (15 minutes)
|
||||
|
||||
**Per-Account Rate Limiting:**
|
||||
- Limit: 5 failed attempts
|
||||
- Lockout: 30 minutes after 5 failures
|
||||
- Response: `429 Too Many Requests`
|
||||
- Header: `Retry-After: 1800` (30 minutes)
|
||||
- Warning: Shows remaining attempts when ≤ 3
|
||||
|
||||
**Behavior:**
|
||||
1. First 2 failures: Generic error message
|
||||
2. 3rd-5th failure: Shows remaining attempts
|
||||
3. After 5th failure: Account locked for 30 minutes
|
||||
4. Successful login: Resets all counters
|
||||
|
||||
### Security Event Logging
|
||||
|
||||
Backend logs security events for:
|
||||
- Failed login attempts (with email hash)
|
||||
- Successful logins (with email hash)
|
||||
- IP rate limit exceeded
|
||||
- Account lockouts
|
||||
|
||||
## Security Features
|
||||
|
||||
1. **Token Storage**: Tokens stored in localStorage
|
||||
2. **Token Expiry**: Automatic expiry checking
|
||||
3. **Rate Limiting**: Backend enforces rate limits
|
||||
4. **Account Lockout**: Protects against brute force
|
||||
5. **Email Normalization**: Prevents bypass via casing
|
||||
6. **Secure Logging**: PII never logged, only hashes
|
||||
7. **Password Hash Verification**: Uses bcrypt on backend
|
||||
|
||||
## Token Lifetime
|
||||
|
||||
- **Access Token**: 15 minutes
|
||||
- **Refresh Token**: 7 days
|
||||
- **Session**: 14 days (max inactivity)
|
||||
|
||||
## Testing the Login Flow
|
||||
|
||||
### 1. Prerequisites
|
||||
|
||||
Ensure you have a registered user. If not, register first:
|
||||
|
||||
```bash
|
||||
# Navigate to registration
|
||||
http://localhost:5173/register
|
||||
|
||||
# Or use curl to register via backend
|
||||
curl -X POST http://localhost:8000/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "test@example.com",
|
||||
"password": "SecurePass123!",
|
||||
"name": "Test User",
|
||||
"tenant_name": "Test Corp",
|
||||
"tenant_slug": "test-corp",
|
||||
"agree_terms_of_service": true
|
||||
}'
|
||||
```
|
||||
|
||||
### 2. Start Services
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd cloud/maplepress-backend
|
||||
task dev
|
||||
|
||||
# Frontend
|
||||
cd web/maplepress-frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 3. Navigate to Login
|
||||
|
||||
Open browser to: `http://localhost:5173/login`
|
||||
|
||||
### 4. Fill Form
|
||||
|
||||
- **Email**: test@example.com
|
||||
- **Password**: SecurePass123!
|
||||
|
||||
### 5. Submit
|
||||
|
||||
Click "Sign In" button
|
||||
|
||||
### 6. Expected Result
|
||||
|
||||
- Loading state appears ("Signing in...")
|
||||
- Request sent to backend
|
||||
- Tokens stored in localStorage
|
||||
- User redirected to `/dashboard`
|
||||
- Dashboard shows user information
|
||||
|
||||
### 7. Verify in Browser Console
|
||||
|
||||
```javascript
|
||||
// Check stored tokens
|
||||
localStorage.getItem('maplepress_access_token')
|
||||
localStorage.getItem('maplepress_user')
|
||||
localStorage.getItem('maplepress_tenant')
|
||||
|
||||
// Check service instance
|
||||
window.maplePressServices.authManager.isAuthenticated() // true
|
||||
window.maplePressServices.authManager.getUser()
|
||||
// Returns: { id: "...", email: "test@example.com", name: "Test User", role: "..." }
|
||||
|
||||
window.maplePressServices.authManager.getTenant()
|
||||
// Returns: { id: "...", name: null, slug: null }
|
||||
// Note: name/slug are null because login endpoint doesn't provide them
|
||||
```
|
||||
|
||||
## Error Testing
|
||||
|
||||
### Test Invalid Credentials
|
||||
|
||||
```javascript
|
||||
// In login form, enter:
|
||||
Email: test@example.com
|
||||
Password: WrongPassword123
|
||||
|
||||
// Expected: "Invalid email or password. Please try again."
|
||||
```
|
||||
|
||||
### Test Rate Limiting
|
||||
|
||||
```javascript
|
||||
// Attempt login 3 times with wrong password
|
||||
// Expected on 3rd attempt: "Invalid email or password. 2 attempts remaining before account lockout."
|
||||
|
||||
// Attempt 2 more times
|
||||
// Expected on 5th attempt: "Account temporarily locked due to too many failed attempts. Please try again later."
|
||||
```
|
||||
|
||||
### Test Missing Fields
|
||||
|
||||
```javascript
|
||||
// Leave email blank
|
||||
// Expected: Browser validation error (HTML5 required)
|
||||
|
||||
// Leave password blank
|
||||
// Expected: Browser validation error (HTML5 required)
|
||||
```
|
||||
|
||||
## Differences from Registration
|
||||
|
||||
| Feature | Registration | Login |
|
||||
|---------|-------------|-------|
|
||||
| Request Fields | 10+ fields | 2 fields only |
|
||||
| Response Fields | Includes tenant name/slug | No tenant name/slug |
|
||||
| Timestamp | `created_at` | `login_at` |
|
||||
| Creates User | Yes | No |
|
||||
| Creates Tenant | Yes | No |
|
||||
| Terms Agreement | Required | Not needed |
|
||||
| Organization Info | Required | Not needed |
|
||||
|
||||
## Token Storage Compatibility
|
||||
|
||||
Both registration and login use the same storage mechanism:
|
||||
|
||||
```javascript
|
||||
// Storage Keys (7 total)
|
||||
maplepress_access_token // JWT access token
|
||||
maplepress_refresh_token // JWT refresh token
|
||||
maplepress_access_expiry // ISO date string
|
||||
maplepress_refresh_expiry // ISO date string
|
||||
maplepress_user // JSON: {id, email, name, role}
|
||||
maplepress_tenant // JSON: {id, name, slug}
|
||||
maplepress_session_id // Session UUID
|
||||
```
|
||||
|
||||
**Note**: After login, `tenant.name` and `tenant.slug` will be `null`. This is expected behavior. If needed, fetch tenant details separately using the `/api/v1/tenants/{id}` endpoint.
|
||||
|
||||
## Session Persistence
|
||||
|
||||
Authentication state persists across:
|
||||
- Page refreshes
|
||||
- Browser restarts (if localStorage not cleared)
|
||||
- Tab changes
|
||||
|
||||
Session is cleared on:
|
||||
- User logout
|
||||
- Token expiry detection
|
||||
- Manual localStorage clear
|
||||
|
||||
## Integration with Dashboard
|
||||
|
||||
The dashboard automatically checks authentication:
|
||||
|
||||
```javascript
|
||||
// Dashboard.jsx
|
||||
useEffect(() => {
|
||||
if (!authManager.isAuthenticated()) {
|
||||
navigate("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
const userData = authManager.getUser();
|
||||
setUser(userData);
|
||||
}, [authManager, navigate]);
|
||||
```
|
||||
|
||||
## API Client Configuration
|
||||
|
||||
The `ApiClient` automatically handles:
|
||||
- JSON content-type headers
|
||||
- Request/response transformation
|
||||
- Error parsing
|
||||
- Authentication headers (for protected endpoints)
|
||||
|
||||
**Note**: Login endpoint doesn't require authentication headers, but subsequent API calls will use the stored access token.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
|
||||
- [ ] "Remember Me" checkbox (longer session)
|
||||
- [ ] "Forgot Password" link
|
||||
- [ ] Social authentication (Google, GitHub)
|
||||
- [ ] Two-factor authentication (2FA/TOTP)
|
||||
- [ ] Session management (view active sessions)
|
||||
- [ ] Device fingerprinting
|
||||
- [ ] Suspicious login detection
|
||||
|
||||
### Security Improvements
|
||||
|
||||
- [ ] CSRF token implementation
|
||||
- [ ] HTTP-only cookie option
|
||||
- [ ] Session fingerprinting
|
||||
- [ ] Geolocation tracking
|
||||
- [ ] Email notification on new login
|
||||
- [ ] Passwordless login option
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Invalid email or password" but credentials are correct
|
||||
|
||||
**Possible causes:**
|
||||
1. Email case sensitivity - backend normalizes to lowercase
|
||||
2. Extra whitespace in password field
|
||||
3. User not yet registered
|
||||
4. Account locked due to previous failed attempts
|
||||
|
||||
**Solution:**
|
||||
- Wait 30 minutes if account is locked
|
||||
- Try registering if user doesn't exist
|
||||
- Check browser console for detailed errors
|
||||
|
||||
### Tokens not being stored
|
||||
|
||||
**Possible causes:**
|
||||
1. localStorage disabled in browser
|
||||
2. Private/Incognito mode restrictions
|
||||
3. Browser extension blocking storage
|
||||
|
||||
**Solution:**
|
||||
- Enable localStorage in browser settings
|
||||
- Use regular browser window
|
||||
- Disable blocking extensions temporarily
|
||||
|
||||
### Redirected back to login after successful login
|
||||
|
||||
**Possible causes:**
|
||||
1. Token expiry detection triggered
|
||||
2. Token format invalid
|
||||
3. localStorage cleared between operations
|
||||
|
||||
**Solution:**
|
||||
- Check browser console for errors
|
||||
- Verify localStorage contains tokens
|
||||
- Check token expiry dates
|
||||
|
||||
## Related Files
|
||||
|
||||
### Created Files
|
||||
```
|
||||
src/services/API/LoginService.js
|
||||
```
|
||||
|
||||
### Modified Files
|
||||
```
|
||||
src/services/Manager/AuthManager.js
|
||||
src/pages/Auth/Login.jsx
|
||||
```
|
||||
|
||||
### Backend Reference Files
|
||||
```
|
||||
cloud/maplepress-backend/docs/API.md
|
||||
cloud/maplepress-backend/internal/interface/http/dto/gateway/login_dto.go
|
||||
cloud/maplepress-backend/internal/interface/http/handler/gateway/login_handler.go
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [REGISTRATION_API.md](./REGISTRATION_API.md) - Registration implementation
|
||||
- [ARCHITECTURE.md](./ARCHITECTURE.md) - Frontend architecture overview
|
||||
- [README.md](./README.md) - Getting started guide
|
||||
- [Backend API Documentation](../../cloud/maplepress-backend/docs/API.md) - Complete API reference
|
||||
|
||||
## Support
|
||||
|
||||
For issues:
|
||||
1. Check backend logs: `docker logs mapleopentech_backend`
|
||||
2. Check browser console for errors
|
||||
3. Verify backend is running on port 8000
|
||||
4. Test backend endpoint directly with curl
|
||||
5. Check rate limiting status (wait 30 minutes if locked)
|
||||
Loading…
Add table
Add a link
Reference in a new issue