@intelicity/gates-sdk 0.1.4 → 0.1.6

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 CHANGED
@@ -14,8 +14,9 @@ npm install @intelicity/gates-sdk
14
14
  - 👥 User management service with backend integration
15
15
  - 🎯 TypeScript support with full type definitions
16
16
  - 💾 Built-in JWKS caching for better performance
17
- - 🎨 Custom error classes for better error handling
17
+ - 🎨 Comprehensive error hierarchy with specific error types
18
18
  - 🔐 Group-based access control
19
+ - ⚡ Detailed error messages with status codes and error codes
19
20
 
20
21
  ## Usage
21
22
 
@@ -79,7 +80,15 @@ try {
79
80
  ### Complete Integration Example
80
81
 
81
82
  ```typescript
82
- import { AuthService, UserService } from "@intelicity/gates-sdk";
83
+ import {
84
+ AuthService,
85
+ UserService,
86
+ TokenExpiredError,
87
+ UnauthorizedGroupError,
88
+ ApiRequestError,
89
+ MissingParameterError,
90
+ GatesError,
91
+ } from "@intelicity/gates-sdk";
83
92
 
84
93
  class MyApplication {
85
94
  private authService: AuthService;
@@ -114,7 +123,41 @@ class MyApplication {
114
123
  total: users.total,
115
124
  };
116
125
  } catch (error) {
117
- throw new Error(`Operation failed: ${error.message}`);
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;
118
161
  }
119
162
  }
120
163
  }
@@ -122,44 +165,98 @@ class MyApplication {
122
165
 
123
166
  ### Error Handling
124
167
 
125
- The SDK provides custom error classes for better error handling:
168
+ The SDK provides a comprehensive error hierarchy for better error handling:
126
169
 
127
170
  ```typescript
128
171
  import {
129
172
  AuthService,
130
173
  UserService,
174
+ // Base errors
175
+ GatesError,
176
+ // Authentication errors
131
177
  AuthenticationError,
132
178
  TokenExpiredError,
133
179
  InvalidTokenError,
134
180
  MissingAuthorizationError,
181
+ UnauthorizedGroupError,
182
+ // API errors
183
+ ApiError,
184
+ ApiRequestError,
185
+ InvalidResponseError,
186
+ // Parameter errors
187
+ MissingParameterError,
188
+ InvalidParameterError,
135
189
  } from "@intelicity/gates-sdk";
136
190
 
137
- const authService = new AuthService(region, userPoolId, audience);
191
+ const authService = new AuthService(region, userPoolId, audience, ["admin"]);
138
192
 
193
+ // Authentication Error Handling
139
194
  try {
140
195
  const user = await authService.verifyToken(token);
196
+ console.log("User authenticated:", user);
141
197
  } catch (error) {
142
198
  if (error instanceof TokenExpiredError) {
143
199
  console.error("Token expired, please login again");
200
+ // error.code === "TOKEN_EXPIRED"
144
201
  } else if (error instanceof InvalidTokenError) {
145
- console.error("Invalid token provided");
146
- } else if (error instanceof MissingAuthorizationError) {
147
- console.error("No authorization token provided");
148
- } else {
202
+ console.error("Invalid token:", error.message);
203
+ // error.code === "INVALID_TOKEN"
204
+ } else if (error instanceof UnauthorizedGroupError) {
205
+ console.error("Access denied:", error.message);
206
+ console.error("Required groups:", error.requiredGroups);
207
+ // error.code === "UNAUTHORIZED_GROUP"
208
+ } else if (error instanceof MissingParameterError) {
209
+ console.error("Missing parameter:", error.message);
210
+ // error.code === "MISSING_PARAMETER"
211
+ } else if (error instanceof AuthenticationError) {
149
212
  console.error("Authentication failed:", error.message);
150
213
  }
151
214
  }
152
215
 
153
- // Error handling with User Service
216
+ // User Service Error Handling
217
+ const userService = new UserService(backendUrl, systemName);
218
+
154
219
  try {
155
220
  const users = await userService.getAllUsers(token);
221
+ console.log("Users fetched:", users.profiles.length);
156
222
  } catch (error) {
157
- if (error.message.includes("HTTP 401")) {
158
- console.error("Unauthorized - check your token");
159
- } else if (error.message.includes("HTTP 403")) {
160
- console.error("Forbidden - insufficient permissions");
161
- } else {
162
- console.error("Failed to fetch users:", error.message);
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);
240
+ } else if (error instanceof GatesError) {
241
+ console.error("Gates SDK error:", error.message, error.code);
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
+ }
163
260
  }
164
261
  }
165
262
  ```
@@ -275,10 +372,82 @@ type VerifyOptions = {
275
372
 
276
373
  #### Custom Errors
277
374
 
278
- - `AuthenticationError`: Base authentication error class
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`)
279
385
  - `TokenExpiredError`: Token has expired
386
+ - Code: `TOKEN_EXPIRED`
280
387
  - `InvalidTokenError`: Token is invalid or malformed
388
+ - Code: `INVALID_TOKEN`
281
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`
411
+
412
+ ### Error Codes Reference
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:**
428
+
429
+ ```typescript
430
+ try {
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
+ }
450
+ ```
282
451
 
283
452
  ## Environment Variables
284
453
 
@@ -334,14 +503,30 @@ const userService = new UserService(
334
503
  - Use strong, unique audience values (client IDs)
335
504
 
336
505
  ```typescript
506
+ import {
507
+ GatesError,
508
+ TokenExpiredError,
509
+ UnauthorizedGroupError,
510
+ } from "@intelicity/gates-sdk";
511
+
337
512
  // Good: Secure error handling
338
513
  try {
339
514
  const user = await authService.verifyToken(token);
340
515
  } catch (error) {
341
516
  // Log for monitoring (server-side only)
342
- console.error('Auth failed:', error.constructor.name);
517
+ if (error instanceof GatesError) {
518
+ console.error('Auth failed:', error.code, error.constructor.name);
519
+ }
343
520
 
344
- // Return generic error to client
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
345
530
  throw new Error('Authentication failed');
346
531
  }
347
532
 
@@ -349,6 +534,20 @@ try {
349
534
  catch (error) {
350
535
  throw new Error(`Auth failed: ${error.message}`); // May expose internal details
351
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
+ }
352
551
  ```
353
552
 
354
553
  ## Development
@@ -1,10 +1,16 @@
1
1
  /**
2
- * Base error class for authentication-related errors
2
+ * Base error class for all Gates SDK errors
3
3
  */
4
- export declare class AuthenticationError extends Error {
4
+ export declare class GatesError extends Error {
5
5
  readonly code?: string | undefined;
6
6
  constructor(message: string, code?: string | undefined);
7
7
  }
8
+ /**
9
+ * Base error class for authentication-related errors
10
+ */
11
+ export declare class AuthenticationError extends GatesError {
12
+ constructor(message: string, code?: string);
13
+ }
8
14
  /**
9
15
  * Error thrown when a token has expired
10
16
  */
@@ -23,4 +29,42 @@ export declare class InvalidTokenError extends AuthenticationError {
23
29
  export declare class MissingAuthorizationError extends AuthenticationError {
24
30
  constructor(message?: string);
25
31
  }
32
+ /**
33
+ * Error thrown when user is not a member of required group
34
+ */
35
+ export declare class UnauthorizedGroupError extends AuthenticationError {
36
+ readonly requiredGroups: string[];
37
+ constructor(message: string, requiredGroups: string[]);
38
+ }
39
+ /**
40
+ * Base error class for API-related errors
41
+ */
42
+ export declare class ApiError extends GatesError {
43
+ readonly statusCode?: number | undefined;
44
+ constructor(message: string, statusCode?: number | undefined, code?: string);
45
+ }
46
+ /**
47
+ * Error thrown when API request fails
48
+ */
49
+ export declare class ApiRequestError extends ApiError {
50
+ constructor(message: string, statusCode?: number);
51
+ }
52
+ /**
53
+ * Error thrown when API response is invalid
54
+ */
55
+ export declare class InvalidResponseError extends ApiError {
56
+ constructor(message?: string);
57
+ }
58
+ /**
59
+ * Error thrown when a required parameter is missing
60
+ */
61
+ export declare class MissingParameterError extends GatesError {
62
+ constructor(parameterName: string);
63
+ }
64
+ /**
65
+ * Error thrown when a parameter has an invalid value
66
+ */
67
+ export declare class InvalidParameterError extends GatesError {
68
+ constructor(parameterName: string, reason?: string);
69
+ }
26
70
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/errors/error.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aACC,IAAI,CAAC,EAAE,MAAM;gBAA9C,OAAO,EAAE,MAAM,EAAkB,IAAI,CAAC,EAAE,MAAM,YAAA;CAK3D;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC5C,OAAO,SAAsB;CAK1C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC5C,OAAO,SAAkB;CAKtC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,mBAAmB;gBACpD,OAAO,SAAiC;CAKrD"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/errors/error.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;aACU,IAAI,CAAC,EAAE,MAAM;gBAA9C,OAAO,EAAE,MAAM,EAAkB,IAAI,CAAC,EAAE,MAAM,YAAA;CAK3D;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAK3C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC5C,OAAO,SAAmB;CAKvC;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;gBAC5C,OAAO,SAAmB;CAKvC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,mBAAmB;gBACpD,OAAO,SAAwC;CAK5D;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,mBAAmB;aAChB,cAAc,EAAE,MAAM,EAAE;gBAAzD,OAAO,EAAE,MAAM,EAAkB,cAAc,EAAE,MAAM,EAAE;CAKtE;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,UAAU;aAGpB,UAAU,CAAC,EAAE,MAAM;gBADnC,OAAO,EAAE,MAAM,EACC,UAAU,CAAC,EAAE,MAAM,YAAA,EACnC,IAAI,CAAC,EAAE,MAAM;CAMhB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ;gBACpC,OAAO,SAAwC;CAK5D;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;gBACvC,aAAa,EAAE,MAAM;CAQlC;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;gBACvC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQnD"}
@@ -1,12 +1,22 @@
1
1
  // src/errors/error.ts
2
2
  /**
3
- * Base error class for authentication-related errors
3
+ * Base error class for all Gates SDK errors
4
4
  */
5
- export class AuthenticationError extends Error {
5
+ export class GatesError extends Error {
6
6
  code;
7
7
  constructor(message, code) {
8
8
  super(message);
9
9
  this.code = code;
10
+ this.name = "GatesError";
11
+ Object.setPrototypeOf(this, GatesError.prototype);
12
+ }
13
+ }
14
+ /**
15
+ * Base error class for authentication-related errors
16
+ */
17
+ export class AuthenticationError extends GatesError {
18
+ constructor(message, code) {
19
+ super(message, code);
10
20
  this.name = "AuthenticationError";
11
21
  Object.setPrototypeOf(this, AuthenticationError.prototype);
12
22
  }
@@ -15,7 +25,7 @@ export class AuthenticationError extends Error {
15
25
  * Error thrown when a token has expired
16
26
  */
17
27
  export class TokenExpiredError extends AuthenticationError {
18
- constructor(message = "Token has expired") {
28
+ constructor(message = "Token expirado") {
19
29
  super(message, "TOKEN_EXPIRED");
20
30
  this.name = "TokenExpiredError";
21
31
  Object.setPrototypeOf(this, TokenExpiredError.prototype);
@@ -25,7 +35,7 @@ export class TokenExpiredError extends AuthenticationError {
25
35
  * Error thrown when a token is invalid
26
36
  */
27
37
  export class InvalidTokenError extends AuthenticationError {
28
- constructor(message = "Invalid token") {
38
+ constructor(message = "Token inválido") {
29
39
  super(message, "INVALID_TOKEN");
30
40
  this.name = "InvalidTokenError";
31
41
  Object.setPrototypeOf(this, InvalidTokenError.prototype);
@@ -35,9 +45,76 @@ export class InvalidTokenError extends AuthenticationError {
35
45
  * Error thrown when authorization header is missing
36
46
  */
37
47
  export class MissingAuthorizationError extends AuthenticationError {
38
- constructor(message = "Missing Authorization header") {
48
+ constructor(message = "Header de autorização não fornecido") {
39
49
  super(message, "MISSING_AUTHORIZATION");
40
50
  this.name = "MissingAuthorizationError";
41
51
  Object.setPrototypeOf(this, MissingAuthorizationError.prototype);
42
52
  }
43
53
  }
54
+ /**
55
+ * Error thrown when user is not a member of required group
56
+ */
57
+ export class UnauthorizedGroupError extends AuthenticationError {
58
+ requiredGroups;
59
+ constructor(message, requiredGroups) {
60
+ super(message, "UNAUTHORIZED_GROUP");
61
+ this.requiredGroups = requiredGroups;
62
+ this.name = "UnauthorizedGroupError";
63
+ Object.setPrototypeOf(this, UnauthorizedGroupError.prototype);
64
+ }
65
+ }
66
+ /**
67
+ * Base error class for API-related errors
68
+ */
69
+ export class ApiError extends GatesError {
70
+ statusCode;
71
+ constructor(message, statusCode, code) {
72
+ super(message, code);
73
+ this.statusCode = statusCode;
74
+ this.name = "ApiError";
75
+ Object.setPrototypeOf(this, ApiError.prototype);
76
+ }
77
+ }
78
+ /**
79
+ * Error thrown when API request fails
80
+ */
81
+ export class ApiRequestError extends ApiError {
82
+ constructor(message, statusCode) {
83
+ super(message, statusCode, "API_REQUEST_ERROR");
84
+ this.name = "ApiRequestError";
85
+ Object.setPrototypeOf(this, ApiRequestError.prototype);
86
+ }
87
+ }
88
+ /**
89
+ * Error thrown when API response is invalid
90
+ */
91
+ export class InvalidResponseError extends ApiError {
92
+ constructor(message = "Formato de resposta da API inválido") {
93
+ super(message, undefined, "INVALID_RESPONSE");
94
+ this.name = "InvalidResponseError";
95
+ Object.setPrototypeOf(this, InvalidResponseError.prototype);
96
+ }
97
+ }
98
+ /**
99
+ * Error thrown when a required parameter is missing
100
+ */
101
+ export class MissingParameterError extends GatesError {
102
+ constructor(parameterName) {
103
+ super(`Parâmetro obrigatório ausente: ${parameterName}`, "MISSING_PARAMETER");
104
+ this.name = "MissingParameterError";
105
+ Object.setPrototypeOf(this, MissingParameterError.prototype);
106
+ }
107
+ }
108
+ /**
109
+ * Error thrown when a parameter has an invalid value
110
+ */
111
+ export class InvalidParameterError extends GatesError {
112
+ constructor(parameterName, reason) {
113
+ const message = reason
114
+ ? `Parâmetro '${parameterName}' inválido: ${reason}`
115
+ : `Parâmetro inválido: ${parameterName}`;
116
+ super(message, "INVALID_PARAMETER");
117
+ this.name = "InvalidParameterError";
118
+ Object.setPrototypeOf(this, InvalidParameterError.prototype);
119
+ }
120
+ }
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@ export type { GatesUser as GatesUser } from "./models/user.js";
2
2
  export type { UserProfile as Profile, ProfileAttribute, } from "./models/profile.js";
3
3
  export { GatesUserService as UserService, type UserListResponse, type GetAllUsersOptions, } from "./services/user-service.js";
4
4
  export { GatesAuthService as AuthService, type VerifyOptions, } from "./services/auth-service.js";
5
- export { AuthenticationError, TokenExpiredError, InvalidTokenError, MissingAuthorizationError, } from "./errors/error.js";
5
+ export { GatesError, AuthenticationError, TokenExpiredError, InvalidTokenError, MissingAuthorizationError, UnauthorizedGroupError, ApiError, ApiRequestError, InvalidResponseError, MissingParameterError, InvalidParameterError, } from "./errors/error.js";
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,SAAS,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EACV,WAAW,IAAI,OAAO,EACtB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,IAAI,WAAW,EAC/B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,IAAI,WAAW,EAC/B,KAAK,aAAa,GACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,SAAS,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EACV,WAAW,IAAI,OAAO,EACtB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,gBAAgB,IAAI,WAAW,EAC/B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,IAAI,WAAW,EAC/B,KAAK,aAAa,GACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAEL,UAAU,EAGV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EAGtB,QAAQ,EACR,eAAe,EACf,oBAAoB,EAGpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,14 @@
1
+ // Main exports for Gates SDK
2
+ // Services
1
3
  export { GatesUserService as UserService, } from "./services/user-service.js";
2
4
  export { GatesAuthService as AuthService, } from "./services/auth-service.js";
3
- export { AuthenticationError, TokenExpiredError, InvalidTokenError, MissingAuthorizationError, } from "./errors/error.js";
5
+ // Errors
6
+ export {
7
+ // Base errors
8
+ GatesError,
9
+ // Authentication errors
10
+ AuthenticationError, TokenExpiredError, InvalidTokenError, MissingAuthorizationError, UnauthorizedGroupError,
11
+ // API errors
12
+ ApiError, ApiRequestError, InvalidResponseError,
13
+ // Parameter errors
14
+ MissingParameterError, InvalidParameterError, } from "./errors/error.js";
@@ -1 +1 @@
1
- {"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACnC,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoB;gBAGjD,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAiCnC,OAAO,KAAK,MAAM,GAEjB;IAED,UAAU,CAAC,MAAM,GAAE,MAAM,EAAO,GAAG,OAAO;IAgBpC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CAiDrD"}
1
+ {"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAS9C,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACnC,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoB;gBAGjD,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAoCnC,OAAO,KAAK,MAAM,GAEjB;IAED,UAAU,CAAC,MAAM,GAAE,MAAM,EAAO,GAAG,OAAO;IAgBpC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CA0FrD"}
@@ -1,5 +1,6 @@
1
- import { jwtVerify } from "jose";
1
+ import { jwtVerify, errors as joseErrors } from "jose";
2
2
  import { getJwks } from "../cache/jwks-cache.js";
3
+ import { InvalidParameterError, MissingParameterError, TokenExpiredError, InvalidTokenError, UnauthorizedGroupError, } from "../errors/error.js";
3
4
  export class GatesAuthService {
4
5
  region;
5
6
  userPoolId;
@@ -7,21 +8,21 @@ export class GatesAuthService {
7
8
  requiredGroup;
8
9
  constructor(region, userPoolId, audience, requiredGroup) {
9
10
  if (!region || typeof region !== "string" || region.trim().length === 0) {
10
- throw new Error("Region é obrigatória e deve ser uma string válida");
11
+ throw new MissingParameterError("region");
11
12
  }
12
13
  if (!userPoolId ||
13
14
  typeof userPoolId !== "string" ||
14
15
  userPoolId.trim().length === 0) {
15
- throw new Error("UserPoolId é obrigatório e deve ser uma string válida");
16
+ throw new MissingParameterError("userPoolId");
16
17
  }
17
18
  if (!audience ||
18
19
  typeof audience !== "string" ||
19
20
  audience.trim().length === 0) {
20
- throw new Error("Audience é obrigatória e deve ser uma string válida");
21
+ throw new MissingParameterError("audience");
21
22
  }
22
23
  // Validar formato do userPoolId (deve seguir padrão AWS)
23
24
  if (!/^[a-zA-Z0-9_-]+$/.test(userPoolId)) {
24
- throw new Error("UserPoolId possui formato inválido");
25
+ throw new InvalidParameterError("userPoolId", "deve seguir o formato AWS (apenas alfanuméricos, hífens e underscores)");
25
26
  }
26
27
  this.region = region;
27
28
  this.userPoolId = userPoolId;
@@ -45,36 +46,64 @@ export class GatesAuthService {
45
46
  }
46
47
  async verifyToken(token) {
47
48
  if (!token) {
48
- throw new Error("Token não fornecido");
49
+ throw new MissingParameterError("token");
49
50
  }
50
- const jwks = getJwks(this.region, this.userPoolId);
51
- const { payload } = await jwtVerify(token, jwks, {
52
- issuer: this.issuer,
53
- audience: this.audience,
54
- });
55
- if (this.requiredGroup) {
56
- const userGroups = payload["cognito:groups"];
57
- if (!userGroups || !Array.isArray(userGroups)) {
58
- throw new Error("Usuário não pertence ao sistema");
51
+ try {
52
+ const jwks = getJwks(this.region, this.userPoolId);
53
+ const { payload } = await jwtVerify(token, jwks, {
54
+ issuer: this.issuer,
55
+ audience: this.audience,
56
+ });
57
+ if (this.requiredGroup) {
58
+ const userGroups = payload["cognito:groups"];
59
+ const requiredGroups = Array.isArray(this.requiredGroup)
60
+ ? this.requiredGroup
61
+ : [this.requiredGroup];
62
+ if (!userGroups || !Array.isArray(userGroups)) {
63
+ throw new UnauthorizedGroupError("Usuário não pertence a nenhum grupo obrigatório", requiredGroups);
64
+ }
65
+ // Verifica se o usuário tem pelo menos um dos grupos obrigatórios
66
+ const hasRequiredGroup = requiredGroups.some((group) => userGroups.includes(group));
67
+ if (!hasRequiredGroup) {
68
+ throw new UnauthorizedGroupError(`Usuário deve ser membro de um dos seguintes grupos: ${requiredGroups.join(", ")}`, requiredGroups);
69
+ }
59
70
  }
60
- const requiredGroups = Array.isArray(this.requiredGroup)
61
- ? this.requiredGroup
62
- : [this.requiredGroup];
63
- // Verifica se o usuário tem pelo menos um dos grupos obrigatórios
64
- const hasRequiredGroup = requiredGroups.some((group) => userGroups.includes(group));
65
- if (!hasRequiredGroup) {
66
- throw new Error(`Usuário deve ser membro de um dos seguintes sistemas: ${requiredGroups.join(", ")}`);
71
+ // Mapear o payload do Cognito para o formato do GatesUser
72
+ const user = {
73
+ user_id: payload.sub,
74
+ email: payload.email,
75
+ name: payload.name,
76
+ role: payload["custom:general_role"],
77
+ exp: payload.exp,
78
+ iat: payload.iat,
79
+ };
80
+ return user;
81
+ }
82
+ catch (error) {
83
+ // Re-throw known errors
84
+ if (error instanceof UnauthorizedGroupError ||
85
+ error instanceof MissingParameterError) {
86
+ throw error;
87
+ }
88
+ // Handle jose-specific errors
89
+ if (error instanceof joseErrors.JWTExpired) {
90
+ throw new TokenExpiredError("Token expirado");
91
+ }
92
+ if (error instanceof joseErrors.JWTInvalid) {
93
+ throw new InvalidTokenError("Token inválido ou malformado");
94
+ }
95
+ // Handle other jose errors
96
+ if (error instanceof Error) {
97
+ if (error.message.includes("expired")) {
98
+ throw new TokenExpiredError(error.message);
99
+ }
100
+ if (error.message.includes("signature") ||
101
+ error.message.includes("invalid")) {
102
+ throw new InvalidTokenError(error.message);
103
+ }
104
+ throw new InvalidTokenError(`Falha na verificação do token: ${error.message}`);
67
105
  }
106
+ throw new InvalidTokenError("Falha na verificação do token");
68
107
  }
69
- // Mapear o payload do Cognito para o formato do GatesUser
70
- const user = {
71
- user_id: payload.sub,
72
- email: payload.email,
73
- name: payload.name,
74
- role: payload["custom:general_role"],
75
- exp: payload.exp,
76
- iat: payload.iat,
77
- };
78
- return user;
79
108
  }
80
109
  }
@@ -27,9 +27,9 @@ export declare class GatesUserService {
27
27
  getAllUsers(idToken: string): Promise<UserListResponse>;
28
28
  /**
29
29
  * Busca um usuário específico por ID
30
- * @param accessToken Token de autenticação
30
+ * @param idToken Token de autenticação
31
31
  * @returns Dados do usuário
32
32
  */
33
- login(accessToken: string): Promise<UserProfile>;
33
+ getProfile(idToken: string): Promise<UserProfile>;
34
34
  }
35
35
  //# sourceMappingURL=user-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user-service.d.ts","sourceRoot":"","sources":["../../src/services/user-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAoB,MAAM,sBAAsB,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgBD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAQ3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAExB;IAEF;;;;;OAKG;IACG,WAAW,CACf,OAAO,EAAE,MAAM,GAEd,OAAO,CAAC,gBAAgB,CAAC;IAiD5B;;;;OAIG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CA2CvD"}
1
+ {"version":3,"file":"user-service.d.ts","sourceRoot":"","sources":["../../src/services/user-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAoB,MAAM,sBAAsB,CAAC;AAOrE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAQ3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAExB;IAEF;;;;;OAKG;IACG,WAAW,CACf,OAAO,EAAE,MAAM,GAEd,OAAO,CAAC,gBAAgB,CAAC;IAqE5B;;;;OAIG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CAuExD"}
@@ -1,3 +1,4 @@
1
+ import { MissingParameterError, ApiRequestError, InvalidResponseError, } from "../errors/error.js";
1
2
  export class GatesUserService {
2
3
  baseUrl;
3
4
  system;
@@ -10,7 +11,7 @@ export class GatesUserService {
10
11
  };
11
12
  }
12
13
  endpoints = {
13
- all: "/get-all-profiles-from-system",
14
+ all: "get-all-profiles-from-system",
14
15
  };
15
16
  /**
16
17
  * Busca todos os usuários do Cognito através do backend
@@ -22,7 +23,7 @@ export class GatesUserService {
22
23
  // options: GetAllUsersOptions = {}
23
24
  ) {
24
25
  if (!idToken) {
25
- throw new Error("ID Token is required");
26
+ throw new MissingParameterError("idToken");
26
27
  }
27
28
  try {
28
29
  const queryParams = new URLSearchParams();
@@ -35,7 +36,7 @@ export class GatesUserService {
35
36
  method: "POST",
36
37
  headers: {
37
38
  ...this.defaultHeaders,
38
- Authorization: `Bearer ${idToken}`,
39
+ Authorization: `${idToken}`,
39
40
  },
40
41
  body: JSON.stringify({
41
42
  system_name: this.system,
@@ -43,9 +44,12 @@ export class GatesUserService {
43
44
  });
44
45
  if (!response.ok) {
45
46
  const errorText = await response.text();
46
- throw new Error(`HTTP ${response.status}: ${errorText}`);
47
+ throw new ApiRequestError(`Falha ao buscar usuários: ${errorText}`, response.status);
47
48
  }
48
49
  const data = (await response.json());
50
+ if (!data.profiles || !Array.isArray(data.profiles)) {
51
+ throw new InvalidResponseError("Resposta da API não contém o array 'profiles'");
52
+ }
49
53
  // Adicionar total baseado no length se não vier da API
50
54
  if (!data.total && data.profiles) {
51
55
  data.total = data.profiles.length;
@@ -53,53 +57,69 @@ export class GatesUserService {
53
57
  return data;
54
58
  }
55
59
  catch (error) {
60
+ // Re-throw known errors
61
+ if (error instanceof MissingParameterError ||
62
+ error instanceof ApiRequestError ||
63
+ error instanceof InvalidResponseError) {
64
+ throw error;
65
+ }
66
+ // Handle fetch errors
56
67
  if (error instanceof Error) {
57
- throw new Error(`Failed to fetch users: ${error.message}`);
68
+ throw new ApiRequestError(`Falha ao buscar usuários: ${error.message}`);
58
69
  }
59
- throw new Error("Failed to fetch users: Unknown error");
70
+ throw new ApiRequestError("Falha ao buscar usuários: Erro desconhecido");
60
71
  }
61
72
  }
62
73
  /**
63
74
  * Busca um usuário específico por ID
64
- * @param accessToken Token de autenticação
75
+ * @param idToken Token de autenticação
65
76
  * @returns Dados do usuário
66
77
  */
67
- async login(accessToken) {
68
- if (!accessToken) {
69
- throw new Error("Access Token is required");
78
+ async getProfile(idToken) {
79
+ if (!idToken) {
80
+ throw new MissingParameterError("idToken");
70
81
  }
71
82
  try {
72
- const response = await fetch(`${this.baseUrl}/login`, {
73
- method: "POST",
83
+ const response = await fetch(`${this.baseUrl}/get-profile?system_name=${this.system}`, {
84
+ method: "GET",
74
85
  headers: {
75
86
  ...this.defaultHeaders,
76
- Authorization: `${accessToken}`,
87
+ Authorization: `${idToken}`,
77
88
  },
78
- body: JSON.stringify({ system_name: this.system }),
79
89
  });
80
90
  if (!response.ok) {
81
91
  const errorText = await response.text();
82
- throw new Error(`HTTP ${response.status}: ${errorText}`);
92
+ throw new ApiRequestError(`Falha ao buscar perfil do usuário: ${errorText}`, response.status);
83
93
  }
84
94
  const data = (await response.json());
85
- // A API retorna { user: {...}, message: "..." }
86
- if (!data.user) {
87
- throw new Error("Invalid response format: missing user data");
95
+ // A API retorna { profile: {...}, email: ..., name: ..., message: "..." }
96
+ if (!data.profile) {
97
+ throw new InvalidResponseError("Resposta da API não contém os dados do 'profile'");
98
+ }
99
+ if (!data.email || !data.name) {
100
+ throw new InvalidResponseError("Resposta da API não contém os campos obrigatórios do usuário (email ou name)");
88
101
  }
89
102
  const userProfile = {
90
- user_id: data.user.user_id,
91
- email: data.user.email,
92
- name: data.user.name,
93
- enabled: data.user.enabled,
94
- profile_attributes: data.user.profile_attributes || [],
103
+ user_id: data.profile.user_id,
104
+ email: data.email,
105
+ name: data.name,
106
+ enabled: data.profile.enabled,
107
+ profile_attributes: data.profile.profile_attributes || [],
95
108
  };
96
109
  return userProfile;
97
110
  }
98
111
  catch (error) {
112
+ // Re-throw known errors
113
+ if (error instanceof MissingParameterError ||
114
+ error instanceof ApiRequestError ||
115
+ error instanceof InvalidResponseError) {
116
+ throw error;
117
+ }
118
+ // Handle fetch errors
99
119
  if (error instanceof Error) {
100
- throw new Error(`Failed to fetch user: ${error.message}`);
120
+ throw new ApiRequestError(`Falha ao buscar perfil do usuário: ${error.message}`);
101
121
  }
102
- throw new Error("Failed to fetch user: Unknown error");
122
+ throw new ApiRequestError("Falha ao buscar perfil do usuário: Erro desconhecido");
103
123
  }
104
124
  }
105
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelicity/gates-sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Simple SDK for authenticating users with AWS Cognito JWT tokens",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",