@intelicity/gates-sdk 0.1.6 → 0.2.0
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/README.md +99 -480
- package/dist/auth/middleware.d.ts +10 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +31 -40
- package/dist/errors/error.d.ts +0 -36
- package/dist/errors/error.d.ts.map +1 -1
- package/dist/errors/error.js +6 -47
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -12
- package/dist/models/user.d.ts +6 -7
- package/dist/models/user.d.ts.map +1 -1
- package/dist/services/admin-service.d.ts +26 -0
- package/dist/services/admin-service.d.ts.map +1 -0
- package/dist/services/admin-service.js +92 -0
- package/dist/services/auth-service.d.ts +4 -7
- package/dist/services/auth-service.d.ts.map +1 -1
- package/dist/services/auth-service.js +32 -46
- package/dist/services/client-auth.d.ts +22 -0
- package/dist/services/client-auth.d.ts.map +1 -0
- package/dist/services/client-auth.js +75 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Gates SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Node.js SDK for the Gates authentication system (AWS Cognito). Provides JWT token verification, group-based access control, admin user management, and framework-agnostic middleware.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,563 +10,182 @@ npm install @intelicity/gates-sdk
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- ⚡ Detailed error messages with status codes and error codes
|
|
13
|
+
- JWT verification for both access and id tokens
|
|
14
|
+
- Admin user management (create + assign to client/group)
|
|
15
|
+
- Framework-agnostic middleware (works with Fastify, Express, etc.)
|
|
16
|
+
- Built-in JWKS caching (1h TTL)
|
|
17
|
+
- Group-based access control (Cognito Groups)
|
|
18
|
+
- Comprehensive error hierarchy
|
|
20
19
|
|
|
21
20
|
## Usage
|
|
22
21
|
|
|
23
|
-
###
|
|
24
|
-
|
|
25
|
-
The `AuthService` provides JWT token verification with AWS Cognito:
|
|
22
|
+
### Token Verification
|
|
26
23
|
|
|
27
24
|
```typescript
|
|
28
25
|
import { AuthService } from "@intelicity/gates-sdk";
|
|
29
26
|
|
|
30
|
-
const
|
|
31
|
-
"sa-east-1",
|
|
27
|
+
const auth = new AuthService(
|
|
28
|
+
"sa-east-1", // AWS region
|
|
32
29
|
"sa-east-1_xxxxxxxxx", // User Pool ID
|
|
33
|
-
"your-client-id"
|
|
34
|
-
["admin", "user"] // Optional: required groups
|
|
30
|
+
"your-client-id" // Cognito App Client ID
|
|
35
31
|
);
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const user = await authService.verifyToken(token);
|
|
40
|
-
console.log("Authenticated user:", user);
|
|
41
|
-
|
|
42
|
-
// Check group membership
|
|
43
|
-
const isAdmin = authService.isMemberOf(user.groups || []);
|
|
44
|
-
console.log("Has required group:", isAdmin);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error("Authentication failed:", error.message);
|
|
47
|
-
}
|
|
33
|
+
const user = await auth.verifyToken(accessToken);
|
|
34
|
+
// user.user_id, user.groups, user.token_use, user.email?, user.name?
|
|
48
35
|
```
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
Supports both `access_token` (validates `client_id` claim) and `id_token` (validates `aud` claim). The `token_use` field on the returned user indicates which type was verified.
|
|
51
38
|
|
|
52
|
-
|
|
39
|
+
### Middleware
|
|
40
|
+
|
|
41
|
+
Framework-agnostic functions for request authentication:
|
|
53
42
|
|
|
54
43
|
```typescript
|
|
55
|
-
import {
|
|
44
|
+
import { handleAuth, AuthService } from "@intelicity/gates-sdk";
|
|
56
45
|
|
|
57
|
-
const
|
|
58
|
-
"https://api.example.com", // Backend API URL
|
|
59
|
-
"your-system-name" // System identifier
|
|
60
|
-
);
|
|
46
|
+
const service = new AuthService(region, userPoolId, clientId);
|
|
61
47
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
}
|
|
48
|
+
// Fastify
|
|
49
|
+
app.addHook("preHandler", async (req) => {
|
|
50
|
+
req.user = await handleAuth(req.headers.authorization, {
|
|
51
|
+
service,
|
|
52
|
+
requiredGroups: "GAIA",
|
|
53
|
+
});
|
|
54
|
+
});
|
|
70
55
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
56
|
+
// Express
|
|
57
|
+
app.use(async (req, res, next) => {
|
|
58
|
+
try {
|
|
59
|
+
req.user = await handleAuth(req.headers.authorization, {
|
|
60
|
+
service,
|
|
61
|
+
requiredGroups: "GAIA",
|
|
62
|
+
});
|
|
63
|
+
next();
|
|
64
|
+
} catch (e) { next(e); }
|
|
65
|
+
});
|
|
78
66
|
```
|
|
79
67
|
|
|
80
|
-
|
|
68
|
+
Individual functions are also available:
|
|
81
69
|
|
|
82
70
|
```typescript
|
|
83
|
-
import {
|
|
84
|
-
AuthService,
|
|
85
|
-
UserService,
|
|
86
|
-
TokenExpiredError,
|
|
87
|
-
UnauthorizedGroupError,
|
|
88
|
-
ApiRequestError,
|
|
89
|
-
MissingParameterError,
|
|
90
|
-
GatesError,
|
|
91
|
-
} from "@intelicity/gates-sdk";
|
|
71
|
+
import { extractToken, authenticate, authorize } from "@intelicity/gates-sdk";
|
|
92
72
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
constructor() {
|
|
98
|
-
this.authService = new AuthService(
|
|
99
|
-
process.env.AWS_REGION!,
|
|
100
|
-
process.env.COGNITO_USER_POOL_ID!,
|
|
101
|
-
process.env.COGNITO_CLIENT_ID!,
|
|
102
|
-
["admin"] // Only admins can access
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
this.userService = new UserService(
|
|
106
|
-
process.env.BACKEND_URL!,
|
|
107
|
-
process.env.SYSTEM_NAME!
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async handleRequest(authToken: string) {
|
|
112
|
-
try {
|
|
113
|
-
// 1. Authenticate user
|
|
114
|
-
const user = await this.authService.verifyToken(authToken);
|
|
115
|
-
console.log(`User ${user.name} authenticated successfully`);
|
|
116
|
-
|
|
117
|
-
// 2. Get user list if authorized
|
|
118
|
-
const users = await this.userService.getAllUsers(authToken);
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
currentUser: user,
|
|
122
|
-
allUsers: users.profiles,
|
|
123
|
-
total: users.total,
|
|
124
|
-
};
|
|
125
|
-
} catch (error) {
|
|
126
|
-
// Handle specific error types
|
|
127
|
-
if (error instanceof TokenExpiredError) {
|
|
128
|
-
return { error: "Session expired", code: 401 };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (error instanceof UnauthorizedGroupError) {
|
|
132
|
-
return {
|
|
133
|
-
error: "Access denied",
|
|
134
|
-
code: 403,
|
|
135
|
-
requiredGroups: error.requiredGroups,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (error instanceof ApiRequestError) {
|
|
140
|
-
return {
|
|
141
|
-
error: "Service unavailable",
|
|
142
|
-
code: error.statusCode || 500,
|
|
143
|
-
details: error.message,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (error instanceof MissingParameterError) {
|
|
148
|
-
return { error: "Invalid request", code: 400 };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (error instanceof GatesError) {
|
|
152
|
-
return {
|
|
153
|
-
error: "Operation failed",
|
|
154
|
-
code: 500,
|
|
155
|
-
errorCode: error.code,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Unknown error
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
73
|
+
const token = extractToken(req.headers.authorization); // Bearer token extraction
|
|
74
|
+
const user = await authenticate(token, service); // JWT verification
|
|
75
|
+
authorize(user, ["GAIA", "RECAPE"]); // Group check (throws if unauthorized)
|
|
164
76
|
```
|
|
165
77
|
|
|
166
78
|
### Error Handling
|
|
167
79
|
|
|
168
|
-
The SDK provides a comprehensive error hierarchy for better error handling:
|
|
169
|
-
|
|
170
80
|
```typescript
|
|
171
81
|
import {
|
|
172
|
-
AuthService,
|
|
173
|
-
UserService,
|
|
174
|
-
// Base errors
|
|
175
|
-
GatesError,
|
|
176
|
-
// Authentication errors
|
|
177
|
-
AuthenticationError,
|
|
178
82
|
TokenExpiredError,
|
|
179
83
|
InvalidTokenError,
|
|
180
|
-
MissingAuthorizationError,
|
|
181
84
|
UnauthorizedGroupError,
|
|
182
|
-
|
|
183
|
-
ApiError,
|
|
184
|
-
ApiRequestError,
|
|
185
|
-
InvalidResponseError,
|
|
186
|
-
// Parameter errors
|
|
187
|
-
MissingParameterError,
|
|
188
|
-
InvalidParameterError,
|
|
85
|
+
GatesError,
|
|
189
86
|
} from "@intelicity/gates-sdk";
|
|
190
87
|
|
|
191
|
-
const authService = new AuthService(region, userPoolId, audience, ["admin"]);
|
|
192
|
-
|
|
193
|
-
// Authentication Error Handling
|
|
194
88
|
try {
|
|
195
|
-
const user = await
|
|
196
|
-
console.log("User authenticated:", user);
|
|
89
|
+
const user = await auth.verifyToken(token);
|
|
197
90
|
} catch (error) {
|
|
198
91
|
if (error instanceof TokenExpiredError) {
|
|
199
|
-
console.error("Token expired, please login again");
|
|
200
92
|
// error.code === "TOKEN_EXPIRED"
|
|
201
93
|
} else if (error instanceof InvalidTokenError) {
|
|
202
|
-
console.error("Invalid token:", error.message);
|
|
203
94
|
// error.code === "INVALID_TOKEN"
|
|
204
95
|
} else if (error instanceof UnauthorizedGroupError) {
|
|
205
|
-
console.error("Access denied:", error.message);
|
|
206
|
-
console.error("Required groups:", error.requiredGroups);
|
|
207
96
|
// error.code === "UNAUTHORIZED_GROUP"
|
|
208
|
-
|
|
209
|
-
console.error("Missing parameter:", error.message);
|
|
210
|
-
// error.code === "MISSING_PARAMETER"
|
|
211
|
-
} else if (error instanceof AuthenticationError) {
|
|
212
|
-
console.error("Authentication failed:", error.message);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// User Service Error Handling
|
|
217
|
-
const userService = new UserService(backendUrl, systemName);
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
const users = await userService.getAllUsers(token);
|
|
221
|
-
console.log("Users fetched:", users.profiles.length);
|
|
222
|
-
} catch (error) {
|
|
223
|
-
if (error instanceof ApiRequestError) {
|
|
224
|
-
console.error(`API request failed [${error.statusCode}]:`, error.message);
|
|
225
|
-
// error.code === "API_REQUEST_ERROR"
|
|
226
|
-
// error.statusCode contains HTTP status code
|
|
227
|
-
|
|
228
|
-
if (error.statusCode === 401) {
|
|
229
|
-
console.error("Unauthorized - check your token");
|
|
230
|
-
} else if (error.statusCode === 403) {
|
|
231
|
-
console.error("Forbidden - insufficient permissions");
|
|
232
|
-
} else if (error.statusCode === 404) {
|
|
233
|
-
console.error("Resource not found");
|
|
234
|
-
}
|
|
235
|
-
} else if (error instanceof InvalidResponseError) {
|
|
236
|
-
console.error("Invalid API response:", error.message);
|
|
237
|
-
// error.code === "INVALID_RESPONSE"
|
|
238
|
-
} else if (error instanceof MissingParameterError) {
|
|
239
|
-
console.error("Missing required parameter:", error.message);
|
|
97
|
+
// error.requiredGroups: string[]
|
|
240
98
|
} else if (error instanceof GatesError) {
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Catch all Gates errors
|
|
246
|
-
try {
|
|
247
|
-
const profile = await userService.getProfile(token);
|
|
248
|
-
} catch (error) {
|
|
249
|
-
if (error instanceof GatesError) {
|
|
250
|
-
// All SDK errors inherit from GatesError
|
|
251
|
-
console.error(`Error [${error.code}]:`, error.message);
|
|
252
|
-
|
|
253
|
-
// You can check specific properties based on error type
|
|
254
|
-
if (error instanceof ApiRequestError && error.statusCode) {
|
|
255
|
-
console.error(`HTTP Status: ${error.statusCode}`);
|
|
256
|
-
}
|
|
257
|
-
if (error instanceof UnauthorizedGroupError) {
|
|
258
|
-
console.error(`Required groups: ${error.requiredGroups.join(", ")}`);
|
|
259
|
-
}
|
|
99
|
+
// error.code, error.message
|
|
260
100
|
}
|
|
261
101
|
}
|
|
262
102
|
```
|
|
263
103
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
### `AuthService`
|
|
267
|
-
|
|
268
|
-
Main service for JWT token verification with AWS Cognito.
|
|
104
|
+
### Admin Service (User Management)
|
|
269
105
|
|
|
270
|
-
|
|
106
|
+
For creating users and managing system access (requires admin id_token):
|
|
271
107
|
|
|
272
108
|
```typescript
|
|
273
|
-
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
**Parameters:**
|
|
277
|
-
|
|
278
|
-
- `region` (string): AWS region (e.g., 'sa-east-1')
|
|
279
|
-
- `userPoolId` (string): Cognito User Pool ID
|
|
280
|
-
- `audience` (string): Expected audience claim (client ID)
|
|
281
|
-
- `requiredGroup` (string | string[], optional): Required Cognito groups
|
|
282
|
-
|
|
283
|
-
#### Methods
|
|
109
|
+
import { GatesAdminService } from "@intelicity/gates-sdk";
|
|
284
110
|
|
|
285
|
-
|
|
111
|
+
const admin = new GatesAdminService({
|
|
112
|
+
baseUrl: "https://abc123.execute-api.sa-east-1.amazonaws.com/prod",
|
|
113
|
+
});
|
|
286
114
|
|
|
287
|
-
|
|
115
|
+
// Create a new user and add to a client (group)
|
|
116
|
+
// Internally calls POST /create-user then PUT /update-user
|
|
117
|
+
const { sub } = await admin.createUser(adminIdToken, {
|
|
118
|
+
email: "newuser@company.com",
|
|
119
|
+
name: "New User",
|
|
120
|
+
role: "CLIENT_USER",
|
|
121
|
+
client: "GAIA",
|
|
122
|
+
});
|
|
288
123
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
Service for managing users through a backend API.
|
|
296
|
-
|
|
297
|
-
#### Constructor
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
new UserService(baseUrl, system);
|
|
124
|
+
// Later: add/remove user access to other systems
|
|
125
|
+
await admin.updateUser(adminIdToken, {
|
|
126
|
+
user_id: sub,
|
|
127
|
+
clients_to_add: ["RECAPE"],
|
|
128
|
+
clients_to_remove: ["INFORMS"],
|
|
129
|
+
});
|
|
301
130
|
```
|
|
302
131
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
- `baseUrl` (string): Backend API base URL
|
|
306
|
-
- `system` (string): System identifier
|
|
307
|
-
|
|
308
|
-
#### Methods
|
|
309
|
-
|
|
310
|
-
##### `getAllUsers(idToken: string): Promise<UserListResponse>`
|
|
311
|
-
|
|
312
|
-
Retrieves all users from the system.
|
|
313
|
-
|
|
314
|
-
##### `getUserById(idToken: string, userId: string): Promise<Profile>`
|
|
315
|
-
|
|
316
|
-
Retrieves a specific user by ID.
|
|
317
|
-
|
|
318
|
-
### Types
|
|
319
|
-
|
|
320
|
-
#### `GatesUser`
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
type GatesUser = {
|
|
324
|
-
user_id: string; // Mapped from 'sub' claim
|
|
325
|
-
email: string; // User email
|
|
326
|
-
name: string; // User display name
|
|
327
|
-
role: string; // Mapped from 'custom:general_role'
|
|
328
|
-
exp: number; // Token expiration timestamp
|
|
329
|
-
iat: number; // Token issued at timestamp
|
|
330
|
-
};
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
#### `Profile`
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
type Profile = {
|
|
337
|
-
user_id: string;
|
|
338
|
-
email: string;
|
|
339
|
-
name: string;
|
|
340
|
-
enabled: boolean;
|
|
341
|
-
profile_attributes: ProfileAttribute[];
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
type ProfileAttribute = {
|
|
345
|
-
attribute_name: string;
|
|
346
|
-
value: string | boolean | number;
|
|
347
|
-
};
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
#### `UserListResponse`
|
|
351
|
-
|
|
352
|
-
```typescript
|
|
353
|
-
type UserListResponse = {
|
|
354
|
-
profiles: Profile[];
|
|
355
|
-
total?: number;
|
|
356
|
-
page?: number;
|
|
357
|
-
limit?: number;
|
|
358
|
-
nextToken?: string;
|
|
359
|
-
};
|
|
360
|
-
```
|
|
132
|
+
## API Reference
|
|
361
133
|
|
|
362
|
-
|
|
134
|
+
### `AuthService`
|
|
363
135
|
|
|
364
136
|
```typescript
|
|
365
|
-
|
|
366
|
-
region: string;
|
|
367
|
-
userPoolId: string;
|
|
368
|
-
audience: string;
|
|
369
|
-
requiredGroup?: string | string[];
|
|
370
|
-
};
|
|
137
|
+
new AuthService(region: string, userPoolId: string, clientId: string)
|
|
371
138
|
```
|
|
372
139
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
All errors inherit from `GatesError` and include a `code` property for easy error identification.
|
|
376
|
-
|
|
377
|
-
**Base Errors:**
|
|
378
|
-
|
|
379
|
-
- `GatesError`: Base error class for all SDK errors
|
|
380
|
-
- Properties: `message`, `code`
|
|
381
|
-
|
|
382
|
-
**Authentication Errors:**
|
|
383
|
-
|
|
384
|
-
- `AuthenticationError`: Base authentication error (extends `GatesError`)
|
|
385
|
-
- `TokenExpiredError`: Token has expired
|
|
386
|
-
- Code: `TOKEN_EXPIRED`
|
|
387
|
-
- `InvalidTokenError`: Token is invalid or malformed
|
|
388
|
-
- Code: `INVALID_TOKEN`
|
|
389
|
-
- `MissingAuthorizationError`: Authorization header is missing
|
|
390
|
-
- Code: `MISSING_AUTHORIZATION`
|
|
391
|
-
- `UnauthorizedGroupError`: User is not a member of required group
|
|
392
|
-
- Code: `UNAUTHORIZED_GROUP`
|
|
393
|
-
- Properties: `message`, `code`, `requiredGroups: string[]`
|
|
394
|
-
|
|
395
|
-
**API Errors:**
|
|
396
|
-
|
|
397
|
-
- `ApiError`: Base API error (extends `GatesError`)
|
|
398
|
-
- Properties: `message`, `code`, `statusCode?: number`
|
|
399
|
-
- `ApiRequestError`: API request failed
|
|
400
|
-
- Code: `API_REQUEST_ERROR`
|
|
401
|
-
- Properties: `message`, `code`, `statusCode?: number`
|
|
402
|
-
- `InvalidResponseError`: API response format is invalid
|
|
403
|
-
- Code: `INVALID_RESPONSE`
|
|
404
|
-
|
|
405
|
-
**Parameter Errors:**
|
|
406
|
-
|
|
407
|
-
- `MissingParameterError`: Required parameter is missing
|
|
408
|
-
- Code: `MISSING_PARAMETER`
|
|
409
|
-
- `InvalidParameterError`: Parameter has an invalid value
|
|
410
|
-
- Code: `INVALID_PARAMETER`
|
|
140
|
+
- `verifyToken(token: string): Promise<GatesUser>` — Verifies JWT, returns user
|
|
411
141
|
|
|
412
|
-
###
|
|
413
|
-
|
|
414
|
-
All SDK errors include a `code` property for programmatic error handling:
|
|
415
|
-
|
|
416
|
-
| Error Code | Error Class | Description |
|
|
417
|
-
| ----------------------- | --------------------------- | ------------------------------------ |
|
|
418
|
-
| `TOKEN_EXPIRED` | `TokenExpiredError` | JWT token has expired |
|
|
419
|
-
| `INVALID_TOKEN` | `InvalidTokenError` | Token is invalid or malformed |
|
|
420
|
-
| `MISSING_AUTHORIZATION` | `MissingAuthorizationError` | Authorization header is missing |
|
|
421
|
-
| `UNAUTHORIZED_GROUP` | `UnauthorizedGroupError` | User lacks required group membership |
|
|
422
|
-
| `API_REQUEST_ERROR` | `ApiRequestError` | API request failed |
|
|
423
|
-
| `INVALID_RESPONSE` | `InvalidResponseError` | API response format is invalid |
|
|
424
|
-
| `MISSING_PARAMETER` | `MissingParameterError` | Required parameter is missing |
|
|
425
|
-
| `INVALID_PARAMETER` | `InvalidParameterError` | Parameter has invalid value |
|
|
426
|
-
|
|
427
|
-
**Usage Example:**
|
|
142
|
+
### `GatesAdminService`
|
|
428
143
|
|
|
429
144
|
```typescript
|
|
430
|
-
|
|
431
|
-
const user = await authService.verifyToken(token);
|
|
432
|
-
} catch (error) {
|
|
433
|
-
if (error instanceof GatesError) {
|
|
434
|
-
switch (error.code) {
|
|
435
|
-
case "TOKEN_EXPIRED":
|
|
436
|
-
// Redirect to login
|
|
437
|
-
break;
|
|
438
|
-
case "UNAUTHORIZED_GROUP":
|
|
439
|
-
// Show access denied page
|
|
440
|
-
break;
|
|
441
|
-
case "API_REQUEST_ERROR":
|
|
442
|
-
// Show error message with retry option
|
|
443
|
-
break;
|
|
444
|
-
default:
|
|
445
|
-
// Generic error handling
|
|
446
|
-
console.error(error.message);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
145
|
+
new GatesAdminService({ baseUrl: string })
|
|
450
146
|
```
|
|
451
147
|
|
|
452
|
-
|
|
148
|
+
- `createUser(idToken: string, params: CreateUserParams): Promise<CreateUserResponse>` — Creates user in Gates
|
|
149
|
+
- `updateUser(idToken: string, params: UpdateUserParams): Promise<void>` — Manages user's system access
|
|
453
150
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
```bash
|
|
457
|
-
# .env file
|
|
458
|
-
GATES_REGION=sa-east-1
|
|
459
|
-
GATES_USER_POOL_ID=sa-east-1_xxxxxxxxx
|
|
460
|
-
GATES_CLIENT_ID=your-cognito-client-id
|
|
461
|
-
GATES_BACKEND_URL=https://your-backend-api.com
|
|
462
|
-
GATES_SYSTEM_NAME=your-system-name
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
Example usage with environment variables:
|
|
151
|
+
### Types
|
|
466
152
|
|
|
467
153
|
```typescript
|
|
468
|
-
|
|
154
|
+
type GatesUser = {
|
|
155
|
+
user_id: string; // from 'sub'
|
|
156
|
+
email?: string; // only in id_tokens
|
|
157
|
+
name?: string; // only in id_tokens
|
|
158
|
+
role?: string; // from 'custom:general_role'
|
|
159
|
+
groups: string[]; // from 'cognito:groups'
|
|
160
|
+
token_use: "access" | "id";
|
|
161
|
+
exp: number;
|
|
162
|
+
iat: number;
|
|
163
|
+
};
|
|
469
164
|
|
|
470
|
-
|
|
471
|
-
process.env.GATES_REGION!,
|
|
472
|
-
process.env.GATES_USER_POOL_ID!,
|
|
473
|
-
process.env.GATES_CLIENT_ID!
|
|
474
|
-
);
|
|
165
|
+
type GatesRole = "INTERNAL_ADMIN" | "INTERNAL_USER" | "CLIENT_ADMIN" | "CLIENT_USER";
|
|
475
166
|
|
|
476
|
-
const userService = new UserService(
|
|
477
|
-
process.env.GATES_BACKEND_URL!,
|
|
478
|
-
process.env.GATES_SYSTEM_NAME!
|
|
479
|
-
);
|
|
480
167
|
```
|
|
481
168
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
## Security Best Practices
|
|
169
|
+
### Error Codes
|
|
485
170
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
- Don't expose sensitive error details to end users
|
|
496
|
-
- Log authentication failures for security monitoring
|
|
497
|
-
- Implement rate limiting for authentication endpoints
|
|
498
|
-
|
|
499
|
-
### Configuration
|
|
500
|
-
|
|
501
|
-
- Use environment variables for sensitive configuration
|
|
502
|
-
- Validate all configuration parameters at startup
|
|
503
|
-
- Use strong, unique audience values (client IDs)
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
import {
|
|
507
|
-
GatesError,
|
|
508
|
-
TokenExpiredError,
|
|
509
|
-
UnauthorizedGroupError,
|
|
510
|
-
} from "@intelicity/gates-sdk";
|
|
511
|
-
|
|
512
|
-
// Good: Secure error handling
|
|
513
|
-
try {
|
|
514
|
-
const user = await authService.verifyToken(token);
|
|
515
|
-
} catch (error) {
|
|
516
|
-
// Log for monitoring (server-side only)
|
|
517
|
-
if (error instanceof GatesError) {
|
|
518
|
-
console.error('Auth failed:', error.code, error.constructor.name);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Return appropriate error to client based on type
|
|
522
|
-
if (error instanceof TokenExpiredError) {
|
|
523
|
-
throw new Error('Session expired');
|
|
524
|
-
}
|
|
525
|
-
if (error instanceof UnauthorizedGroupError) {
|
|
526
|
-
throw new Error('Access denied');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Generic error for other cases
|
|
530
|
-
throw new Error('Authentication failed');
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Bad: Exposing sensitive information
|
|
534
|
-
catch (error) {
|
|
535
|
-
throw new Error(`Auth failed: ${error.message}`); // May expose internal details
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Good: Check error codes for routing logic
|
|
539
|
-
try {
|
|
540
|
-
const users = await userService.getAllUsers(token);
|
|
541
|
-
} catch (error) {
|
|
542
|
-
if (error instanceof GatesError) {
|
|
543
|
-
// Safe to use error codes for client-side logic
|
|
544
|
-
if (error.code === 'TOKEN_EXPIRED') {
|
|
545
|
-
redirectToLogin();
|
|
546
|
-
} else if (error.code === 'UNAUTHORIZED_GROUP') {
|
|
547
|
-
showAccessDenied();
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
```
|
|
171
|
+
| Code | Class | Description |
|
|
172
|
+
| -------------------------- | --------------------------- | ------------------------------- |
|
|
173
|
+
| `TOKEN_EXPIRED` | `TokenExpiredError` | JWT has expired |
|
|
174
|
+
| `INVALID_TOKEN` | `InvalidTokenError` | Invalid or malformed token |
|
|
175
|
+
| `MISSING_AUTHORIZATION` | `MissingAuthorizationError` | Authorization header missing |
|
|
176
|
+
| `UNAUTHORIZED_GROUP` | `UnauthorizedGroupError` | User not in required group |
|
|
177
|
+
| `API_REQUEST_ERROR` | `ApiRequestError` | Gates API request failed |
|
|
178
|
+
| `MISSING_PARAMETER` | `MissingParameterError` | Required parameter missing |
|
|
179
|
+
| `INVALID_PARAMETER` | `InvalidParameterError` | Parameter has invalid value |
|
|
552
180
|
|
|
553
181
|
## Development
|
|
554
182
|
|
|
555
183
|
```bash
|
|
556
|
-
# Install dependencies
|
|
557
184
|
npm install
|
|
558
|
-
|
|
559
|
-
#
|
|
560
|
-
npm run
|
|
561
|
-
|
|
562
|
-
# Lint
|
|
563
|
-
npm run lint
|
|
564
|
-
|
|
565
|
-
# Type check
|
|
566
|
-
npm run typecheck
|
|
567
|
-
|
|
568
|
-
# Run tests
|
|
569
|
-
npm test
|
|
185
|
+
npm run build # tsc && tsc-alias → dist/
|
|
186
|
+
npm run typecheck # tsc --noEmit
|
|
187
|
+
npm test # vitest run
|
|
188
|
+
npm run test:watch # vitest (watch mode)
|
|
570
189
|
```
|
|
571
190
|
|
|
572
191
|
## License
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { AuthService } from "../services/auth-service.js";
|
|
2
|
+
import { GatesUser } from "../models/user.js";
|
|
3
|
+
export declare function extractToken(authorizationHeader: string | undefined): string;
|
|
4
|
+
export declare function authenticate(token: string, service: AuthService): Promise<GatesUser>;
|
|
5
|
+
export declare function authorize(user: GatesUser, requiredGroups: string | string[]): void;
|
|
6
|
+
export type AuthHandlerConfig = {
|
|
7
|
+
service: AuthService;
|
|
8
|
+
requiredGroups?: string | string[];
|
|
9
|
+
};
|
|
10
|
+
export declare function handleAuth(authorizationHeader: string | undefined, config: AuthHandlerConfig): Promise<GatesUser>;
|
|
2
11
|
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAM9C,wBAAgB,YAAY,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAa5E;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,CAAC,CAEpB;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,EACf,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,IAAI,CAaN;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACpC,CAAC;AAEF,wBAAsB,UAAU,CAC9B,mBAAmB,EAAE,MAAM,GAAG,SAAS,EACvC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,SAAS,CAAC,CASpB"}
|