@ahhaohho/auth-middleware 2.1.0 → 2.2.0
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 +3 -2
- package/src/index.js +6 -0
- package/src/utils/extractUserId.js +91 -0
- package/src/utils/secretManager.js +17 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahhaohho/auth-middleware",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Shared authentication and authorization middleware for ahhaohho microservices",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"passport-jwt": "^4.0.1"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"express": "^4.x"
|
|
42
|
+
"express": "^4.x",
|
|
43
|
+
"mongoose": "^8.x"
|
|
43
44
|
},
|
|
44
45
|
"engines": {
|
|
45
46
|
"node": ">=16.0.0"
|
package/src/index.js
CHANGED
|
@@ -24,6 +24,9 @@ const {
|
|
|
24
24
|
fetchRolePermissions
|
|
25
25
|
} = require('./middleware/authorization');
|
|
26
26
|
|
|
27
|
+
// userId 추출 유틸리티
|
|
28
|
+
const { extractUserId } = require('./utils/extractUserId');
|
|
29
|
+
|
|
27
30
|
// 유틸리티
|
|
28
31
|
const { verifyTokenWithFallback, signToken, getCurrentSigningKey } = require('./utils/jwtValidator');
|
|
29
32
|
const { isBlacklisted, addToBlacklist, clearBlacklist } = require('./utils/blacklist');
|
|
@@ -61,6 +64,9 @@ module.exports = {
|
|
|
61
64
|
requireOwnership, // 리소스 소유자 검증: requireOwnership('userId')
|
|
62
65
|
requireAny, // 복합 조건 인가: requireAny({ roles: [...], permissions: [...] })
|
|
63
66
|
|
|
67
|
+
// ===== userId 추출 =====
|
|
68
|
+
extractUserId, // userId 추출: await extractUserId(req, isRequired)
|
|
69
|
+
|
|
64
70
|
// ===== 유틸리티 함수 =====
|
|
65
71
|
utils: {
|
|
66
72
|
// JWT 관련
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request 객체에서 userId를 추출하는 유틸리티 함수
|
|
5
|
+
*
|
|
6
|
+
* 우선순위:
|
|
7
|
+
* 1. req.user.userId (JWT 인증 미들웨어에서 설정)
|
|
8
|
+
* 2. req.userId (이미 설정된 경우)
|
|
9
|
+
* 3. req.headers['user-id'] (NODE_ENV !== 'production'일 때만)
|
|
10
|
+
* 4. req.query.userId (NODE_ENV !== 'production'일 때만)
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} req - Express request 객체 또는 req-like 객체
|
|
13
|
+
* @param {boolean} [isRequired=false] - true면 userId 없을 시 에러 throw
|
|
14
|
+
* @returns {Promise<mongoose.Types.ObjectId|null>} userId (ObjectId) 또는 null
|
|
15
|
+
* @throws {Error} isRequired=true이고 userId가 없거나 유효하지 않은 경우
|
|
16
|
+
*/
|
|
17
|
+
async function extractUserId(req, isRequired = false) {
|
|
18
|
+
const isDebug = process.env.NODE_ENV !== 'production';
|
|
19
|
+
|
|
20
|
+
let userId = null;
|
|
21
|
+
|
|
22
|
+
// 1. JWT 인증 미들웨어에서 설정한 req.user.userId (최우선)
|
|
23
|
+
if (req.user && req.user.userId) {
|
|
24
|
+
userId = req.user.userId;
|
|
25
|
+
}
|
|
26
|
+
// 2. 이미 설정된 req.userId
|
|
27
|
+
else if (req.userId) {
|
|
28
|
+
userId = req.userId;
|
|
29
|
+
}
|
|
30
|
+
// 3. 디버그 모드에서만: 헤더
|
|
31
|
+
else if (isDebug && req.headers && req.headers['user-id']) {
|
|
32
|
+
userId = req.headers['user-id'];
|
|
33
|
+
}
|
|
34
|
+
// 4. 디버그 모드에서만: 쿼리 파라미터
|
|
35
|
+
else if (isDebug && req.query && req.query.userId) {
|
|
36
|
+
userId = req.query.userId;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// userId가 필수가 아니고 없는 경우
|
|
40
|
+
if (!isRequired && !userId) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// userId가 필수이고 없는 경우
|
|
45
|
+
if (isRequired && (!userId || (typeof userId === 'string' && userId.trim() === ''))) {
|
|
46
|
+
const error = new Error('유효하지 않은 요청: userId가 없거나 유효하지 않습니다.');
|
|
47
|
+
error.name = 'ValidationError';
|
|
48
|
+
error.statusCode = 400;
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// userId가 있는 경우 형식 검증 및 ObjectId 변환
|
|
53
|
+
if (userId) {
|
|
54
|
+
// 이미 ObjectId인 경우 그대로 반환
|
|
55
|
+
if (typeof userId !== 'string') {
|
|
56
|
+
if (mongoose.Types.ObjectId.isValid(userId)) {
|
|
57
|
+
return userId;
|
|
58
|
+
}
|
|
59
|
+
const error = new Error('유효하지 않은 userId 형식입니다.');
|
|
60
|
+
error.name = 'ValidationError';
|
|
61
|
+
error.statusCode = 400;
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (userId.trim() === '') {
|
|
66
|
+
if (isRequired) {
|
|
67
|
+
const error = new Error('유효하지 않은 userId 형식입니다.');
|
|
68
|
+
error.name = 'ValidationError';
|
|
69
|
+
error.statusCode = 400;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (mongoose.Types.ObjectId.isValid(userId)) {
|
|
76
|
+
return new mongoose.Types.ObjectId(userId);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isRequired) {
|
|
80
|
+
const error = new Error('유효하지 않은 userId 형식입니다.');
|
|
81
|
+
error.name = 'ValidationError';
|
|
82
|
+
error.statusCode = 400;
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { extractUserId };
|
|
@@ -3,12 +3,27 @@ const redisManager = require('../config/redis');
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* AWS Secrets Manager에서 JWT 키 가져오기
|
|
6
|
+
*
|
|
7
|
+
* JWT_AWS_ACCESS_KEY_ID, JWT_AWS_SECRET_ACCESS_KEY 환경변수가 설정되어 있으면
|
|
8
|
+
* 해당 자격 증명을 사용하고, 없으면 기본 AWS 자격 증명을 사용합니다.
|
|
6
9
|
*/
|
|
7
10
|
class SecretManager {
|
|
8
11
|
constructor() {
|
|
9
|
-
|
|
12
|
+
// JWT 전용 AWS 자격 증명 설정 (선택적)
|
|
13
|
+
const clientConfig = {
|
|
10
14
|
region: process.env.AWS_REGION || 'ap-northeast-2'
|
|
11
|
-
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// JWT 전용 자격 증명이 있으면 사용
|
|
18
|
+
if (process.env.JWT_AWS_ACCESS_KEY_ID && process.env.JWT_AWS_SECRET_ACCESS_KEY) {
|
|
19
|
+
clientConfig.credentials = {
|
|
20
|
+
accessKeyId: process.env.JWT_AWS_ACCESS_KEY_ID,
|
|
21
|
+
secretAccessKey: process.env.JWT_AWS_SECRET_ACCESS_KEY
|
|
22
|
+
};
|
|
23
|
+
console.log('[@ahhaohho/auth-middleware] Using JWT-specific AWS credentials');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.client = new SecretsManagerClient(clientConfig);
|
|
12
27
|
this.secretName = process.env.JWT_SECRET_NAME;
|
|
13
28
|
}
|
|
14
29
|
|