@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/.turbo/turbo-build$colon$types.log +1 -1
- package/dist/MiauClient.d.ts +3 -1
- package/dist/index.js +42 -22
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/MiauClient.ts +15 -1
- package/src/middleware.ts +37 -28
package/package.json
CHANGED
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
|
|
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
|
|
51
|
+
const clientTokenData = await miauClient.verify(token, publicKey);
|
|
52
|
+
const serverTokenData = await miauClient.getTokenData();
|
|
52
53
|
|
|
53
54
|
if (
|
|
54
|
-
!
|
|
55
|
-
!
|
|
56
|
-
!
|
|
57
|
-
!
|
|
58
|
-
!
|
|
59
|
-
!
|
|
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(
|
|
62
|
+
throw new HttpError(401, 'Invalid Token', 'Token invalid or expired', 'MIAU_TKN_E');
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
const { application, secret } =
|
|
65
|
+
const { application: clientApplication, secret: clientSecret } = clientTokenData;
|
|
66
|
+
const { application: serverApplication, secret: serverSecret } = serverTokenData;
|
|
65
67
|
|
|
66
|
-
if (
|
|
68
|
+
if (clientSecret.environment != serverSecret.environment) {
|
|
67
69
|
throw new HttpError(
|
|
68
|
-
|
|
70
|
+
401,
|
|
69
71
|
'Invalid Environment',
|
|
70
|
-
`Secret environment ${
|
|
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',
|
|
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(
|
|
83
|
+
const permission = await miauClient.getPermissions(clientApplication.id);
|
|
81
84
|
|
|
82
85
|
if (!permission) {
|
|
83
|
-
throw new HttpError(
|
|
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(
|
|
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:
|
|
108
|
+
req.miauApplication = { id: clientApplication.id, name: clientApplication.name };
|
|
100
109
|
const environment = miauClient.getEnvironment();
|
|
101
110
|
req.miauMetadata = permission?.metadata?.[environment] || {};
|
|
102
111
|
|