11 KiB
11 KiB
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:
GET https://api.fastcheck.store/websession
Headers: {
"Content-Type": "application/json"
}
Backend Response:
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "",
"expires": "2026-01-19T10:50:00Z",
"userSessionId": "",
"Status": false
}
Backend Implementation:
// 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:
// 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):
GET https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
Headers: {
"Content-Type": "application/json"
}
Backend Response (Not Authenticated Yet):
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "",
"expires": "2026-01-19T10:50:00Z",
"userSessionId": "",
"Status": false
}
Backend Response (Authenticated):
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo",
"expires": "2026-01-19T12:00:00Z",
"userSessionId": "8A94EFEFD003426A9B456C48CAC99BE6",
"Status": true
}
Backend Implementation:
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:
- User scans QR code:
fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A - Mobile app extracts sessionId:
1AF3781BF6B94604B771AEA1D44FA63A - Mobile app authenticates user (PIN, biometrics, etc.)
- Mobile app sends authentication request to backend:
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:
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:
DELETE https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
Headers: {
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
"Content-Type": "application/json"
}
Backend Implementation:
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:
POST https://api.fastcheck.store/fastcheck
Headers: {
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
"Content-Type": "application/json"
}
Body: {
"amount": 150000,
"currency": "RUB"
}
Backend Authentication Middleware:
// 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:
// 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:
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):
// Redis structure
const sessionKey = `websession:${sessionId}`;
await redis.setex(sessionKey, 300, JSON.stringify({
sessionId: sessionId,
userId: userId,
userSessionId: userSessionId,
status: true
}));
Security Considerations
- Session Expiration: Sessions should expire after 5 minutes if not authenticated
- HTTPS Only: All communication must be over HTTPS
- CORS Configuration: Configure CORS to allow frontend domain
- Session Cleanup: Regularly clean up expired sessions
- Rate Limiting: Limit polling requests to prevent abuse
- Mobile App Authentication: Mobile app must authenticate before linking session
Testing the Flow
1. Test Session Creation:
curl -X GET https://api.fastcheck.store/websession
Expected: New session with Status: false
2. Test Polling:
curl -X GET https://api.fastcheck.store/websession/{sessionId}
Expected: Same session, Status: false (until mobile app authenticates)
3. Test Mobile Authentication (simulate):
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:
curl -X GET https://api.fastcheck.store/websession/{sessionId}
Expected: Session with Status: true, userId populated
Frontend Polling Implementation (Already Done)
// 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:
- ✅
GET /websession- Create session for QR - ✅
GET /websession/:id- Check session status (polled) - ⚠️
POST /websession/authenticate- Mobile app authenticates session (NEW) - ✅
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