@eduzz/miau-client 1.2.0 → 1.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eduzz/miau-client",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Eduzz Miau Client",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/MiauClient.ts CHANGED
@@ -4,7 +4,7 @@ import { type RequestHandler } from 'express';
4
4
  import jwt from 'jsonwebtoken';
5
5
  import { JwksClient } from 'jwks-rsa';
6
6
 
7
- import { type SecretEnv, type Permission, type Resource, inverseEnvMap } from '@eduzz/miau-types';
7
+ import { type SecretEnv, type Permission, type Resource, inverseEnvMap, type MiauClientToken } from '@eduzz/miau-types';
8
8
 
9
9
  import { miauMiddleware, type RequestAugmentation } from './middleware';
10
10
 
@@ -123,6 +123,16 @@ export class MiauClient {
123
123
  return this.jwtToken;
124
124
  }
125
125
 
126
+ public getTokenData = async () => {
127
+ const token = await this.getToken();
128
+
129
+ if (!token) {
130
+ throw new Error('Token is undefined');
131
+ }
132
+
133
+ return jwt.decode(token) as MiauClientToken;
134
+ };
135
+
126
136
  public middleware<T = Record<string, string>>(config?: {
127
137
  requestAugmentation?: RequestAugmentation<T>;
128
138
  fallbackMiddleware?: RequestHandler;
@@ -142,6 +152,10 @@ export class MiauClient {
142
152
  });
143
153
  }
144
154
 
155
+ public async verify(token: string, publicKey: string): Promise<MiauClientToken> {
156
+ return jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as MiauClientToken;
157
+ }
158
+
145
159
  private getApiJwtUrl = () => {
146
160
  return `${this.config.apiUrl}/v1/jwt`;
147
161
  };
package/src/middleware.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type RequestHandler, type NextFunction, type Request, type Response } from 'express';
2
2
  import jwt from 'jsonwebtoken';
3
3
 
4
- import { type Resource, type MiauApplication, type MiauClientToken, type JWTToken, issuers } from '@eduzz/miau-types';
4
+ import { type Resource, type MiauApplication, type JWTToken, issuers } from '@eduzz/miau-types';
5
5
 
6
6
  import { isResourceAllowed } from './functions';
7
7
  import type { MiauClient } from './MiauClient';
@@ -9,8 +9,8 @@ import type { MiauClient } from './MiauClient';
9
9
  class HttpError extends Error {
10
10
  public status: number;
11
11
 
12
- constructor(status: number, name: string, message: string) {
13
- super(message);
12
+ constructor(status: number, name: string, message: string, code: string) {
13
+ super(`${message} (${code})`);
14
14
  this.name = name;
15
15
  this.status = status;
16
16
 
@@ -30,73 +30,82 @@ export const miauMiddleware = <T>(
30
30
  const token = req.headers.authorization?.split(' ').pop();
31
31
 
32
32
  if (!token) {
33
- throw new HttpError(400, 'Invalid Token', 'Token not provided');
33
+ throw new HttpError(400, 'Invalid Token', 'Token not provided', 'MIAU_TKN_A');
34
34
  }
35
35
 
36
36
  const decodedToken = jwt.decode(token, { complete: true }) as JWTToken | null;
37
37
 
38
38
  if (!decodedToken) {
39
- throw new HttpError(400, 'Invalid Token', 'Token could not be decoded');
39
+ throw new HttpError(400, 'Invalid Token', 'Token could not be decoded', 'MIAU_TKN_B');
40
40
  }
41
41
 
42
42
  if (!decodedToken.header?.kid) {
43
- throw new HttpError(400, 'Invalid Token', 'Missing kid in token header');
43
+ throw new HttpError(400, 'Invalid Token', 'Missing kid in token header', 'MIAU_TKN_C');
44
44
  }
45
45
 
46
46
  if (decodedToken.payload.iss !== issuers['production']) {
47
- throw new HttpError(400, 'Invalid Token', 'Token issuer is invalid');
47
+ throw new HttpError(400, 'Invalid Token', 'Token issuer is invalid', 'MIAU_TKN_D');
48
48
  }
49
49
 
50
50
  const publicKey = await miauClient.getPublicKey(decodedToken.header.kid);
51
- const clientToken = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as MiauClientToken;
51
+ const clientTokenData = await miauClient.verify(token, publicKey);
52
+ const serverTokenData = await miauClient.getTokenData();
52
53
 
53
54
  if (
54
- !clientToken ||
55
- !clientToken.application ||
56
- !clientToken.secret ||
57
- !clientToken.application.id ||
58
- !clientToken.secret.id ||
59
- !clientToken.secret.environment
55
+ !clientTokenData ||
56
+ !clientTokenData.application ||
57
+ !clientTokenData.secret ||
58
+ !clientTokenData.application.id ||
59
+ !clientTokenData.secret.id ||
60
+ !clientTokenData.secret.environment
60
61
  ) {
61
- throw new HttpError(400, 'Invalid Token', 'Token verification failed');
62
+ throw new HttpError(401, 'Invalid Token', 'Token invalid or expired', 'MIAU_TKN_E');
62
63
  }
63
64
 
64
- const { application, secret } = clientToken;
65
+ const { application: clientApplication, secret: clientSecret } = clientTokenData;
66
+ const { application: serverApplication, secret: serverSecret } = serverTokenData;
65
67
 
66
- if (secret.environment != miauClient.getEnvironment()) {
68
+ if (clientSecret.environment != serverSecret.environment) {
67
69
  throw new HttpError(
68
- 400,
70
+ 401,
69
71
  'Invalid Environment',
70
- `Secret environment ${secret.environment} does not match client environment ${miauClient.getEnvironment()}`
72
+ `Secret environment ${clientSecret.environment} does not match Server environment ${serverSecret.environment}`,
73
+ 'MIAU_ENV_A'
71
74
  );
72
75
  }
73
76
 
74
77
  const resources = await miauClient.getResources();
75
78
 
76
79
  if (!resources) {
77
- throw new HttpError(401, 'Unauthorized', 'No resources found for this application');
80
+ throw new HttpError(401, 'Unauthorized', `No resources configured in ${serverApplication.name}`, 'MIAU_RES_A');
78
81
  }
79
82
 
80
- const permission = await miauClient.getPermissions(application.id);
83
+ const permission = await miauClient.getPermissions(clientApplication.id);
81
84
 
82
85
  if (!permission) {
83
- throw new HttpError(401, 'Unauthorized', 'No permissions found for this application');
86
+ throw new HttpError(
87
+ 401,
88
+ 'Unauthorized',
89
+ `No permissions found for ${clientApplication.name} in ${serverApplication.name}`,
90
+ 'MIAU_PERM_A'
91
+ );
84
92
  }
85
93
 
86
94
  const permittedResources = permission?.resources || [];
87
95
 
88
- if (!permittedResources.length) {
89
- throw new HttpError(403, 'Forbidden', 'No resources are permitted for this application');
90
- }
91
-
92
96
  const resource = { protocol: 'http', method: req.method, path: req.path } as Resource;
93
97
  const isAllowed = isResourceAllowed(resource, resources, permittedResources);
94
98
 
95
99
  if (!isAllowed) {
96
- throw new HttpError(403, 'Forbidden', `You do not have permission to access ${req.method} ${req.path}`);
100
+ throw new HttpError(
101
+ 403,
102
+ 'Forbidden',
103
+ `${clientApplication.name} does not have permission to ${req.method} ${req.path} on ${serverApplication.name}`,
104
+ 'MIAU_PERM_B'
105
+ );
97
106
  }
98
107
 
99
- req.miauApplication = { id: application.id, name: application.name };
108
+ req.miauApplication = { id: clientApplication.id, name: clientApplication.name };
100
109
  const environment = miauClient.getEnvironment();
101
110
  req.miauMetadata = permission?.metadata?.[environment] || {};
102
111