@chengyixu/jwt-decode-cli 1.0.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/README.md +132 -0
- package/index.js +258 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# jwt-decode-cli
|
|
2
|
+
|
|
3
|
+
> Decode and inspect JWT tokens from the terminal. No external dependencies.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/jwt-decode-cli)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
A fast, zero-dependency CLI tool to decode JSON Web Tokens (JWTs). Inspect headers, payloads, expiry status, and claim details — all from your terminal.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g jwt-decode-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or run without installing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx jwt-decode-cli <token>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Pass token directly
|
|
30
|
+
jwt-decode eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
31
|
+
|
|
32
|
+
# Pipe from stdin
|
|
33
|
+
echo "eyJhbGci..." | jwt-decode
|
|
34
|
+
cat token.txt | jwt-decode
|
|
35
|
+
|
|
36
|
+
# Show help
|
|
37
|
+
jwt-decode --help
|
|
38
|
+
|
|
39
|
+
# Note signature verification requirement
|
|
40
|
+
jwt-decode <token> --verify
|
|
41
|
+
|
|
42
|
+
# Raw JSON output (no formatting)
|
|
43
|
+
jwt-decode <token> --raw
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Output
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
━━ Header ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
52
|
+
{
|
|
53
|
+
"alg": "HS256",
|
|
54
|
+
"typ": "JWT"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
━━ Payload ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
58
|
+
{
|
|
59
|
+
"sub": "1234567890",
|
|
60
|
+
"name": "John Doe",
|
|
61
|
+
"iat": 1516239022
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
━━ Signature ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
65
|
+
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
|
66
|
+
|
|
67
|
+
━━ Time Analysis ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
68
|
+
iat (issued at): 2018-01-18 01:30:22 UTC (7y 2m ago)
|
|
69
|
+
exp: ⚠ No expiry set — token never expires
|
|
70
|
+
|
|
71
|
+
━━ Claims Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
72
|
+
sub (subject): 1234567890
|
|
73
|
+
custom claims: name
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- **Zero dependencies** — uses only Node.js built-ins
|
|
81
|
+
- **Colored output** — syntax-highlighted JSON, color-coded expiry status
|
|
82
|
+
- **Expiry detection** — shows time until expiry or how long since expired
|
|
83
|
+
- **Stdin support** — pipe tokens from other commands
|
|
84
|
+
- **Claims summary** — highlights standard JWT claims (iss, sub, aud, jti, etc.)
|
|
85
|
+
- **Raw JSON mode** — `--raw` for scripting/piping to `jq`
|
|
86
|
+
- **Expired token warning** — bold red banner when token is expired
|
|
87
|
+
- **`--verify` flag** — placeholder with guidance for signature verification
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Flags
|
|
92
|
+
|
|
93
|
+
| Flag | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| `--verify` | Notes that verification requires a secret/public key, provides guidance |
|
|
96
|
+
| `--raw` | Output raw JSON (no colors or formatting) |
|
|
97
|
+
| `--no-color` | Disable colored output (or set `NO_COLOR` env var) |
|
|
98
|
+
| `--help`, `-h` | Show help |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Examples
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Decode a token from an environment variable
|
|
106
|
+
jwt-decode $JWT_TOKEN
|
|
107
|
+
|
|
108
|
+
# Check token expiry in a script
|
|
109
|
+
jwt-decode $TOKEN --raw | jq '.payload.exp'
|
|
110
|
+
|
|
111
|
+
# Decode token stored in a file
|
|
112
|
+
cat ~/.config/token | jwt-decode
|
|
113
|
+
|
|
114
|
+
# Use in CI/CD to inspect tokens
|
|
115
|
+
echo $GITHUB_TOKEN | jwt-decode
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Security Note
|
|
121
|
+
|
|
122
|
+
This tool **decodes** JWTs but does **not verify** the signature. It is intended for development, debugging, and inspection purposes. Never trust a JWT's claims without verifying the signature using the appropriate secret or public key.
|
|
123
|
+
|
|
124
|
+
For signature verification, use:
|
|
125
|
+
- [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) npm package
|
|
126
|
+
- [jwt.io](https://jwt.io) (web-based debugger)
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT © chengyixu
|
package/index.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
// ANSI color codes
|
|
8
|
+
const colors = {
|
|
9
|
+
reset: '\x1b[0m',
|
|
10
|
+
bold: '\x1b[1m',
|
|
11
|
+
dim: '\x1b[2m',
|
|
12
|
+
red: '\x1b[31m',
|
|
13
|
+
green: '\x1b[32m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
blue: '\x1b[34m',
|
|
16
|
+
magenta: '\x1b[35m',
|
|
17
|
+
cyan: '\x1b[36m',
|
|
18
|
+
white: '\x1b[37m',
|
|
19
|
+
bgRed: '\x1b[41m',
|
|
20
|
+
bgGreen: '\x1b[42m',
|
|
21
|
+
bgYellow: '\x1b[43m',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function c(color, text) {
|
|
25
|
+
// Disable colors if not a TTY or NO_COLOR env set
|
|
26
|
+
if (!process.stdout.isTTY || process.env.NO_COLOR) return text;
|
|
27
|
+
return colors[color] + text + colors.reset;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function printHelp() {
|
|
31
|
+
console.log(`
|
|
32
|
+
${c('bold', 'jwt-decode-cli')} — Decode and inspect JWT tokens from the terminal
|
|
33
|
+
|
|
34
|
+
${c('cyan', 'USAGE:')}
|
|
35
|
+
jwt-decode <token> Decode a JWT token
|
|
36
|
+
echo <token> | jwt-decode Decode via stdin
|
|
37
|
+
jwt-decode --help Show this help
|
|
38
|
+
|
|
39
|
+
${c('cyan', 'FLAGS:')}
|
|
40
|
+
--verify Placeholder: signature verification (requires secret/key)
|
|
41
|
+
--raw Output raw JSON (no formatting)
|
|
42
|
+
--no-color Disable colored output
|
|
43
|
+
--help, -h Show help
|
|
44
|
+
|
|
45
|
+
${c('cyan', 'EXAMPLES:')}
|
|
46
|
+
jwt-decode eyJhbGci...
|
|
47
|
+
cat token.txt | jwt-decode
|
|
48
|
+
jwt-decode eyJhbGci... --verify
|
|
49
|
+
|
|
50
|
+
${c('cyan', 'OUTPUT:')}
|
|
51
|
+
- Header (algorithm, token type)
|
|
52
|
+
- Payload (claims, custom fields)
|
|
53
|
+
- Expiry status (valid, expired, or no expiry set)
|
|
54
|
+
- Time until expiration or time since expiry
|
|
55
|
+
- Issued-at and not-before times
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function base64urlDecode(str) {
|
|
60
|
+
// Convert base64url to base64
|
|
61
|
+
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
62
|
+
// Pad to multiple of 4
|
|
63
|
+
while (base64.length % 4 !== 0) {
|
|
64
|
+
base64 += '=';
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return Buffer.from(base64, 'base64').toString('utf8');
|
|
68
|
+
} catch (e) {
|
|
69
|
+
throw new Error('Failed to base64url-decode segment: ' + e.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function formatDuration(seconds) {
|
|
74
|
+
const abs = Math.abs(seconds);
|
|
75
|
+
if (abs < 60) return `${Math.floor(abs)}s`;
|
|
76
|
+
if (abs < 3600) return `${Math.floor(abs / 60)}m ${Math.floor(abs % 60)}s`;
|
|
77
|
+
if (abs < 86400) return `${Math.floor(abs / 3600)}h ${Math.floor((abs % 3600) / 60)}m`;
|
|
78
|
+
const days = Math.floor(abs / 86400);
|
|
79
|
+
const hours = Math.floor((abs % 86400) / 3600);
|
|
80
|
+
return `${days}d ${hours}h`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatTimestamp(ts) {
|
|
84
|
+
try {
|
|
85
|
+
return new Date(ts * 1000).toISOString().replace('T', ' ').replace('.000Z', ' UTC');
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return String(ts);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function prettyPrintJSON(obj, indent = 2) {
|
|
92
|
+
if (!process.stdout.isTTY || process.env.NO_COLOR) {
|
|
93
|
+
return JSON.stringify(obj, null, indent);
|
|
94
|
+
}
|
|
95
|
+
// Colorize JSON output
|
|
96
|
+
return JSON.stringify(obj, null, indent)
|
|
97
|
+
.replace(/"([^"]+)":/g, c('cyan', '"$1"') + ':')
|
|
98
|
+
.replace(/: "([^"]*)"/g, ': ' + c('green', '"$1"'))
|
|
99
|
+
.replace(/: (\d+\.?\d*)/g, ': ' + c('yellow', '$1'))
|
|
100
|
+
.replace(/: (true|false)/g, ': ' + c('magenta', '$1'))
|
|
101
|
+
.replace(/: (null)/g, ': ' + c('dim', '$1'));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function decodeJWT(token, opts = {}) {
|
|
105
|
+
const parts = token.split('.');
|
|
106
|
+
|
|
107
|
+
if (parts.length !== 3) {
|
|
108
|
+
console.error(c('red', `Error: Invalid JWT format. Expected 3 parts (header.payload.signature), got ${parts.length}.`));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
113
|
+
|
|
114
|
+
// Decode header
|
|
115
|
+
let header;
|
|
116
|
+
try {
|
|
117
|
+
header = JSON.parse(base64urlDecode(headerB64));
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error(c('red', 'Error: Could not decode JWT header: ' + e.message));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Decode payload
|
|
124
|
+
let payload;
|
|
125
|
+
try {
|
|
126
|
+
payload = JSON.parse(base64urlDecode(payloadB64));
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error(c('red', 'Error: Could not decode JWT payload: ' + e.message));
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (opts.raw) {
|
|
133
|
+
console.log(JSON.stringify({ header, payload, signature: signatureB64 }, null, 2));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const now = Math.floor(Date.now() / 1000);
|
|
138
|
+
|
|
139
|
+
// ── HEADER ──────────────────────────────────────────────────────────────
|
|
140
|
+
console.log('\n' + c('bold', c('blue', '━━ Header ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
141
|
+
console.log(prettyPrintJSON(header));
|
|
142
|
+
|
|
143
|
+
// ── PAYLOAD ─────────────────────────────────────────────────────────────
|
|
144
|
+
console.log('\n' + c('bold', c('blue', '━━ Payload ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
145
|
+
console.log(prettyPrintJSON(payload));
|
|
146
|
+
|
|
147
|
+
// ── SIGNATURE ───────────────────────────────────────────────────────────
|
|
148
|
+
console.log('\n' + c('bold', c('blue', '━━ Signature ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
149
|
+
console.log(c('dim', signatureB64));
|
|
150
|
+
|
|
151
|
+
// ── TIME ANALYSIS ────────────────────────────────────────────────────────
|
|
152
|
+
console.log('\n' + c('bold', c('blue', '━━ Time Analysis ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
153
|
+
|
|
154
|
+
// Issued at (iat)
|
|
155
|
+
if (payload.iat !== undefined) {
|
|
156
|
+
const issuedAgo = now - payload.iat;
|
|
157
|
+
console.log(c('dim', ' iat (issued at): ') + c('white', formatTimestamp(payload.iat)) +
|
|
158
|
+
c('dim', ` (${formatDuration(issuedAgo)} ago)`));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Not before (nbf)
|
|
162
|
+
if (payload.nbf !== undefined) {
|
|
163
|
+
const nbfDiff = now - payload.nbf;
|
|
164
|
+
const nbfStatus = nbfDiff >= 0
|
|
165
|
+
? c('green', '✓ active')
|
|
166
|
+
: c('yellow', `⚠ not yet valid (in ${formatDuration(-nbfDiff)})`);
|
|
167
|
+
console.log(c('dim', ' nbf (not before): ') + c('white', formatTimestamp(payload.nbf)) +
|
|
168
|
+
' ' + nbfStatus);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Expiry (exp)
|
|
172
|
+
if (payload.exp !== undefined) {
|
|
173
|
+
const diff = payload.exp - now;
|
|
174
|
+
const expLine = c('dim', ' exp (expires): ') + c('white', formatTimestamp(payload.exp));
|
|
175
|
+
|
|
176
|
+
if (diff > 0) {
|
|
177
|
+
console.log(expLine + ' ' + c('green', `✓ valid (expires in ${formatDuration(diff)})`));
|
|
178
|
+
} else {
|
|
179
|
+
console.log(expLine + ' ' + c('red', `✗ EXPIRED ${formatDuration(diff)} ago`));
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
console.log(c('dim', ' exp: ') + c('yellow', '⚠ No expiry set — token never expires'));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── CLAIMS SUMMARY ───────────────────────────────────────────────────────
|
|
186
|
+
const standardClaims = new Set(['iss', 'sub', 'aud', 'exp', 'nbf', 'iat', 'jti']);
|
|
187
|
+
const customClaims = Object.keys(payload).filter(k => !standardClaims.has(k));
|
|
188
|
+
|
|
189
|
+
console.log('\n' + c('bold', c('blue', '━━ Claims Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
190
|
+
|
|
191
|
+
if (payload.iss) console.log(c('dim', ' iss (issuer): ') + c('white', payload.iss));
|
|
192
|
+
if (payload.sub) console.log(c('dim', ' sub (subject): ') + c('white', payload.sub));
|
|
193
|
+
if (payload.aud) {
|
|
194
|
+
const aud = Array.isArray(payload.aud) ? payload.aud.join(', ') : payload.aud;
|
|
195
|
+
console.log(c('dim', ' aud (audience): ') + c('white', aud));
|
|
196
|
+
}
|
|
197
|
+
if (payload.jti) console.log(c('dim', ' jti (JWT ID): ') + c('white', payload.jti));
|
|
198
|
+
|
|
199
|
+
if (customClaims.length > 0) {
|
|
200
|
+
console.log(c('dim', ` custom claims: `) + c('cyan', customClaims.join(', ')));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── VERIFICATION NOTICE ──────────────────────────────────────────────────
|
|
204
|
+
if (opts.verify) {
|
|
205
|
+
console.log('\n' + c('bold', c('blue', '━━ Verification ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
|
|
206
|
+
console.log(c('yellow', ' ⚠ --verify flag noted.'));
|
|
207
|
+
console.log(c('dim', ' Signature verification requires the secret key or public key.'));
|
|
208
|
+
console.log(c('dim', ' This tool decodes the token without verifying the signature.'));
|
|
209
|
+
console.log(c('dim', ' For full verification, use: https://jwt.io or a library like jsonwebtoken.'));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── EXPIRY BANNER ────────────────────────────────────────────────────────
|
|
213
|
+
if (payload.exp !== undefined && payload.exp < now) {
|
|
214
|
+
console.log('\n' + c('bgRed', c('bold', ' !! TOKEN IS EXPIRED !! ')));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log('');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── MAIN ──────────────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
const opts = {
|
|
223
|
+
verify: args.includes('--verify'),
|
|
224
|
+
raw: args.includes('--raw'),
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Handle --help / -h
|
|
228
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
229
|
+
printHelp();
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Filter out flags to get positional token argument
|
|
234
|
+
const positional = args.filter(a => !a.startsWith('--'));
|
|
235
|
+
|
|
236
|
+
if (positional.length > 0) {
|
|
237
|
+
// Token from CLI argument
|
|
238
|
+
decodeJWT(positional[0].trim(), opts);
|
|
239
|
+
} else if (!process.stdin.isTTY) {
|
|
240
|
+
// Token from stdin (pipe)
|
|
241
|
+
let data = '';
|
|
242
|
+
process.stdin.setEncoding('utf8');
|
|
243
|
+
process.stdin.on('data', chunk => { data += chunk; });
|
|
244
|
+
process.stdin.on('end', () => {
|
|
245
|
+
const token = data.trim();
|
|
246
|
+
if (!token) {
|
|
247
|
+
console.error(c('red', 'Error: No token provided via stdin.'));
|
|
248
|
+
printHelp();
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
decodeJWT(token, opts);
|
|
252
|
+
});
|
|
253
|
+
} else {
|
|
254
|
+
// No input at all
|
|
255
|
+
console.error(c('red', 'Error: No JWT token provided.\n'));
|
|
256
|
+
printHelp();
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chengyixu/jwt-decode-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Decode and inspect JWT tokens from the terminal. View header, payload, expiry, and more.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jwt-decode": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node index.js eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"jwt",
|
|
14
|
+
"token",
|
|
15
|
+
"decode",
|
|
16
|
+
"auth",
|
|
17
|
+
"cli",
|
|
18
|
+
"security",
|
|
19
|
+
"json-web-token",
|
|
20
|
+
"inspect",
|
|
21
|
+
"debug",
|
|
22
|
+
"authentication"
|
|
23
|
+
],
|
|
24
|
+
"author": "chengyixu",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/chengyixu/jwt-decode-cli.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/chengyixu/jwt-decode-cli#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=12.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|