fixed rout
This commit is contained in:
426
BACKEND_IMPLEMENTATION.md
Normal file
426
BACKEND_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# FastCheck Backend Implementation Guide
|
||||
|
||||
## QR Code Authentication Flow
|
||||
|
||||
### Overview
|
||||
The frontend displays a QR code that contains a session ID. When a user scans this QR code with the mobile app, the mobile app authenticates and links to that session. The frontend polls the backend every 2 seconds to check if the session has been authenticated.
|
||||
|
||||
### Step-by-Step Implementation
|
||||
|
||||
---
|
||||
|
||||
## 1. Create WebSession (QR Code Generation)
|
||||
|
||||
### Frontend Request:
|
||||
```typescript
|
||||
GET https://api.fastcheck.store/websession
|
||||
Headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Response:
|
||||
```json
|
||||
{
|
||||
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
||||
"userId": "",
|
||||
"expires": "2026-01-19T10:50:00Z",
|
||||
"userSessionId": "",
|
||||
"Status": false
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Implementation:
|
||||
```javascript
|
||||
// Example Node.js/Express
|
||||
app.get('/websession', (req, res) => {
|
||||
// Generate unique session ID (UUID or similar)
|
||||
const sessionId = generateUUID(); // e.g., "1AF3781BF6B94604B771AEA1D44FA63A"
|
||||
|
||||
// Set expiration time (e.g., 5 minutes from now)
|
||||
const expires = new Date(Date.now() + 5 * 60 * 1000).toISOString();
|
||||
|
||||
// Store session in database or cache (Redis recommended)
|
||||
await sessionStore.create({
|
||||
sessionId: sessionId,
|
||||
userId: null,
|
||||
userSessionId: null,
|
||||
status: false,
|
||||
expiresAt: expires,
|
||||
createdAt: new Date()
|
||||
});
|
||||
|
||||
// Return session data
|
||||
res.json({
|
||||
sessionId: sessionId,
|
||||
userId: "",
|
||||
expires: expires,
|
||||
userSessionId: "",
|
||||
Status: false
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### What Frontend Does:
|
||||
```typescript
|
||||
// Frontend generates QR code data from session ID
|
||||
const qrData = `fastcheck://login?session=${sessionId}`;
|
||||
// Example: "fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A"
|
||||
```
|
||||
|
||||
**QR Code Contains:** Deep link URL with session ID
|
||||
- Format: `fastcheck://login?session={sessionId}`
|
||||
- Mobile app will parse this URL and extract the sessionId
|
||||
- Mobile app will then authenticate and update this session
|
||||
|
||||
---
|
||||
|
||||
## 2. Check WebSession Status (Polling)
|
||||
|
||||
### Frontend Request (Every 2 seconds):
|
||||
```typescript
|
||||
GET https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
|
||||
Headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Response (Not Authenticated Yet):
|
||||
```json
|
||||
{
|
||||
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
||||
"userId": "",
|
||||
"expires": "2026-01-19T10:50:00Z",
|
||||
"userSessionId": "",
|
||||
"Status": false
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Response (Authenticated):
|
||||
```json
|
||||
{
|
||||
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
||||
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo",
|
||||
"expires": "2026-01-19T12:00:00Z",
|
||||
"userSessionId": "8A94EFEFD003426A9B456C48CAC99BE6",
|
||||
"Status": true
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Implementation:
|
||||
```javascript
|
||||
app.get('/websession/:sessionId', async (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
|
||||
// Retrieve session from database/cache
|
||||
const session = await sessionStore.get(sessionId);
|
||||
|
||||
if (!session) {
|
||||
return res.status(404).json({ message: "Session not found" });
|
||||
}
|
||||
|
||||
// Check if session expired
|
||||
if (new Date() > new Date(session.expiresAt)) {
|
||||
await sessionStore.delete(sessionId);
|
||||
return res.status(404).json({ message: "Session expired" });
|
||||
}
|
||||
|
||||
// Return session status
|
||||
res.json({
|
||||
sessionId: session.sessionId,
|
||||
userId: session.userId || "",
|
||||
expires: session.expiresAt,
|
||||
userSessionId: session.userSessionId || "",
|
||||
Status: session.status || false
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Mobile App Authenticates Session
|
||||
|
||||
**This is what the MOBILE APP does** (not the web frontend):
|
||||
|
||||
### Mobile App Flow:
|
||||
1. User scans QR code: `fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A`
|
||||
2. Mobile app extracts sessionId: `1AF3781BF6B94604B771AEA1D44FA63A`
|
||||
3. Mobile app authenticates user (PIN, biometrics, etc.)
|
||||
4. Mobile app sends authentication request to backend:
|
||||
|
||||
```typescript
|
||||
POST https://api.fastcheck.store/websession/authenticate
|
||||
Headers: {
|
||||
"Authorization": "Bearer {mobile_app_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
Body: {
|
||||
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
||||
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo"
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Implementation:
|
||||
```javascript
|
||||
app.post('/websession/authenticate', authenticateMobileApp, async (req, res) => {
|
||||
const { sessionId, userId } = req.body;
|
||||
const mobileUserId = req.user.id; // From mobile app authentication
|
||||
|
||||
// Verify the mobile user matches
|
||||
if (userId !== mobileUserId) {
|
||||
return res.status(403).json({ message: "Unauthorized" });
|
||||
}
|
||||
|
||||
// Update session with user information
|
||||
const userSessionId = generateUUID();
|
||||
await sessionStore.update(sessionId, {
|
||||
userId: userId,
|
||||
userSessionId: userSessionId,
|
||||
status: true,
|
||||
authenticatedAt: new Date()
|
||||
});
|
||||
|
||||
res.json({ message: "Session authenticated" });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Logout (Delete Session)
|
||||
|
||||
### Frontend Request:
|
||||
```typescript
|
||||
DELETE https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
|
||||
Headers: {
|
||||
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Implementation:
|
||||
```javascript
|
||||
app.delete('/websession/:sessionId', async (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
|
||||
// Delete session from database/cache
|
||||
await sessionStore.delete(sessionId);
|
||||
|
||||
res.json({ message: "Session deleted" });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Authenticated API Requests
|
||||
|
||||
After login, all API requests include the sessionId in the Authorization header:
|
||||
|
||||
### Frontend Request:
|
||||
```typescript
|
||||
POST https://api.fastcheck.store/fastcheck
|
||||
Headers: {
|
||||
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
Body: {
|
||||
"amount": 150000,
|
||||
"currency": "RUB"
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Authentication Middleware:
|
||||
```javascript
|
||||
// Middleware to verify session
|
||||
const authenticateSession = async (req, res, next) => {
|
||||
try {
|
||||
// Parse Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
return res.status(401).json({ message: "not authorized" });
|
||||
}
|
||||
|
||||
// Parse JSON from Authorization header
|
||||
const { sessionID } = JSON.parse(authHeader);
|
||||
|
||||
// Verify session exists and is authenticated
|
||||
const session = await sessionStore.get(sessionID);
|
||||
|
||||
if (!session || !session.status) {
|
||||
return res.status(401).json({ message: "not authorized" });
|
||||
}
|
||||
|
||||
// Check if session expired
|
||||
if (new Date() > new Date(session.expiresAt)) {
|
||||
await sessionStore.delete(sessionID);
|
||||
return res.status(401).json({ message: "not authorized" });
|
||||
}
|
||||
|
||||
// Attach user info to request
|
||||
req.user = {
|
||||
userId: session.userId,
|
||||
userSessionId: session.userSessionId,
|
||||
sessionId: sessionID
|
||||
};
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({ message: "not authorized" });
|
||||
}
|
||||
};
|
||||
|
||||
// Use middleware on protected routes
|
||||
app.post('/fastcheck', authenticateSession, async (req, res) => {
|
||||
const { amount, currency } = req.body;
|
||||
const userId = req.user.userId;
|
||||
|
||||
// Create FastCheck logic...
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## QR Code Data Format
|
||||
|
||||
### What the QR Code Contains:
|
||||
```
|
||||
fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A
|
||||
```
|
||||
|
||||
**Format breakdown:**
|
||||
- **Scheme**: `fastcheck://` - Deep link scheme for mobile app
|
||||
- **Path**: `login` - Indicates this is a login QR code
|
||||
- **Parameter**: `session={sessionId}` - The web session ID
|
||||
|
||||
### Frontend QR Code Implementation:
|
||||
```typescript
|
||||
// In login.component.ts
|
||||
const sessionResponse = await createWebSession();
|
||||
const qrData = `fastcheck://login?session=${sessionResponse.sessionId}`;
|
||||
|
||||
// QR code component displays this as a QR image
|
||||
<qrcode [qrdata]="qrData" [width]="250"></qrcode>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Recommendations
|
||||
|
||||
### WebSession Table:
|
||||
```sql
|
||||
CREATE TABLE web_sessions (
|
||||
session_id VARCHAR(64) PRIMARY KEY,
|
||||
user_id VARCHAR(255),
|
||||
user_session_id VARCHAR(64),
|
||||
status BOOLEAN DEFAULT FALSE,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
authenticated_at TIMESTAMP,
|
||||
INDEX idx_expires (expires_at),
|
||||
INDEX idx_status (status)
|
||||
);
|
||||
|
||||
-- Auto-delete expired sessions
|
||||
CREATE EVENT cleanup_expired_sessions
|
||||
ON SCHEDULE EVERY 1 HOUR
|
||||
DO
|
||||
DELETE FROM web_sessions WHERE expires_at < NOW();
|
||||
```
|
||||
|
||||
### Or use Redis (Recommended for sessions):
|
||||
```javascript
|
||||
// Redis structure
|
||||
const sessionKey = `websession:${sessionId}`;
|
||||
await redis.setex(sessionKey, 300, JSON.stringify({
|
||||
sessionId: sessionId,
|
||||
userId: userId,
|
||||
userSessionId: userSessionId,
|
||||
status: true
|
||||
}));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Session Expiration**: Sessions should expire after 5 minutes if not authenticated
|
||||
2. **HTTPS Only**: All communication must be over HTTPS
|
||||
3. **CORS Configuration**: Configure CORS to allow frontend domain
|
||||
4. **Session Cleanup**: Regularly clean up expired sessions
|
||||
5. **Rate Limiting**: Limit polling requests to prevent abuse
|
||||
6. **Mobile App Authentication**: Mobile app must authenticate before linking session
|
||||
|
||||
---
|
||||
|
||||
## Testing the Flow
|
||||
|
||||
### 1. Test Session Creation:
|
||||
```bash
|
||||
curl -X GET https://api.fastcheck.store/websession
|
||||
```
|
||||
|
||||
Expected: New session with Status: false
|
||||
|
||||
### 2. Test Polling:
|
||||
```bash
|
||||
curl -X GET https://api.fastcheck.store/websession/{sessionId}
|
||||
```
|
||||
|
||||
Expected: Same session, Status: false (until mobile app authenticates)
|
||||
|
||||
### 3. Test Mobile Authentication (simulate):
|
||||
```bash
|
||||
curl -X POST https://api.fastcheck.store/websession/authenticate \
|
||||
-H "Authorization: Bearer {mobile_token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId": "{sessionId}", "userId": "{userId}"}'
|
||||
```
|
||||
|
||||
Expected: Session updated with Status: true
|
||||
|
||||
### 4. Test Polling After Auth:
|
||||
```bash
|
||||
curl -X GET https://api.fastcheck.store/websession/{sessionId}
|
||||
```
|
||||
|
||||
Expected: Session with Status: true, userId populated
|
||||
|
||||
---
|
||||
|
||||
## Frontend Polling Implementation (Already Done)
|
||||
|
||||
```typescript
|
||||
// In auth.service.ts
|
||||
startPolling(sessionId: string): Observable<WebSession> {
|
||||
return interval(2000).pipe( // Poll every 2 seconds
|
||||
switchMap(() => this.checkWebSessionStatus(sessionId)),
|
||||
tap(session => {
|
||||
if (session.Status) {
|
||||
this.setAuthenticated(session);
|
||||
}
|
||||
}),
|
||||
takeWhile(session => !session.Status, true) // Stop when authenticated
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary for Backend Team
|
||||
|
||||
**Required Endpoints:**
|
||||
1. ✅ `GET /websession` - Create session for QR
|
||||
2. ✅ `GET /websession/:id` - Check session status (polled)
|
||||
3. ⚠️ `POST /websession/authenticate` - Mobile app authenticates session (NEW)
|
||||
4. ✅ `DELETE /websession/:id` - Logout
|
||||
|
||||
**Required Logic:**
|
||||
- Generate unique session IDs
|
||||
- Store sessions with expiration
|
||||
- Mobile app updates session status
|
||||
- Web frontend polls until Status = true
|
||||
- All authenticated APIs verify session in Authorization header
|
||||
|
||||
**QR Code Data:**
|
||||
- Format: `fastcheck://login?session={sessionId}`
|
||||
- Mobile app parses and authenticates
|
||||
- Web polls until mobile authenticates
|
||||
Reference in New Issue
Block a user