@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahhaohho/auth-middleware",
3
- "version": "2.1.0",
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
- this.client = new SecretsManagerClient({
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