@alexdevco/passport 1.0.10 → 1.0.12
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/dist/interfaces/ jwt.interface.d.ts +6 -0
- package/dist/interfaces/passport-options.interface.d.ts +1 -1
- package/dist/interfaces/token.interface.d.ts +23 -3
- package/dist/strategies/hs256.strategy.d.ts +1 -0
- package/dist/strategies/hs256.strategy.js +22 -1
- package/dist/strategies/rs256.strategy.d.ts +1 -0
- package/dist/strategies/rs256.strategy.js +21 -1
- package/dist/utils/checks.d.ts +12 -1
- package/dist/utils/checks.js +20 -1
- package/package.json +1 -1
|
@@ -16,4 +16,10 @@ export interface IJWTStrategy {
|
|
|
16
16
|
* @returns Результат проверки токена.
|
|
17
17
|
*/
|
|
18
18
|
verify(token: string): IVerifyResult;
|
|
19
|
+
/**
|
|
20
|
+
* Проверяет действительность refresh токена.
|
|
21
|
+
* @param token - Refresh токен для проверки.
|
|
22
|
+
* @returns Результат проверки токена.
|
|
23
|
+
*/
|
|
24
|
+
verifyRefresh(token: string): IVerifyResult;
|
|
19
25
|
}
|
|
@@ -6,7 +6,7 @@ export interface IPassportOptions {
|
|
|
6
6
|
/**
|
|
7
7
|
* Секретный ключ, используемый для подписи JWT токенов.
|
|
8
8
|
*/
|
|
9
|
-
secretKey
|
|
9
|
+
secretKey?: string;
|
|
10
10
|
/**
|
|
11
11
|
* Публичный ключ, используемый для верификации JWT токенов.
|
|
12
12
|
* Обязателен для асимметричных алгоритмов (например, RS256).
|
|
@@ -8,9 +8,29 @@ export interface IPayload {
|
|
|
8
8
|
*/
|
|
9
9
|
sub: string | number;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Роли пользователя в системе.
|
|
12
|
+
* Определяет уровень доступа и разрешения.
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
+
roles: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Разрешения пользователя в системе.
|
|
17
|
+
* Определяют конкретные действия, которые пользователь может выполнять.
|
|
18
|
+
*/
|
|
19
|
+
permissions: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Время выпуска токена (Issued At) в секундах since Unix epoch.
|
|
22
|
+
*/
|
|
23
|
+
iat: number;
|
|
24
|
+
/**
|
|
25
|
+
* Время истечения срока действия токена (Expiration Time) в секундах since Unix epoch.
|
|
26
|
+
*/
|
|
27
|
+
exp: number;
|
|
28
|
+
}
|
|
29
|
+
export interface IRefreshPayload {
|
|
30
|
+
/**
|
|
31
|
+
* Идентификатор (Subject ID) - уникальный идентификатор пользователя.
|
|
32
|
+
*/
|
|
33
|
+
sub: string | number;
|
|
14
34
|
/**
|
|
15
35
|
* Время выпуска токена (Issued At) в секундах since Unix epoch.
|
|
16
36
|
*/
|
|
@@ -51,5 +71,5 @@ export interface IVerifyResult {
|
|
|
51
71
|
/**
|
|
52
72
|
* Полезная нагрузка токена, если он действителен.
|
|
53
73
|
*/
|
|
54
|
-
payload?: IPayload;
|
|
74
|
+
payload?: IPayload | IRefreshPayload;
|
|
55
75
|
}
|
|
@@ -20,6 +20,7 @@ export declare class HS256Strategy implements IJWTStrategy {
|
|
|
20
20
|
* @returns Результат проверки токена.
|
|
21
21
|
*/
|
|
22
22
|
verify(token: string): IVerifyResult;
|
|
23
|
+
verifyRefresh(token: string): IVerifyResult;
|
|
23
24
|
/**
|
|
24
25
|
* Вычисляет HMAC-подпись для переданных данных, используя секретный ключ.
|
|
25
26
|
* Алгоритм шифрования: SHA-256.
|
|
@@ -48,7 +48,28 @@ class HS256Strategy {
|
|
|
48
48
|
const payload = parsed;
|
|
49
49
|
if (!Number.isFinite(payload.exp))
|
|
50
50
|
return { valid: false, reason: 'Expired error format' };
|
|
51
|
-
if (
|
|
51
|
+
if ((0, utils_1.now)() > payload.exp)
|
|
52
|
+
return { valid: false, reason: 'Token is expired' };
|
|
53
|
+
return { valid: true, payload };
|
|
54
|
+
}
|
|
55
|
+
verifyRefresh(token) {
|
|
56
|
+
const parts = token.split(HS256Strategy.INTERNAL_SEPARATOR);
|
|
57
|
+
if (parts.length !== HS256Strategy.PARTS_LENGTH) {
|
|
58
|
+
return { valid: false, reason: 'Invalid format' };
|
|
59
|
+
}
|
|
60
|
+
const [headerB64, payloadB64, mac] = parts;
|
|
61
|
+
const serialized = (0, utils_1.serialize)([headerB64, payloadB64], HS256Strategy.INTERNAL_SEPARATOR);
|
|
62
|
+
const expectedMac = this.computeHmac(serialized);
|
|
63
|
+
if (!(0, utils_1.constantTimeEqual)(expectedMac, mac)) {
|
|
64
|
+
return { valid: false, reason: 'Invalid signature' };
|
|
65
|
+
}
|
|
66
|
+
const parsed = JSON.parse((0, utils_1.base64UrlDecode)(payloadB64));
|
|
67
|
+
if (!(0, utils_1.isRefreshPayload)(parsed))
|
|
68
|
+
return { valid: false, reason: 'Invalid payload stucture' };
|
|
69
|
+
const payload = parsed;
|
|
70
|
+
if (!Number.isFinite(payload.exp))
|
|
71
|
+
return { valid: false, reason: 'Expired error format' };
|
|
72
|
+
if ((0, utils_1.now)() > payload.exp)
|
|
52
73
|
return { valid: false, reason: 'Token is expired' };
|
|
53
74
|
return { valid: true, payload };
|
|
54
75
|
}
|
|
@@ -27,6 +27,7 @@ export declare class RS256Strategy implements IJWTStrategy {
|
|
|
27
27
|
* @returns результат проверки токена со статусом и возможной полезной нагрузкой
|
|
28
28
|
*/
|
|
29
29
|
verify(token: string): IVerifyResult;
|
|
30
|
+
verifyRefresh(token: string): IVerifyResult;
|
|
30
31
|
/**
|
|
31
32
|
* Создание подписи для токена
|
|
32
33
|
*
|
|
@@ -55,7 +55,27 @@ class RS256Strategy {
|
|
|
55
55
|
const payload = parsed;
|
|
56
56
|
if (!Number.isFinite(payload.exp))
|
|
57
57
|
return { valid: false, reason: 'Expired error format' };
|
|
58
|
-
if (
|
|
58
|
+
if ((0, utils_1.now)() > payload.exp)
|
|
59
|
+
return { valid: false, reason: 'Token is expired' };
|
|
60
|
+
return { valid: true, payload };
|
|
61
|
+
}
|
|
62
|
+
verifyRefresh(token) {
|
|
63
|
+
const parts = token.split(RS256Strategy.INTERNAL_SEPARATOR);
|
|
64
|
+
if (parts.length !== RS256Strategy.PARTS_LENGTH)
|
|
65
|
+
return { valid: false, reason: 'Invalid format' };
|
|
66
|
+
const [header64, payload64, mac] = parts;
|
|
67
|
+
const serialized = (0, utils_1.serialize)([header64, payload64], RS256Strategy.INTERNAL_SEPARATOR);
|
|
68
|
+
const signatureBuffer = Buffer.from(mac, 'base64url');
|
|
69
|
+
const verify = (0, crypto_1.createVerify)(RS256Strategy.JWT_ALG).update(serialized);
|
|
70
|
+
if (!verify.verify(this.PUBLIC_KEY, signatureBuffer))
|
|
71
|
+
return { valid: false, reason: 'Invalid signature' };
|
|
72
|
+
const parsed = JSON.parse((0, utils_1.base64UrlDecode)(payload64));
|
|
73
|
+
if (!(0, utils_1.isRefreshPayload)(parsed))
|
|
74
|
+
return { valid: false, reason: 'Invalid payload stucture' };
|
|
75
|
+
const payload = parsed;
|
|
76
|
+
if (!Number.isFinite(payload.exp))
|
|
77
|
+
return { valid: false, reason: 'Expired error format' };
|
|
78
|
+
if ((0, utils_1.now)() > payload.exp)
|
|
59
79
|
return { valid: false, reason: 'Token is expired' };
|
|
60
80
|
return { valid: true, payload };
|
|
61
81
|
}
|
package/dist/utils/checks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IHeader, IPayload } from '../interfaces';
|
|
1
|
+
import { IHeader, IPayload, IRefreshPayload } from '../interfaces';
|
|
2
2
|
/**
|
|
3
3
|
* Проверяет, является ли переданный объект полезной нагрузкой токена.
|
|
4
4
|
* Объект считается полезной нагрузкой, если:
|
|
@@ -11,6 +11,17 @@ import { IHeader, IPayload } from '../interfaces';
|
|
|
11
11
|
* @returns true, если объект соответствует интерфейсу IPayload, иначе false
|
|
12
12
|
*/
|
|
13
13
|
export declare function isPayload(data: any): data is IPayload;
|
|
14
|
+
/**
|
|
15
|
+
* Проверяет, является ли переданный объект полезной нагрузкой refresh-токена.
|
|
16
|
+
* Объект считается полезной нагрузкой refresh-токена, если:
|
|
17
|
+
* - свойство sub является строкой или числом
|
|
18
|
+
* - свойство iat является числом
|
|
19
|
+
* - свойство exp является числом
|
|
20
|
+
*
|
|
21
|
+
* @param data - проверяемый объект
|
|
22
|
+
* @returns true, если объект соответствует интерфейсу IRefreshPayload, иначе false
|
|
23
|
+
*/
|
|
24
|
+
export declare function isRefreshPayload(data: any): data is IRefreshPayload;
|
|
14
25
|
/**
|
|
15
26
|
* Проверяет, является ли переданный объект заголовком токена.
|
|
16
27
|
* Объект считается заголовком, если:
|
package/dist/utils/checks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isPayload = isPayload;
|
|
4
|
+
exports.isRefreshPayload = isRefreshPayload;
|
|
4
5
|
exports.isHeader = isHeader;
|
|
5
6
|
/**
|
|
6
7
|
* Проверяет, является ли переданный объект полезной нагрузкой токена.
|
|
@@ -15,7 +16,25 @@ exports.isHeader = isHeader;
|
|
|
15
16
|
*/
|
|
16
17
|
function isPayload(data) {
|
|
17
18
|
return ((typeof data.sub === 'string' || typeof data.sub === 'number') &&
|
|
18
|
-
|
|
19
|
+
Array.isArray(data.roles) &&
|
|
20
|
+
data.roles.every((role) => typeof role === 'string') &&
|
|
21
|
+
Array.isArray(data.permissions) &&
|
|
22
|
+
data.permissions.every((perm) => typeof perm === 'string') &&
|
|
23
|
+
typeof data.iat === 'number' &&
|
|
24
|
+
typeof data.exp === 'number');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Проверяет, является ли переданный объект полезной нагрузкой refresh-токена.
|
|
28
|
+
* Объект считается полезной нагрузкой refresh-токена, если:
|
|
29
|
+
* - свойство sub является строкой или числом
|
|
30
|
+
* - свойство iat является числом
|
|
31
|
+
* - свойство exp является числом
|
|
32
|
+
*
|
|
33
|
+
* @param data - проверяемый объект
|
|
34
|
+
* @returns true, если объект соответствует интерфейсу IRefreshPayload, иначе false
|
|
35
|
+
*/
|
|
36
|
+
function isRefreshPayload(data) {
|
|
37
|
+
return ((typeof data.sub === 'string' || typeof data.sub === 'number') &&
|
|
19
38
|
typeof data.iat === 'number' &&
|
|
20
39
|
typeof data.exp === 'number');
|
|
21
40
|
}
|