gufi-cli 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/docs.js +1 -5
- package/dist/index.js +1 -0
- package/dist/lib/docs-resolver.d.ts +8 -0
- package/dist/lib/docs-resolver.js +27 -0
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +232 -34
- package/docs/dev-guide/1-01-architecture.md +358 -0
- package/docs/dev-guide/1-02-multi-tenant.md +415 -0
- package/docs/dev-guide/1-03-column-types.md +594 -0
- package/docs/dev-guide/1-04-json-config.md +442 -0
- package/docs/dev-guide/1-05-authentication.md +427 -0
- package/docs/dev-guide/2-01-api-reference.md +564 -0
- package/docs/dev-guide/2-02-automations.md +508 -0
- package/docs/dev-guide/2-03-gufi-cli.md +568 -0
- package/docs/dev-guide/2-04-realtime.md +401 -0
- package/docs/dev-guide/2-05-permissions.md +497 -0
- package/docs/dev-guide/2-06-integrations-overview.md +104 -0
- package/docs/dev-guide/2-07-stripe.md +173 -0
- package/docs/dev-guide/2-08-nayax.md +297 -0
- package/docs/dev-guide/2-09-ourvend.md +226 -0
- package/docs/dev-guide/2-10-tns.md +177 -0
- package/docs/dev-guide/2-11-custom-http.md +268 -0
- package/docs/dev-guide/3-01-custom-views.md +555 -0
- package/docs/dev-guide/3-02-webhooks-api.md +446 -0
- package/docs/mcp/00-overview.md +329 -0
- package/docs/mcp/01-architecture.md +220 -0
- package/docs/mcp/02-modules.md +285 -0
- package/docs/mcp/03-fields.md +357 -0
- package/docs/mcp/04-views.md +613 -0
- package/docs/mcp/05-automations.md +461 -0
- package/docs/mcp/06-api.md +480 -0
- package/docs/mcp/07-packages.md +246 -0
- package/docs/mcp/08-common-errors.md +284 -0
- package/docs/mcp/09-examples.md +453 -0
- package/docs/mcp/README.md +71 -0
- package/docs/mcp/tool-descriptions.json +49 -0
- package/package.json +3 -2
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: authentication
|
|
3
|
+
title: "Authentication & Security"
|
|
4
|
+
description: "JWT tokens, sessions, and security best practices"
|
|
5
|
+
icon: Key
|
|
6
|
+
category: dev
|
|
7
|
+
part: 1
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Authentication & Security
|
|
11
|
+
|
|
12
|
+
JWT tokens, sessions, and security best practices
|
|
13
|
+
|
|
14
|
+
## Authentication Flow
|
|
15
|
+
|
|
16
|
+
Gufi uses a dual-layer JWT authentication system for security and seamless user experience.
|
|
17
|
+
|
|
18
|
+
### Overview
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ Authentication Flow │
|
|
23
|
+
├─────────────────────────────────────────────────────────────┤
|
|
24
|
+
│ │
|
|
25
|
+
│ Login Request │
|
|
26
|
+
│ │ │
|
|
27
|
+
│ ▼ │
|
|
28
|
+
│ ┌─────────────┐ │
|
|
29
|
+
│ │ Backend │──────┐ │
|
|
30
|
+
│ │ Validates │ │ │
|
|
31
|
+
│ └─────────────┘ │ │
|
|
32
|
+
│ │ │ │
|
|
33
|
+
│ ▼ ▼ │
|
|
34
|
+
│ ┌─────────────┐ ┌─────────────┐ │
|
|
35
|
+
│ │ Access │ │ Refresh │ │
|
|
36
|
+
│ │ Token │ │ Cookie │ │
|
|
37
|
+
│ │ (15 min) │ │ (7 days) │ │
|
|
38
|
+
│ └─────────────┘ └─────────────┘ │
|
|
39
|
+
│ │ │ │
|
|
40
|
+
│ ▼ │ │
|
|
41
|
+
│ API Requests ◄───────────┘ (auto-refresh) │
|
|
42
|
+
│ │
|
|
43
|
+
└─────────────────────────────────────────────────────────────┘
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Login Process
|
|
47
|
+
|
|
48
|
+
### Step 1: User Credentials
|
|
49
|
+
|
|
50
|
+
```http
|
|
51
|
+
POST /api/auth/login
|
|
52
|
+
Content-Type: application/json
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
"email": "user@company.com",
|
|
56
|
+
"password": "securePassword123"
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step 2: Server Validates
|
|
61
|
+
|
|
62
|
+
1. Find user by email
|
|
63
|
+
2. Verify password hash (bcrypt)
|
|
64
|
+
3. Check account status
|
|
65
|
+
4. Load company memberships
|
|
66
|
+
|
|
67
|
+
### Step 3: Token Generation
|
|
68
|
+
|
|
69
|
+
**Access Token (JWT)**
|
|
70
|
+
- Short-lived: 15 minutes
|
|
71
|
+
- Contains: user ID, company ID, roles
|
|
72
|
+
- Sent in response body
|
|
73
|
+
|
|
74
|
+
**Refresh Token (HttpOnly Cookie)**
|
|
75
|
+
- Long-lived: 7 days
|
|
76
|
+
- Contains: user ID, token family ID
|
|
77
|
+
- Set as HttpOnly, Secure, SameSite cookie
|
|
78
|
+
|
|
79
|
+
### Response
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"token": "eyJhbGciOiJIUzI1NiIs...",
|
|
84
|
+
"user": {
|
|
85
|
+
"id": 123,
|
|
86
|
+
"email": "user@company.com",
|
|
87
|
+
"name": "John Doe",
|
|
88
|
+
"companies": [
|
|
89
|
+
{ "id": 146, "name": "Acme Corp", "roles": ["Admin"] }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Plus Set-Cookie header for refresh token.
|
|
96
|
+
|
|
97
|
+
## Token Structure
|
|
98
|
+
|
|
99
|
+
### Access Token Payload
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"sub": "123",
|
|
104
|
+
"email": "user@company.com",
|
|
105
|
+
"company_id": 146,
|
|
106
|
+
"roles": ["Admin"],
|
|
107
|
+
"platform_role": "client",
|
|
108
|
+
"iat": 1699999000,
|
|
109
|
+
"exp": 1699999900
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Key Claims
|
|
114
|
+
|
|
115
|
+
| Claim | Description |
|
|
116
|
+
|---|---|
|
|
117
|
+
| sub | User ID |
|
|
118
|
+
| company_id | Active company |
|
|
119
|
+
| roles | Company-specific roles |
|
|
120
|
+
| platform_role | Platform-wide role (admin/consultant/client) |
|
|
121
|
+
| exp | Expiration timestamp |
|
|
122
|
+
|
|
123
|
+
## Token Refresh
|
|
124
|
+
|
|
125
|
+
### Automatic Refresh
|
|
126
|
+
|
|
127
|
+
Frontend proactively refreshes 30 seconds before expiry:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Frontend checks every minute
|
|
131
|
+
const checkTokenExpiry = () => {
|
|
132
|
+
const payload = decodeJwt(token);
|
|
133
|
+
const expiresIn = payload.exp * 1000 - Date.now();
|
|
134
|
+
|
|
135
|
+
if (expiresIn < 30000) {
|
|
136
|
+
refreshToken();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Refresh Endpoint
|
|
142
|
+
|
|
143
|
+
```http
|
|
144
|
+
POST /api/auth/refresh
|
|
145
|
+
Cookie: refresh_token=...
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Response:
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"token": "eyJhbGciOiJIUzI1NiIs..."
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
New refresh cookie also set.
|
|
156
|
+
|
|
157
|
+
### Token Rotation
|
|
158
|
+
|
|
159
|
+
Each refresh generates a new refresh token, invalidating the old one. This prevents token theft attacks.
|
|
160
|
+
|
|
161
|
+
## Using Tokens
|
|
162
|
+
|
|
163
|
+
### API Requests
|
|
164
|
+
|
|
165
|
+
```http
|
|
166
|
+
GET /api/tables/products
|
|
167
|
+
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
|
168
|
+
X-Company-ID: 146
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Backend Validation
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// middleware/auth.js
|
|
175
|
+
const authenticateUserToken = async (req, res, next) => {
|
|
176
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
177
|
+
|
|
178
|
+
if (!token) {
|
|
179
|
+
return res.status(401).json({ error: 'No token provided' });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
|
184
|
+
req.user = payload;
|
|
185
|
+
|
|
186
|
+
// Validate company access
|
|
187
|
+
const companyId = req.headers['x-company-id'];
|
|
188
|
+
if (companyId && companyId !== String(payload.company_id)) {
|
|
189
|
+
// Verify user has access to requested company
|
|
190
|
+
const hasAccess = await checkCompanyAccess(payload.sub, companyId);
|
|
191
|
+
if (!hasAccess) {
|
|
192
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
next();
|
|
197
|
+
} catch (error) {
|
|
198
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Platform Roles
|
|
204
|
+
|
|
205
|
+
### Role Hierarchy
|
|
206
|
+
|
|
207
|
+
| Role | Description | Access |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| admin | Platform administrator | Everything |
|
|
210
|
+
| consultant | Developer/consultant | Developer tools, Marketplace, no company management |
|
|
211
|
+
| client | Regular user | Basic access, own companies only |
|
|
212
|
+
|
|
213
|
+
### Checking Platform Role
|
|
214
|
+
|
|
215
|
+
**Frontend:**
|
|
216
|
+
```typescript
|
|
217
|
+
import { usePlatformRole } from '@/hooks/usePlatformRole';
|
|
218
|
+
|
|
219
|
+
const { isAdmin, isConsultant, canAccessDeveloperCenter } = usePlatformRole();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Backend:**
|
|
223
|
+
```javascript
|
|
224
|
+
const platformRole = req.user?.platform_role || 'client';
|
|
225
|
+
|
|
226
|
+
if (platformRole !== 'admin') {
|
|
227
|
+
return res.status(403).json({ error: 'Admin only' });
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Company Roles
|
|
232
|
+
|
|
233
|
+
### Per-Company Permissions
|
|
234
|
+
|
|
235
|
+
Users can have different roles in different companies:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
User 123
|
|
239
|
+
├── Company A: Admin
|
|
240
|
+
├── Company B: Manager
|
|
241
|
+
└── Company C: Viewer
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Role-Based Access
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
// Check company-specific role
|
|
248
|
+
const companyMembership = await getCompanyMembership(userId, companyId);
|
|
249
|
+
const roles = companyMembership.roles; // ['Admin', 'Sales']
|
|
250
|
+
|
|
251
|
+
if (!roles.includes('Admin')) {
|
|
252
|
+
// Limited access
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Security Features
|
|
257
|
+
|
|
258
|
+
### Password Requirements
|
|
259
|
+
|
|
260
|
+
- Minimum 8 characters
|
|
261
|
+
- At least one number
|
|
262
|
+
- bcrypt hashing with salt rounds: 12
|
|
263
|
+
|
|
264
|
+
### Rate Limiting
|
|
265
|
+
|
|
266
|
+
| Endpoint | Limit |
|
|
267
|
+
|---|---|
|
|
268
|
+
| /auth/login | 5 attempts per minute |
|
|
269
|
+
| /auth/refresh | 10 per minute |
|
|
270
|
+
| /auth/reset-password | 3 per hour |
|
|
271
|
+
|
|
272
|
+
### Account Lockout
|
|
273
|
+
|
|
274
|
+
After 5 failed login attempts:
|
|
275
|
+
|
|
276
|
+
1. Account locked for 15 minutes
|
|
277
|
+
2. Email notification sent
|
|
278
|
+
3. Admin can unlock manually
|
|
279
|
+
|
|
280
|
+
### Two-Factor Authentication
|
|
281
|
+
|
|
282
|
+
Optional 2FA using TOTP:
|
|
283
|
+
|
|
284
|
+
```http
|
|
285
|
+
POST /api/auth/login
|
|
286
|
+
{
|
|
287
|
+
"email": "user@company.com",
|
|
288
|
+
"password": "password",
|
|
289
|
+
"totp_code": "123456"
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Session Management
|
|
294
|
+
|
|
295
|
+
### Active Sessions
|
|
296
|
+
|
|
297
|
+
Users can view active sessions:
|
|
298
|
+
|
|
299
|
+
```http
|
|
300
|
+
GET /api/auth/sessions
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"sessions": [
|
|
307
|
+
{
|
|
308
|
+
"id": "sess_123",
|
|
309
|
+
"device": "Chrome on Windows",
|
|
310
|
+
"ip": "192.168.1.1",
|
|
311
|
+
"lastActive": "2024-01-15T10:30:00Z",
|
|
312
|
+
"current": true
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Revoking Sessions
|
|
319
|
+
|
|
320
|
+
```http
|
|
321
|
+
DELETE /api/auth/sessions/sess_123
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Or revoke all:
|
|
325
|
+
```http
|
|
326
|
+
DELETE /api/auth/sessions
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## API Keys
|
|
330
|
+
|
|
331
|
+
### For Integrations
|
|
332
|
+
|
|
333
|
+
API keys provide machine-to-machine authentication:
|
|
334
|
+
|
|
335
|
+
```http
|
|
336
|
+
GET /api/tables/products
|
|
337
|
+
Authorization: Bearer api_key_xxxxx
|
|
338
|
+
X-Company-ID: 146
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Key Properties
|
|
342
|
+
|
|
343
|
+
| Property | Description |
|
|
344
|
+
|---|---|
|
|
345
|
+
| name | Descriptive name |
|
|
346
|
+
| permissions | read, write, delete |
|
|
347
|
+
| company_id | Bound to company |
|
|
348
|
+
| last_used | Last usage timestamp |
|
|
349
|
+
|
|
350
|
+
### Creating Keys
|
|
351
|
+
|
|
352
|
+
```http
|
|
353
|
+
POST /api/keys
|
|
354
|
+
{
|
|
355
|
+
"name": "My Integration",
|
|
356
|
+
"company_id": 146,
|
|
357
|
+
"permissions": ["read", "write"]
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Logout
|
|
362
|
+
|
|
363
|
+
### Endpoint
|
|
364
|
+
|
|
365
|
+
```http
|
|
366
|
+
POST /api/auth/logout
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Actions:
|
|
370
|
+
1. Invalidate refresh token
|
|
371
|
+
2. Clear refresh cookie
|
|
372
|
+
3. Optionally invalidate all sessions
|
|
373
|
+
|
|
374
|
+
### Frontend
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
const logout = async () => {
|
|
378
|
+
await fetch('/api/auth/logout', { method: 'POST' });
|
|
379
|
+
clearLocalStorage();
|
|
380
|
+
redirectToLogin();
|
|
381
|
+
};
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Security Best Practices
|
|
385
|
+
|
|
386
|
+
### Token Storage
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Good: Store access token in memory only
|
|
390
|
+
let accessToken = null;
|
|
391
|
+
|
|
392
|
+
// Bad: Store in localStorage (XSS vulnerable)
|
|
393
|
+
localStorage.setItem('token', token);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### HTTPS Only
|
|
397
|
+
|
|
398
|
+
All production endpoints require HTTPS. HTTP requests are rejected.
|
|
399
|
+
|
|
400
|
+
### CORS Configuration
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
const corsOptions = {
|
|
404
|
+
origin: ['https://app.gogufi.com'],
|
|
405
|
+
credentials: true, // For cookies
|
|
406
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE']
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Security Headers
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
414
|
+
X-Content-Type-Options: nosniff
|
|
415
|
+
X-Frame-Options: DENY
|
|
416
|
+
X-XSS-Protection: 1; mode=block
|
|
417
|
+
Content-Security-Policy: default-src 'self'
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Error Codes
|
|
421
|
+
|
|
422
|
+
| Code | Meaning | Action |
|
|
423
|
+
|---|---|---|
|
|
424
|
+
| 401 | Token invalid/expired | Refresh or re-login |
|
|
425
|
+
| 403 | Access denied | Check permissions |
|
|
426
|
+
| 423 | Account locked | Wait or contact admin |
|
|
427
|
+
| 429 | Too many requests | Wait and retry |
|