@eduzz/miau-client 0.0.11 → 0.0.13
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/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-prepublish.log +1 -1
- package/dist/MiauClient.d.ts +3 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +70 -48
- package/dist/index.js.map +4 -4
- package/dist/miau-types/types/Application.d.ts +1 -1
- package/dist/miau-types/types/Permission.d.ts +1 -1
- package/dist/miau-types/types/Resource.d.ts +1 -1
- package/dist/middleware.d.ts +9 -0
- package/package.json +1 -1
- package/src/MiauClient.ts +10 -4
- package/src/index.ts +0 -1
- package/src/middleware.ts +93 -0
- package/dist/MiauMiddleware.d.ts +0 -3
- package/src/MiauMiddleware.ts +0 -61
|
@@ -3,7 +3,7 @@ export declare const ResourceTypes: ResourceType[];
|
|
|
3
3
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
4
4
|
export declare const HttpMethods: HttpMethod[];
|
|
5
5
|
export type Resource = {
|
|
6
|
-
id
|
|
6
|
+
id?: string;
|
|
7
7
|
type: ResourceType;
|
|
8
8
|
method: HttpMethod;
|
|
9
9
|
path: string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RequestHandler, type NextFunction, type Request, type Response } from 'express';
|
|
2
|
+
import type { MiauApplication } from '@eduzz/miau-types';
|
|
3
|
+
import type { MiauClient } from './MiauClient';
|
|
4
|
+
export type RequestAugmentation<T> = (data: {
|
|
5
|
+
req: Request;
|
|
6
|
+
app: MiauApplication;
|
|
7
|
+
meta: T;
|
|
8
|
+
}) => void;
|
|
9
|
+
export declare const miauMiddleware: <T>(miauClient: MiauClient, requestAugmentation?: RequestAugmentation<T>, fallbackMidlleware?: RequestHandler) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
package/package.json
CHANGED
package/src/MiauClient.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import { type RequestHandler } from 'express';
|
|
1
2
|
import jwt from 'jsonwebtoken';
|
|
2
3
|
import { JwksClient } from 'jwks-rsa';
|
|
3
4
|
|
|
4
5
|
import { type Permission } from '@eduzz/miau-types';
|
|
5
6
|
|
|
6
|
-
type
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
appSecret: string;
|
|
9
|
-
};
|
|
7
|
+
import { miauMiddleware, type RequestAugmentation } from './middleware';
|
|
10
8
|
|
|
9
|
+
type MiauClientConfig = { apiUrl: string; appSecret: string };
|
|
11
10
|
type FetchInput = Parameters<typeof fetch>[0];
|
|
12
11
|
type FetchInit = Parameters<typeof fetch>[1];
|
|
13
12
|
|
|
@@ -72,6 +71,13 @@ export class MiauClient {
|
|
|
72
71
|
return this.jwtToken;
|
|
73
72
|
}
|
|
74
73
|
|
|
74
|
+
public middleware<T = Record<string, string>>(
|
|
75
|
+
requestAugmentation?: RequestAugmentation<T>,
|
|
76
|
+
fallbackMidlleware?: RequestHandler
|
|
77
|
+
): RequestHandler {
|
|
78
|
+
return miauMiddleware<T>(this, requestAugmentation, fallbackMidlleware);
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
public async getPermissions(targetAppId: string) {
|
|
76
82
|
if (this.permissionsCache.has(targetAppId)) {
|
|
77
83
|
const { data, expiresAt } = this.permissionsCache.get(targetAppId)!;
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type RequestHandler, type NextFunction, type Request, type Response } from 'express';
|
|
2
|
+
import jwt from 'jsonwebtoken';
|
|
3
|
+
|
|
4
|
+
import type { MiauApplication, Resource } from '@eduzz/miau-types';
|
|
5
|
+
|
|
6
|
+
import type { MiauClient } from './MiauClient';
|
|
7
|
+
|
|
8
|
+
class HttpError extends Error {
|
|
9
|
+
public status: number;
|
|
10
|
+
|
|
11
|
+
constructor(status: number, name: string, message: string) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = name;
|
|
14
|
+
this.status = status;
|
|
15
|
+
|
|
16
|
+
Object.setPrototypeOf(this, HttpError.prototype);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type RequestAugmentation<T> = (data: { req: Request; app: MiauApplication; meta: T }) => void;
|
|
21
|
+
|
|
22
|
+
const wildcardToRegex = (pattern: string) => {
|
|
23
|
+
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&');
|
|
24
|
+
const withWildcards = escaped.replace(/\*/g, '[^/]+');
|
|
25
|
+
const regexStr = `^${withWildcards}$`;
|
|
26
|
+
return new RegExp(regexStr);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const miauMiddleware = <T>(
|
|
30
|
+
miauClient: MiauClient,
|
|
31
|
+
requestAugmentation?: RequestAugmentation<T>,
|
|
32
|
+
fallbackMidlleware?: RequestHandler
|
|
33
|
+
) => {
|
|
34
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
35
|
+
try {
|
|
36
|
+
const token = req.headers.authorization?.split(' ').pop();
|
|
37
|
+
|
|
38
|
+
if (!token) {
|
|
39
|
+
throw new HttpError(401, 'Invalid Token', 'Token not provided');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const decodedToken = jwt.decode(token, { complete: true }) as { header: { kid: string } };
|
|
43
|
+
|
|
44
|
+
if (!decodedToken?.header?.kid) {
|
|
45
|
+
throw new HttpError(401, 'Invalid Token', 'Missing kid in token header');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const publicKey = await miauClient.getPublicKey(decodedToken.header.kid);
|
|
49
|
+
const appToken = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as MiauApplication;
|
|
50
|
+
|
|
51
|
+
if (!appToken || !appToken.id || !appToken.name) {
|
|
52
|
+
throw new HttpError(401, 'Invalid Token', 'Token verification failed');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const permission = await miauClient.getPermissions(appToken.id);
|
|
56
|
+
|
|
57
|
+
if (!permission) {
|
|
58
|
+
res.status(401).json({ error: 'Unauthorized', message: 'No permissions found for this application' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const resources = permission?.resources || [];
|
|
62
|
+
const isAllowed = resources.some((resource: Resource) => {
|
|
63
|
+
return (
|
|
64
|
+
resource.method.toLowerCase() === req.method.toLowerCase() &&
|
|
65
|
+
wildcardToRegex(resource.path).test(req.path.toLowerCase())
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!isAllowed) {
|
|
70
|
+
throw new HttpError(403, 'Forbidden', `You do not have permission to access ${req.method} ${req.path}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
req.miauApplication = { id: appToken?.id, name: appToken?.name };
|
|
74
|
+
req.miauMetadata = permission?.metadata || {};
|
|
75
|
+
|
|
76
|
+
if (requestAugmentation) {
|
|
77
|
+
console.log('Request augmentation is being applied');
|
|
78
|
+
requestAugmentation({ req, app: req.miauApplication, meta: req.miauMetadata as T });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
next();
|
|
82
|
+
} catch (err: HttpError | any) {
|
|
83
|
+
if (err instanceof HttpError && err.status == 401 && fallbackMidlleware) {
|
|
84
|
+
return fallbackMidlleware(req, res, next);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const errorStatus = err.status || 403;
|
|
88
|
+
|
|
89
|
+
res.status(errorStatus).json({ error: err.name, message: err.message });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
package/dist/MiauMiddleware.d.ts
DELETED
package/src/MiauMiddleware.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { type NextFunction, type Request, type Response } from 'express';
|
|
2
|
-
import jwt from 'jsonwebtoken';
|
|
3
|
-
|
|
4
|
-
import type { MiauApplication, Resource } from '@eduzz/miau-types';
|
|
5
|
-
|
|
6
|
-
import type { MiauClient } from './MiauClient';
|
|
7
|
-
|
|
8
|
-
const wildcardToRegex = (pattern: string) => {
|
|
9
|
-
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&');
|
|
10
|
-
const withWildcards = escaped.replace(/\*/g, '[^/]+');
|
|
11
|
-
const regexStr = `^${withWildcards}$`;
|
|
12
|
-
return new RegExp(regexStr);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const miauMiddleware = (miauService: MiauClient) => {
|
|
16
|
-
return async (req: Request, res: Response, next: NextFunction) => {
|
|
17
|
-
try {
|
|
18
|
-
const token = req.headers.authorization?.split(' ').pop();
|
|
19
|
-
|
|
20
|
-
if (!token) {
|
|
21
|
-
res.status(401).json({ error: 'Invalid Token', message: 'Token not provided' });
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const decodedToken = jwt.decode(token, { complete: true }) as { header: { kid: string } };
|
|
26
|
-
|
|
27
|
-
if (!decodedToken?.header?.kid) {
|
|
28
|
-
res.status(401).json({ error: 'Invalid token', message: 'Missing kid' });
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const publicKey = await miauService.getPublicKey(decodedToken.header.kid);
|
|
33
|
-
const appToken = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as MiauApplication;
|
|
34
|
-
|
|
35
|
-
req.miauApplication = { id: appToken?.id, name: appToken?.name };
|
|
36
|
-
|
|
37
|
-
const permission = await miauService.getPermissions(req.miauApplication.id);
|
|
38
|
-
req.miauMetadata = permission?.metadata || {};
|
|
39
|
-
|
|
40
|
-
const resources = permission?.resources || [];
|
|
41
|
-
const isAllowed = resources.some((resource: Resource) => {
|
|
42
|
-
return (
|
|
43
|
-
resource.method.toLowerCase() === req.method.toLowerCase() &&
|
|
44
|
-
wildcardToRegex(resource.path).test(req.path.toLowerCase())
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (!isAllowed) {
|
|
49
|
-
res
|
|
50
|
-
.status(403)
|
|
51
|
-
.json({ error: 'Forbidden', message: `You do not have permission to access ${req.method} ${req.path}` });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
next();
|
|
56
|
-
} catch (err: any) {
|
|
57
|
-
res.status(401).json({ error: err.name, message: err.message });
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
};
|