@ahhaohho/auth-middleware 1.0.4 → 1.0.6
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": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Shared authentication middleware with Passport.js for ahhaohho microservices",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
"author": "ahhaohho",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"passport-jwt": "^4.0.1",
|
|
33
|
-
"jsonwebtoken": "^9.0.2",
|
|
31
|
+
"@aws-sdk/client-secrets-manager": "^3.552.0",
|
|
34
32
|
"ioredis": "^5.4.1",
|
|
35
|
-
"
|
|
33
|
+
"jsonwebtoken": "^9.0.2",
|
|
34
|
+
"passport": "^0.7.0",
|
|
35
|
+
"passport-custom": "^1.1.1",
|
|
36
|
+
"passport-jwt": "^4.0.1"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
39
|
"express": "^4.x"
|
|
@@ -1,15 +1,44 @@
|
|
|
1
|
-
const { Strategy: JwtStrategy
|
|
1
|
+
const { Strategy: JwtStrategy } = require('passport-jwt');
|
|
2
2
|
const { getJwtKeys } = require('../utils/secretManager');
|
|
3
3
|
const { isBlacklisted } = require('../utils/blacklist');
|
|
4
4
|
const jwt = require('jsonwebtoken');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Bearer 접두사가 있든 없든 토큰을 추출하는 커스텀 함수
|
|
8
|
+
*/
|
|
9
|
+
const extractJwtFromHeader = (req) => {
|
|
10
|
+
const authHeader = req.headers.authorization || req.headers.Authorization;
|
|
11
|
+
|
|
12
|
+
if (!authHeader) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Bearer 접두사가 있는 경우
|
|
17
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
18
|
+
return authHeader.substring(7);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// bearer (소문자)인 경우
|
|
22
|
+
if (authHeader.startsWith('bearer ')) {
|
|
23
|
+
return authHeader.substring(7);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Bearer 접두사 없이 토큰만 있는 경우
|
|
27
|
+
// JWT 형식인지 확인 (xxx.yyy.zzz 형태)
|
|
28
|
+
if (authHeader.split('.').length === 3) {
|
|
29
|
+
return authHeader;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
6
35
|
/**
|
|
7
36
|
* Passport JWT 전략 생성
|
|
8
37
|
* Access Token 검증용
|
|
9
38
|
*/
|
|
10
39
|
function createJwtStrategy() {
|
|
11
40
|
const options = {
|
|
12
|
-
jwtFromRequest:
|
|
41
|
+
jwtFromRequest: extractJwtFromHeader,
|
|
13
42
|
// 다중 키 지원을 위한 secretOrKeyProvider 사용
|
|
14
43
|
secretOrKeyProvider: async (request, rawJwtToken, done) => {
|
|
15
44
|
try {
|
|
@@ -68,7 +97,7 @@ function createJwtStrategy() {
|
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
// 블랙리스트 확인
|
|
71
|
-
const token =
|
|
100
|
+
const token = extractJwtFromHeader(request);
|
|
72
101
|
const blacklisted = await isBlacklisted(decoded.userId, 'access', token);
|
|
73
102
|
if (blacklisted) {
|
|
74
103
|
return done(new Error('Token has been revoked'), false);
|
|
@@ -1,70 +1,73 @@
|
|
|
1
|
-
const { Strategy:
|
|
1
|
+
const { Strategy: CustomStrategy } = require('passport-custom');
|
|
2
2
|
const { verifyTokenWithFallback } = require('../utils/jwtValidator');
|
|
3
3
|
const { isBlacklisted } = require('../utils/blacklist');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Refresh Token 헤더에서 토큰 추출
|
|
7
|
+
* Bearer 접두사가 있든 없든 처리
|
|
7
8
|
*/
|
|
8
9
|
function extractRefreshToken(req) {
|
|
9
10
|
let token = null;
|
|
10
11
|
|
|
11
|
-
if (req && req.headers
|
|
12
|
-
let refreshToken = req.headers['refresh-token'];
|
|
12
|
+
if (req && req.headers) {
|
|
13
|
+
let refreshToken = req.headers['refresh-token'] || req.headers['refreshtoken'];
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
if (refreshToken) {
|
|
16
|
+
// Bearer 접두사 제거 (대소문자 구분 없이)
|
|
17
|
+
if (refreshToken.startsWith('Bearer ')) {
|
|
18
|
+
refreshToken = refreshToken.substring(7);
|
|
19
|
+
} else if (refreshToken.startsWith('bearer ')) {
|
|
20
|
+
refreshToken = refreshToken.substring(7);
|
|
21
|
+
}
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
token = refreshToken.trim();
|
|
24
|
+
}
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
return token;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
|
-
* Passport Refresh Token 전략 생성
|
|
31
|
+
* Passport Refresh Token 전략 생성 (Custom Strategy 사용)
|
|
27
32
|
*/
|
|
28
33
|
function createRefreshStrategy() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!decoded || !decoded.userId) {
|
|
37
|
-
return done(new Error('Invalid refresh token payload'), false);
|
|
38
|
-
}
|
|
34
|
+
return new CustomStrategy(async (req, done) => {
|
|
35
|
+
try {
|
|
36
|
+
// 1. 토큰 추출
|
|
37
|
+
const token = extractRefreshToken(req);
|
|
38
|
+
if (!token) {
|
|
39
|
+
return done(null, false, { message: 'No refresh token provided' });
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (blacklisted) {
|
|
43
|
-
return done(new Error('Refresh token has been revoked'), false);
|
|
44
|
-
}
|
|
42
|
+
// 2. 다중 키로 토큰 검증
|
|
43
|
+
const { decoded, keyUsed } = await verifyTokenWithFallback(token);
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
45
|
+
if (!decoded || !decoded.userId) {
|
|
46
|
+
return done(null, false, { message: 'Invalid refresh token payload' });
|
|
47
|
+
}
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
done(
|
|
49
|
+
// 3. 블랙리스트 확인 (refresh 타입)
|
|
50
|
+
const blacklisted = await isBlacklisted(decoded.userId, 'refresh', token);
|
|
51
|
+
if (blacklisted) {
|
|
52
|
+
return done(null, false, { message: 'Refresh token has been revoked' });
|
|
55
53
|
}
|
|
56
|
-
},
|
|
57
|
-
passReqToCallback: false
|
|
58
|
-
};
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
phoneNumber: jwtPayload.phoneNumber
|
|
65
|
-
};
|
|
55
|
+
// 4. 검증 성공 - user 객체 반환
|
|
56
|
+
console.log(
|
|
57
|
+
`[@ahhaohho/auth-middleware] ✅ Refresh token verified with ${keyUsed} key for user ${decoded.userId}`
|
|
58
|
+
);
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
const user = {
|
|
61
|
+
userId: decoded.userId,
|
|
62
|
+
userRole: decoded.userRole,
|
|
63
|
+
phoneNumber: decoded.phoneNumber
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return done(null, user);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('[@ahhaohho/auth-middleware] ❌ Refresh token verification failed:', error.message);
|
|
69
|
+
return done(error, false);
|
|
70
|
+
}
|
|
68
71
|
});
|
|
69
72
|
}
|
|
70
73
|
|