@shadimakhoul/ggcoach 1.0.1 → 1.0.2
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/config/initExpress.ts +1 -1
- package/middleware/auth.ts +49 -55
- package/middleware/errorHandler.ts +2 -2
- package/middleware/internalAuth.ts +19 -17
- package/package.json +1 -1
package/config/initExpress.ts
CHANGED
|
@@ -17,8 +17,8 @@ export function initExpress(isDevelopment: boolean) {
|
|
|
17
17
|
app.use(express.json({ limit: "10mb" }));
|
|
18
18
|
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
|
19
19
|
|
|
20
|
+
app.use(morgan("dev", { stream } as any));
|
|
20
21
|
if (isDevelopment) {
|
|
21
|
-
app.use(morgan("dev", { stream } as any));
|
|
22
22
|
} else {
|
|
23
23
|
app.use(morgan("combined", { stream } as any));
|
|
24
24
|
}
|
package/middleware/auth.ts
CHANGED
|
@@ -8,46 +8,43 @@ import { JWTPayload } from "../types";
|
|
|
8
8
|
|
|
9
9
|
const TOKEN_VERSION_PREFIX = "auth:token-version:";
|
|
10
10
|
|
|
11
|
-
export const authenticateToken =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
next: NextFunction,
|
|
16
|
-
): Promise<void> => {
|
|
17
|
-
const authHeader = req.headers["authorization"];
|
|
18
|
-
const token = authHeader && authHeader.split(" ")[1]; // Bearer TOKEN
|
|
11
|
+
export const authenticateToken = (JWT_SECRET: string) => {
|
|
12
|
+
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
13
|
+
const authHeader = req.headers["authorization"];
|
|
14
|
+
const token = authHeader && authHeader.split(" ")[1]; // Bearer TOKEN
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
if (!token) {
|
|
17
|
+
res.status(401).json({
|
|
18
|
+
success: false,
|
|
19
|
+
message: "Access token required",
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
try {
|
|
25
|
+
const decoded = jwt.verify(token, JWT_SECRET, {
|
|
26
|
+
issuer: "ggcoach-auth-service",
|
|
27
|
+
audience: "ggcoach-app",
|
|
28
|
+
}) as JWTPayload;
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const isValid = await validateTokenWithRedis(decoded);
|
|
31
|
+
if (!isValid) {
|
|
32
|
+
res.status(403).json({
|
|
33
|
+
success: false,
|
|
34
|
+
message: "Invalid or expired token",
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
req.user = decoded;
|
|
40
|
+
next();
|
|
41
|
+
} catch (error) {
|
|
36
42
|
res.status(403).json({
|
|
37
43
|
success: false,
|
|
38
44
|
message: "Invalid or expired token",
|
|
39
45
|
});
|
|
40
|
-
return;
|
|
41
46
|
}
|
|
42
|
-
|
|
43
|
-
req.user = decoded;
|
|
44
|
-
next();
|
|
45
|
-
} catch (error) {
|
|
46
|
-
res.status(403).json({
|
|
47
|
-
success: false,
|
|
48
|
-
message: "Invalid or expired token",
|
|
49
|
-
});
|
|
50
|
-
}
|
|
47
|
+
};
|
|
51
48
|
};
|
|
52
49
|
|
|
53
50
|
const validateTokenWithRedis = async (decoded: JWTPayload): Promise<boolean> => {
|
|
@@ -70,31 +67,28 @@ const validateTokenWithRedis = async (decoded: JWTPayload): Promise<boolean> =>
|
|
|
70
67
|
}
|
|
71
68
|
};
|
|
72
69
|
|
|
73
|
-
export const optionalAuth =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
next: NextFunction,
|
|
78
|
-
): Promise<void> => {
|
|
79
|
-
const authHeader = req.headers["authorization"];
|
|
80
|
-
const token = authHeader && authHeader.split(" ")[1];
|
|
70
|
+
export const optionalAuth = (JWT_SECRET: string) => {
|
|
71
|
+
return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {
|
|
72
|
+
const authHeader = req.headers["authorization"];
|
|
73
|
+
const token = authHeader && authHeader.split(" ")[1];
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
75
|
+
if (token) {
|
|
76
|
+
try {
|
|
77
|
+
const decoded = jwt.verify(token, JWT_SECRET, {
|
|
78
|
+
issuer: "ggcoach-auth-service",
|
|
79
|
+
audience: "ggcoach-app",
|
|
80
|
+
}) as JWTPayload;
|
|
81
|
+
const isValid = await validateTokenWithRedis(decoded);
|
|
82
|
+
if (isValid) {
|
|
83
|
+
req.user = decoded;
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// Token is invalid, but we continue without user info
|
|
87
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
88
|
+
logger.warn(`Invalid token in optional auth: ${err.message}`);
|
|
91
89
|
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
// Token is invalid, but we continue without user info
|
|
94
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
95
|
-
logger.warn(`Invalid token in optional auth: ${err.message}`);
|
|
96
90
|
}
|
|
97
|
-
}
|
|
98
91
|
|
|
99
|
-
|
|
92
|
+
next();
|
|
93
|
+
};
|
|
100
94
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
import { logger } from '../utils';
|
|
3
3
|
|
|
4
|
-
export const errorHandler = (
|
|
4
|
+
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction): void => {
|
|
5
5
|
logger.error(`Error: ${err}`);
|
|
6
6
|
|
|
7
7
|
// Handle specific error types
|
|
@@ -54,7 +54,7 @@ export const errorHandler = (isDevelopment: boolean, err: any, req: Request, res
|
|
|
54
54
|
success: false,
|
|
55
55
|
message: err.message || 'Internal server error',
|
|
56
56
|
code: err.code || 'INTERNAL_ERROR',
|
|
57
|
-
...(
|
|
57
|
+
...(process.env.NODE_ENV === 'development' && {
|
|
58
58
|
stack: err.stack,
|
|
59
59
|
requestId: req.headers['x-request-id']
|
|
60
60
|
})
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
|
|
3
|
-
export const requireInternalToken = (ALLOW_DIRECT_ACCESS: boolean, INTERNAL_SERVICE_TOKEN: string
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export const requireInternalToken = (ALLOW_DIRECT_ACCESS: boolean, INTERNAL_SERVICE_TOKEN: string) => {
|
|
4
|
+
return (req: Request, res: Response, next: NextFunction): void => {
|
|
5
|
+
if (ALLOW_DIRECT_ACCESS) {
|
|
6
|
+
next();
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const token = INTERNAL_SERVICE_TOKEN;
|
|
11
|
+
if (!token) {
|
|
12
|
+
// If token isn't set, reject to be safe
|
|
13
|
+
res.status(403).json({ success: false, message: 'Internal access token not configured' });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const incoming = (req.headers['x-internal-token'] as string) || '';
|
|
18
|
+
if (incoming !== token) {
|
|
19
|
+
res.status(403).json({ success: false, message: 'Forbidden' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
next();
|
|
24
|
+
};
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export default requireInternalToken;
|