mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-20 14:56:17 -04:00
refactor(jwt-parser): simplified code
This commit is contained in:
parent
acc7f0a586
commit
f52f7a845c
8 changed files with 203 additions and 558 deletions
|
@ -58,6 +58,7 @@
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"randombytes": "^2.1.0",
|
"randombytes": "^2.1.0",
|
||||||
"sql-formatter": "^8.2.0",
|
"sql-formatter": "^8.2.0",
|
||||||
|
"ts-pattern": "^4.1.3",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
|
|
75
pnpm-lock.yaml
generated
75
pnpm-lock.yaml
generated
|
@ -56,6 +56,7 @@ specifiers:
|
||||||
sql-formatter: ^8.2.0
|
sql-formatter: ^8.2.0
|
||||||
standard-version: ^9.5.0
|
standard-version: ^9.5.0
|
||||||
start-server-and-test: ^1.14.0
|
start-server-and-test: ^1.14.0
|
||||||
|
ts-pattern: ^4.1.3
|
||||||
typescript: ~4.5.5
|
typescript: ~4.5.5
|
||||||
uuid: ^8.3.2
|
uuid: ^8.3.2
|
||||||
vite: ^2.9.15
|
vite: ^2.9.15
|
||||||
|
@ -96,6 +97,7 @@ dependencies:
|
||||||
qrcode: 1.5.1
|
qrcode: 1.5.1
|
||||||
randombytes: 2.1.0
|
randombytes: 2.1.0
|
||||||
sql-formatter: 8.2.0
|
sql-formatter: 8.2.0
|
||||||
|
ts-pattern: 4.1.3
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
vue-router: 4.1.6_vue@3.2.45
|
vue-router: 4.1.6_vue@3.2.45
|
||||||
|
@ -122,7 +124,7 @@ devDependencies:
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-config-prettier: 8.5.0_eslint@8.27.0
|
eslint-config-prettier: 8.5.0_eslint@8.27.0
|
||||||
eslint-import-resolver-typescript: 3.5.2_dcpv4nbdr5ks2h5677xdltrk6e
|
eslint-import-resolver-typescript: 3.5.2_dcpv4nbdr5ks2h5677xdltrk6e
|
||||||
eslint-plugin-import: 2.26.0_gbipkkcbnjmysmpjttq6vkmfqq
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
eslint-plugin-vue: 8.7.1_eslint@8.27.0
|
eslint-plugin-vue: 8.7.1_eslint@8.27.0
|
||||||
jsdom: 19.0.0
|
jsdom: 19.0.0
|
||||||
less: 4.1.3
|
less: 4.1.3
|
||||||
|
@ -132,7 +134,7 @@ devDependencies:
|
||||||
typescript: 4.5.5
|
typescript: 4.5.5
|
||||||
vite: 2.9.15_less@4.1.3
|
vite: 2.9.15_less@4.1.3
|
||||||
vite-plugin-md: 0.12.4_vite@2.9.15
|
vite-plugin-md: 0.12.4_vite@2.9.15
|
||||||
vite-plugin-pwa: 0.11.13_7mbbuzxp22mje5bxdolj2b6yg4
|
vite-plugin-pwa: 0.11.13_vite@2.9.15
|
||||||
vite-svg-loader: 3.6.0
|
vite-svg-loader: 3.6.0
|
||||||
vitest: 0.13.1_uwxj23d3xojfwkqpytqc7pyhry
|
vitest: 0.13.1_uwxj23d3xojfwkqpytqc7pyhry
|
||||||
vue-tsc: 0.31.4_typescript@4.5.5
|
vue-tsc: 0.31.4_typescript@4.5.5
|
||||||
|
@ -2066,10 +2068,6 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
|
||||||
eslint-plugin-vue: ^8.0.1
|
eslint-plugin-vue: ^8.0.1
|
||||||
typescript: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
typescript:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 5.42.1_vfr6z4qvdp6defk3ked6x75zyi
|
'@typescript-eslint/eslint-plugin': 5.42.1_vfr6z4qvdp6defk3ked6x75zyi
|
||||||
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
|
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
|
||||||
|
@ -2079,6 +2077,7 @@ packages:
|
||||||
vue-eslint-parser: 8.3.0_eslint@8.27.0
|
vue-eslint-parser: 8.3.0_eslint@8.27.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vue/reactivity-transform/3.2.45:
|
/@vue/reactivity-transform/3.2.45:
|
||||||
|
@ -2107,6 +2106,7 @@ packages:
|
||||||
'@vue/runtime-core': 3.2.45
|
'@vue/runtime-core': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
csstype: 2.6.21
|
csstype: 2.6.21
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vue/server-renderer/3.2.45_vue@3.2.45:
|
/@vue/server-renderer/3.2.45_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
||||||
|
@ -2116,6 +2116,7 @@ packages:
|
||||||
'@vue/compiler-ssr': 3.2.45
|
'@vue/compiler-ssr': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vue/shared/3.2.45:
|
/@vue/shared/3.2.45:
|
||||||
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
||||||
|
@ -2860,8 +2861,8 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
JSONStream: 1.3.5
|
|
||||||
is-text-path: 1.0.1
|
is-text-path: 1.0.1
|
||||||
|
JSONStream: 1.3.5
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
meow: 8.1.2
|
meow: 8.1.2
|
||||||
split2: 3.2.2
|
split2: 3.2.2
|
||||||
|
@ -2990,6 +2991,7 @@ packages:
|
||||||
|
|
||||||
/csstype/2.6.21:
|
/csstype/2.6.21:
|
||||||
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/csstype/3.0.11:
|
/csstype/3.0.11:
|
||||||
resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
|
resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
|
||||||
|
@ -3028,22 +3030,12 @@ packages:
|
||||||
|
|
||||||
/debug/2.6.9:
|
/debug/2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.0.0
|
ms: 2.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/debug/3.2.7:
|
/debug/3.2.7:
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3582,8 +3574,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-import-resolver-typescript/3.5.2_dcpv4nbdr5ks2h5677xdltrk6e:
|
/eslint-import-resolver-typescript/3.5.2_dcpv4nbdr5ks2h5677xdltrk6e:
|
||||||
|
@ -3596,7 +3586,7 @@ packages:
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
enhanced-resolve: 5.10.0
|
enhanced-resolve: 5.10.0
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-plugin-import: 2.26.0_gbipkkcbnjmysmpjttq6vkmfqq
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
get-tsconfig: 4.2.0
|
get-tsconfig: 4.2.0
|
||||||
globby: 13.1.2
|
globby: 13.1.2
|
||||||
is-core-module: 2.11.0
|
is-core-module: 2.11.0
|
||||||
|
@ -3606,54 +3596,32 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-module-utils/2.7.4_idrr6ghswzssuopqxluk4kfum4:
|
/eslint-module-utils/2.7.4_eslint@8.27.0:
|
||||||
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
|
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@typescript-eslint/parser': '*'
|
|
||||||
eslint: '*'
|
eslint: '*'
|
||||||
eslint-import-resolver-node: '*'
|
|
||||||
eslint-import-resolver-typescript: '*'
|
|
||||||
eslint-import-resolver-webpack: '*'
|
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@typescript-eslint/parser':
|
|
||||||
optional: true
|
|
||||||
eslint:
|
eslint:
|
||||||
optional: true
|
optional: true
|
||||||
eslint-import-resolver-node:
|
|
||||||
optional: true
|
|
||||||
eslint-import-resolver-typescript:
|
|
||||||
optional: true
|
|
||||||
eslint-import-resolver-webpack:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
|
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-import-resolver-node: 0.3.6
|
|
||||||
eslint-import-resolver-typescript: 3.5.2_dcpv4nbdr5ks2h5677xdltrk6e
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-import/2.26.0_gbipkkcbnjmysmpjttq6vkmfqq:
|
/eslint-plugin-import/2.26.0_eslint@8.27.0:
|
||||||
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
|
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@typescript-eslint/parser': '*'
|
|
||||||
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
|
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
|
||||||
peerDependenciesMeta:
|
|
||||||
'@typescript-eslint/parser':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
|
|
||||||
array-includes: 3.1.6
|
array-includes: 3.1.6
|
||||||
array.prototype.flat: 1.3.1
|
array.prototype.flat: 1.3.1
|
||||||
debug: 2.6.9
|
debug: 2.6.9
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-import-resolver-node: 0.3.6
|
eslint-import-resolver-node: 0.3.6
|
||||||
eslint-module-utils: 2.7.4_idrr6ghswzssuopqxluk4kfum4
|
eslint-module-utils: 2.7.4_eslint@8.27.0
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
is-core-module: 2.11.0
|
is-core-module: 2.11.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
@ -3661,10 +3629,6 @@ packages:
|
||||||
object.values: 1.1.6
|
object.values: 1.1.6
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
tsconfig-paths: 3.14.1
|
tsconfig-paths: 3.14.1
|
||||||
transitivePeerDependencies:
|
|
||||||
- eslint-import-resolver-typescript
|
|
||||||
- eslint-import-resolver-webpack
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-prettier/4.2.1_v7o5sx5x3wbs57ifz6wc4f76we:
|
/eslint-plugin-prettier/4.2.1_v7o5sx5x3wbs57ifz6wc4f76we:
|
||||||
|
@ -4870,8 +4834,6 @@ packages:
|
||||||
mime: 1.6.0
|
mime: 1.6.0
|
||||||
needle: 3.1.0
|
needle: 3.1.0
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/leven/3.1.0:
|
/leven/3.1.0:
|
||||||
|
@ -5214,8 +5176,6 @@ packages:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
iconv-lite: 0.6.3
|
iconv-lite: 0.6.3
|
||||||
sax: 1.2.4
|
sax: 1.2.4
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
@ -6495,6 +6455,10 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ts-pattern/4.1.3:
|
||||||
|
resolution: {integrity: sha512-8beXMWTGEv1JfDjSxfNhe4uT5jKYdhmEUKzt4gZW9dmHlquq3b+IbEyA7vX9LjBfzHmvKnM4HiomAUCyaW2Pew==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tsconfig-paths/3.14.1:
|
/tsconfig-paths/3.14.1:
|
||||||
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
|
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6578,6 +6542,7 @@ packages:
|
||||||
resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==}
|
resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=4.2.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/uc.micro/1.0.6:
|
/uc.micro/1.0.6:
|
||||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||||
|
@ -6734,11 +6699,10 @@ packages:
|
||||||
vite: 2.9.15_less@4.1.3
|
vite: 2.9.15_less@4.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-pwa/0.11.13_7mbbuzxp22mje5bxdolj2b6yg4:
|
/vite-plugin-pwa/0.11.13_vite@2.9.15:
|
||||||
resolution: {integrity: sha512-Ssj14m3TRVLfkFEAWSMcFE2d1cSdEZyrVTzfY2lSL+umHYvcIFHVDAY143sygtBCb44OPczsAOmWwBTxwOvh7g==}
|
resolution: {integrity: sha512-Ssj14m3TRVLfkFEAWSMcFE2d1cSdEZyrVTzfY2lSL+umHYvcIFHVDAY143sygtBCb44OPczsAOmWwBTxwOvh7g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.0.0
|
vite: ^2.0.0
|
||||||
workbox-window: ^6.4.0
|
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
fast-glob: 3.2.12
|
fast-glob: 3.2.12
|
||||||
|
@ -7004,6 +6968,7 @@ packages:
|
||||||
'@vue/runtime-dom': 3.2.45
|
'@vue/runtime-dom': 3.2.45
|
||||||
'@vue/server-renderer': 3.2.45_vue@3.2.45
|
'@vue/server-renderer': 3.2.45_vue@3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vueuc/0.4.49_vue@3.2.45:
|
/vueuc/0.4.49_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-WarAC44a/Yx78CxkAgROYLq+LkAeCGA/6wHidVoFmHLbzyF3SiP2nzRNGD/8zJeJInXv18EnWK6A//eGgMMq8w==}
|
resolution: {integrity: sha512-WarAC44a/Yx78CxkAgROYLq+LkAeCGA/6wHidVoFmHLbzyF3SiP2nzRNGD/8zJeJInXv18EnWK6A//eGgMMq8w==}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<template>
|
|
||||||
<n-space>
|
|
||||||
<em>{{ claim }}</em>
|
|
||||||
<span v-if="label.label !== claim">
|
|
||||||
<n-popover placement="right" trigger="hover">
|
|
||||||
<template #trigger>
|
|
||||||
<n-icon :component="InfoCircle" trigger />
|
|
||||||
</template>
|
|
||||||
{{ label.label }}
|
|
||||||
<template v-if="label.ref !== ''" #footer> {{ label.ref }} </template>
|
|
||||||
</n-popover>
|
|
||||||
</span>
|
|
||||||
</n-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { InfoCircle } from '@vicons/tabler';
|
|
||||||
import { getClaimLabel } from './jwt-parser.service';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
claim: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const label = computed(() => getClaimLabel(props.claim ? props.claim : ''));
|
|
||||||
</script>
|
|
|
@ -4,8 +4,24 @@ import { defineTool } from '../tool';
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'JWT parser',
|
name: 'JWT parser',
|
||||||
path: '/jwt-parser',
|
path: '/jwt-parser',
|
||||||
description: 'Parse a JWT (JSON Web Token) to display its content.',
|
description: 'Parse and decode your JSON Web Token (jwt) and display its content.',
|
||||||
keywords: ['jwt', 'parser'],
|
keywords: [
|
||||||
|
'jwt',
|
||||||
|
'parser',
|
||||||
|
'decode',
|
||||||
|
'typ',
|
||||||
|
'alg',
|
||||||
|
'iss',
|
||||||
|
'sub',
|
||||||
|
'aud',
|
||||||
|
'exp',
|
||||||
|
'nbf',
|
||||||
|
'iat',
|
||||||
|
'jti',
|
||||||
|
'json',
|
||||||
|
'web',
|
||||||
|
'token',
|
||||||
|
],
|
||||||
component: () => import('./jwt-parser.vue'),
|
component: () => import('./jwt-parser.vue'),
|
||||||
icon: Key,
|
icon: Key,
|
||||||
});
|
});
|
||||||
|
|
92
src/tools/jwt-parser/jwt-parser.constants.ts
Normal file
92
src/tools/jwt-parser/jwt-parser.constants.ts
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// From https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
|
||||||
|
export const ALGORITHM_DESCRIPTIONS: { [k: string]: string } = {
|
||||||
|
HS256: 'HMAC using SHA-256',
|
||||||
|
HS384: 'HMAC using SHA-384',
|
||||||
|
HS512: 'HMAC using SHA-512',
|
||||||
|
RS256: 'RSASSA-PKCS1-v1_5 using SHA-256',
|
||||||
|
RS384: 'RSASSA-PKCS1-v1_5 using SHA-384',
|
||||||
|
RS512: 'RSASSA-PKCS1-v1_5 using SHA-512',
|
||||||
|
ES256: 'ECDSA using P-256 and SHA-256',
|
||||||
|
ES384: 'ECDSA using P-384 and SHA-384',
|
||||||
|
ES512: 'ECDSA using P-521 and SHA-512',
|
||||||
|
PS256: 'RSASSA-PSS using SHA-256 and MGF1 with SHA-256',
|
||||||
|
PS384: 'RSASSA-PSS using SHA-384 and MGF1 with SHA-384',
|
||||||
|
PS512: 'RSASSA-PSS using SHA-512 and MGF1 with SHA-512',
|
||||||
|
none: 'No digital signature or MAC performed',
|
||||||
|
};
|
||||||
|
|
||||||
|
// List extracted from IANA: https://www.iana.org/assignments/jwt/jwt.xhtml
|
||||||
|
export const CLAIM_DESCRIPTIONS: Record<string, string> = {
|
||||||
|
typ: 'Type',
|
||||||
|
alg: 'Algorithm',
|
||||||
|
iss: 'Issuer',
|
||||||
|
sub: 'Subject',
|
||||||
|
aud: 'Audience',
|
||||||
|
exp: 'Expiration Time',
|
||||||
|
nbf: 'Not Before',
|
||||||
|
iat: 'Issued At',
|
||||||
|
jti: 'JWT ID',
|
||||||
|
name: 'Full name',
|
||||||
|
given_name: 'Given name(s) or first name(s)',
|
||||||
|
family_name: 'Surname(s) or last name(s)',
|
||||||
|
middle_name: 'Middle name(s)',
|
||||||
|
nickname: 'Casual name',
|
||||||
|
preferred_username: 'Shorthand name by which the End-User wishes to be referred to',
|
||||||
|
profile: 'Profile page URL',
|
||||||
|
picture: 'Profile picture URL',
|
||||||
|
website: 'Web page or blog URL',
|
||||||
|
email: 'Preferred e-mail address',
|
||||||
|
email_verified: 'True if the e-mail address has been verified; otherwise false',
|
||||||
|
gender: 'Gender',
|
||||||
|
birthdate: 'Birthday',
|
||||||
|
zoneinfo: 'Time zone',
|
||||||
|
locale: 'Locale',
|
||||||
|
phone_number: 'Preferred telephone number',
|
||||||
|
phone_number_verified: 'True if the phone number has been verified; otherwise false',
|
||||||
|
address: 'Preferred postal address',
|
||||||
|
updated_at: 'Time the information was last updated',
|
||||||
|
azp: 'Authorized party - the party to which the ID Token was issued',
|
||||||
|
nonce: 'Value used to associate a Client session with an ID Token',
|
||||||
|
auth_time: 'Time when the authentication occurred',
|
||||||
|
at_hash: 'Access Token hash value',
|
||||||
|
c_hash: 'Code hash value',
|
||||||
|
acr: 'Authentication Context Class Reference',
|
||||||
|
amr: 'Authentication Methods References',
|
||||||
|
sub_jwk: 'Public key used to check the signature of an ID Token',
|
||||||
|
cnf: 'Confirmation',
|
||||||
|
sip_from_tag: 'SIP From tag header field parameter value',
|
||||||
|
sip_date: 'SIP Date header field value',
|
||||||
|
sip_callid: 'SIP Call-Id header field value',
|
||||||
|
sip_cseq_num: 'SIP CSeq numeric header field parameter value',
|
||||||
|
sip_via_branch: 'SIP Via branch header field parameter value',
|
||||||
|
orig: 'Originating Identity String',
|
||||||
|
dest: 'Destination Identity String',
|
||||||
|
mky: 'Media Key Fingerprint String',
|
||||||
|
events: 'Security Events',
|
||||||
|
toe: 'Time of Event',
|
||||||
|
txn: 'Transaction Identifier',
|
||||||
|
rph: 'Resource Priority Header Authorization',
|
||||||
|
sid: 'Session ID',
|
||||||
|
vot: 'Vector of Trust value',
|
||||||
|
vtm: 'Vector of Trust trustmark URL',
|
||||||
|
attest: 'Attestation level as defined in SHAKEN framework',
|
||||||
|
origid: 'Originating Identifier as defined in SHAKEN framework',
|
||||||
|
act: 'Actor',
|
||||||
|
scope: 'Scope Values',
|
||||||
|
client_id: 'Client Identifier',
|
||||||
|
may_act: 'Authorized Actor - the party that is authorized to become the actor',
|
||||||
|
jcard: 'jCard data',
|
||||||
|
at_use_nbr: 'Number of API requests for which the access token can be used',
|
||||||
|
div: 'Diverted Target of a Call',
|
||||||
|
opt: 'Original PASSporT (in Full Form)',
|
||||||
|
vc: 'Verifiable Credential as specified in the W3C Recommendation',
|
||||||
|
vp: 'Verifiable Presentation as specified in the W3C Recommendation',
|
||||||
|
sph: 'SIP Priority header field',
|
||||||
|
ace_profile: 'ACE profile a token is supposed to be used with.',
|
||||||
|
cnonce: 'Client nonce',
|
||||||
|
exi: 'Expires in',
|
||||||
|
roles: 'Roles',
|
||||||
|
groups: 'Groups',
|
||||||
|
entitlements: 'Entitlements',
|
||||||
|
token_introspection: 'Token introspection response',
|
||||||
|
};
|
|
@ -1,429 +1,46 @@
|
||||||
import jwt_decode, { InvalidTokenError } from 'jwt-decode';
|
import jwtDecode, { type JwtHeader, type JwtPayload } from 'jwt-decode';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
import { ALGORITHM_DESCRIPTIONS, CLAIM_DESCRIPTIONS } from './jwt-parser.constants';
|
||||||
|
|
||||||
interface JWT {
|
export { decodeJwt };
|
||||||
header: Map<string, unknown>;
|
|
||||||
payload: Map<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function safeJwtDecode(rawJwt: string): JWT {
|
function decodeJwt({ jwt }: { jwt: string }) {
|
||||||
try {
|
const rawHeader = jwtDecode<JwtHeader>(jwt, { header: true });
|
||||||
const header = jwt_decode(rawJwt, { header: true }) as Map<string, unknown>;
|
const rawPayload = jwtDecode<JwtPayload>(jwt);
|
||||||
const payload = jwt_decode(rawJwt) as Map<string, unknown>;
|
|
||||||
return { header, payload };
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof InvalidTokenError) {
|
|
||||||
return { header: new Map<string, unknown>(), payload: new Map<string, unknown>() };
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getClaimLabel(claim: string): { label: string; ref: string } {
|
const header = _.map(rawHeader, (value, claim) => parseClaims({ claim, value }));
|
||||||
const infos = STANDARD_CLAIMS.find((info) => info.name === claim);
|
const payload = _.map(rawPayload, (value, claim) => parseClaims({ claim, value }));
|
||||||
if (infos) {
|
|
||||||
return { label: infos.long_name, ref: infos.ref };
|
|
||||||
}
|
|
||||||
switch (claim) {
|
|
||||||
case 'typ':
|
|
||||||
return { label: 'Type', ref: '' };
|
|
||||||
case 'alg':
|
|
||||||
return { label: 'Algorithm', ref: '' };
|
|
||||||
}
|
|
||||||
return { label: claim, ref: '' };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseClaimValue(claim: string, value: unknown): { value: unknown; extension?: unknown } {
|
return {
|
||||||
switch (claim) {
|
header,
|
||||||
case 'exp':
|
payload,
|
||||||
case 'nbf':
|
|
||||||
case 'iat': {
|
|
||||||
// Convert to milliseconds, JWT specs says it should be in seconds, JS
|
|
||||||
// works with milliseconds
|
|
||||||
value = typeof value === 'string' ? parseInt(value) : value;
|
|
||||||
const date = new Date((value as number) * 1000);
|
|
||||||
return { value: `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`, extension: value };
|
|
||||||
}
|
|
||||||
case 'alg':
|
|
||||||
return { value: AlgorithmKeyDescriptionMapping[value as string], extension: value };
|
|
||||||
default:
|
|
||||||
if (typeof value === 'boolean') {
|
|
||||||
// Perhaps there's a better way to do this?
|
|
||||||
return { value: value ? 'true' : 'false' };
|
|
||||||
}
|
|
||||||
return { value: value };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// From https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
|
|
||||||
const AlgorithmKeyDescriptionMapping: { [k: string]: string } = {
|
|
||||||
HS256: 'HMAC using SHA-256',
|
|
||||||
HS384: 'HMAC using SHA-384',
|
|
||||||
HS512: 'HMAC using SHA-512',
|
|
||||||
RS256: 'RSASSA-PKCS1-v1_5 using SHA-256',
|
|
||||||
RS384: 'RSASSA-PKCS1-v1_5 using SHA-384',
|
|
||||||
RS512: 'RSASSA-PKCS1-v1_5 using SHA-512',
|
|
||||||
ES256: 'ECDSA using P-256 and SHA-256',
|
|
||||||
ES384: 'ECDSA using P-384 and SHA-384',
|
|
||||||
ES512: 'ECDSA using P-521 and SHA-512',
|
|
||||||
PS256: 'RSASSA-PSS using SHA-256 and MGF1 with SHA-256',
|
|
||||||
PS384: 'RSASSA-PSS using SHA-384 and MGF1 with SHA-384',
|
|
||||||
PS512: 'RSASSA-PSS using SHA-512 and MGF1 with SHA-512',
|
|
||||||
none: 'No digital signature or MAC performed',
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// List extracted from IANA: https://www.iana.org/assignments/jwt/jwt.xhtml
|
function parseClaims({ claim, value }: { claim: string; value: unknown }) {
|
||||||
const STANDARD_CLAIMS = [
|
const claimDescription = CLAIM_DESCRIPTIONS[claim];
|
||||||
{
|
const formattedValue = _.toString(value);
|
||||||
name: 'iss',
|
const friendlyValue = getFriendlyValue({ claim, value });
|
||||||
long_name: 'Issuer',
|
|
||||||
ref: '[RFC7519 - Section 4.1.1]',
|
return {
|
||||||
},
|
value: formattedValue,
|
||||||
{
|
friendlyValue,
|
||||||
name: 'sub',
|
claim,
|
||||||
long_name: 'Subject',
|
claimDescription,
|
||||||
ref: '[RFC7519 - Section 4.1.2]',
|
};
|
||||||
},
|
}
|
||||||
{
|
|
||||||
name: 'aud',
|
function getFriendlyValue({ claim, value }: { claim: string; value: unknown }) {
|
||||||
long_name: 'Audience',
|
return match(claim)
|
||||||
ref: '[RFC7519 - Section 4.1.3]',
|
.with('exp', 'nbf', 'iat', () => dateFormatter(value))
|
||||||
},
|
.with('alg', () => (_.isString(value) ? ALGORITHM_DESCRIPTIONS[value] : undefined))
|
||||||
{
|
.otherwise(() => undefined);
|
||||||
name: 'exp',
|
}
|
||||||
long_name: 'Expiration Time',
|
|
||||||
ref: '[RFC7519 - Section 4.1.4]',
|
const dateFormatter = (value: unknown) => {
|
||||||
},
|
if (_.isNil(value)) return undefined;
|
||||||
{
|
|
||||||
name: 'nbf',
|
const date = new Date(Number(value) * 1000);
|
||||||
long_name: 'Not Before',
|
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
||||||
ref: '[RFC7519 - Section 4.1.5]',
|
};
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'iat',
|
|
||||||
long_name: 'Issued At',
|
|
||||||
ref: '[RFC7519 - Section 4.1.6]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'jti',
|
|
||||||
long_name: 'JWT ID',
|
|
||||||
ref: '[RFC7519 - Section 4.1.7]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
long_name: 'Full name',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'given_name',
|
|
||||||
long_name: 'Given name(s) or first name(s)',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'family_name',
|
|
||||||
long_name: 'Surname(s) or last name(s)',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'middle_name',
|
|
||||||
long_name: 'Middle name(s)',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nickname',
|
|
||||||
long_name: 'Casual name',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preferred_username',
|
|
||||||
long_name: 'Shorthand name by which the End-User wishes to be referred to',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'profile',
|
|
||||||
long_name: 'Profile page URL',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'picture',
|
|
||||||
long_name: 'Profile picture URL',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'website',
|
|
||||||
long_name: 'Web page or blog URL',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'email',
|
|
||||||
long_name: 'Preferred e-mail address',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'email_verified',
|
|
||||||
long_name: 'True if the e-mail address has been verified; otherwise false',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gender',
|
|
||||||
long_name: 'Gender',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'birthdate',
|
|
||||||
long_name: 'Birthday',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zoneinfo',
|
|
||||||
long_name: 'Time zone',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'locale',
|
|
||||||
long_name: 'Locale',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'phone_number',
|
|
||||||
long_name: 'Preferred telephone number',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'phone_number_verified',
|
|
||||||
long_name: 'True if the phone number has been verified; otherwise false',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'address',
|
|
||||||
long_name: 'Preferred postal address',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updated_at',
|
|
||||||
long_name: 'Time the information was last updated',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 5.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'azp',
|
|
||||||
long_name: 'Authorized party - the party to which the ID Token was issued',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nonce',
|
|
||||||
long_name: 'Value used to associate a Client session with an ID Token',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'auth_time',
|
|
||||||
long_name: 'Time when the authentication occurred',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'at_hash',
|
|
||||||
long_name: 'Access Token hash value',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'c_hash',
|
|
||||||
long_name: 'Code hash value',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 3.3.2.11]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'acr',
|
|
||||||
long_name: 'Authentication Context Class Reference',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'amr',
|
|
||||||
long_name: 'Authentication Methods References',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sub_jwk',
|
|
||||||
long_name: 'Public key used to check the signature of an ID Token',
|
|
||||||
ref: '[OpenID Connect Core 1.0 - Section 7.4]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cnf',
|
|
||||||
long_name: 'Confirmation',
|
|
||||||
ref: '[RFC7800 - Section 3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sip_from_tag',
|
|
||||||
long_name: 'SIP From tag header field parameter value',
|
|
||||||
ref: '[RFC8055][RFC3261]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sip_date',
|
|
||||||
long_name: 'SIP Date header field value',
|
|
||||||
ref: '[RFC8055][RFC3261]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sip_callid',
|
|
||||||
long_name: 'SIP Call-Id header field value',
|
|
||||||
ref: '[RFC8055][RFC3261]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sip_cseq_num',
|
|
||||||
long_name: 'SIP CSeq numeric header field parameter value',
|
|
||||||
ref: '[RFC8055][RFC3261]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sip_via_branch',
|
|
||||||
long_name: 'SIP Via branch header field parameter value',
|
|
||||||
ref: '[RFC8055][RFC3261]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'orig',
|
|
||||||
long_name: 'Originating Identity String',
|
|
||||||
ref: '[RFC8225 - Section 5.2.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dest',
|
|
||||||
long_name: 'Destination Identity String',
|
|
||||||
ref: '[RFC8225 - Section 5.2.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mky',
|
|
||||||
long_name: 'Media Key Fingerprint String',
|
|
||||||
ref: '[RFC8225 - Section 5.2.2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'events',
|
|
||||||
long_name: 'Security Events',
|
|
||||||
ref: '[RFC8417 - Section 2.2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'toe',
|
|
||||||
long_name: 'Time of Event',
|
|
||||||
ref: '[RFC8417 - Section 2.2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'txn',
|
|
||||||
long_name: 'Transaction Identifier',
|
|
||||||
ref: '[RFC8417 - Section 2.2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rph',
|
|
||||||
long_name: 'Resource Priority Header Authorization',
|
|
||||||
ref: '[RFC8443 - Section 3]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sid',
|
|
||||||
long_name: 'Session ID',
|
|
||||||
ref: '[OpenID Connect Front-Channel Logout 1.0 - Section 3]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vot',
|
|
||||||
long_name: 'Vector of Trust value',
|
|
||||||
ref: '[RFC8485]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vtm',
|
|
||||||
long_name: 'Vector of Trust trustmark URL',
|
|
||||||
ref: '[RFC8485]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'attest',
|
|
||||||
long_name: 'Attestation level as defined in SHAKEN framework',
|
|
||||||
ref: '[RFC8588]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'origid',
|
|
||||||
long_name: 'Originating Identifier as defined in SHAKEN framework',
|
|
||||||
ref: '[RFC8588]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'act',
|
|
||||||
long_name: 'Actor',
|
|
||||||
ref: '[RFC8693 - Section 4.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'scope',
|
|
||||||
long_name: 'Scope Values',
|
|
||||||
ref: '[RFC8693 - Section 4.2]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'client_id',
|
|
||||||
long_name: 'Client Identifier',
|
|
||||||
ref: '[RFC8693 - Section 4.3]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'may_act',
|
|
||||||
long_name: 'Authorized Actor - the party that is authorized to become the actor',
|
|
||||||
ref: '[RFC8693 - Section 4.4]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'jcard',
|
|
||||||
long_name: 'jCard data',
|
|
||||||
ref: '[RFC8688][RFC7095]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'at_use_nbr',
|
|
||||||
long_name: 'Number of API requests for which the access token can be used',
|
|
||||||
ref: '[ETSI GS NFV-SEC 022 V2.7.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'div',
|
|
||||||
long_name: 'Diverted Target of a Call',
|
|
||||||
ref: '[RFC8946]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'opt',
|
|
||||||
long_name: 'Original PASSporT (in Full Form)',
|
|
||||||
ref: '[RFC8946]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vc',
|
|
||||||
long_name: 'Verifiable Credential as specified in the W3C Recommendation',
|
|
||||||
ref: '[W3C Recommendation Verifiable Credentials Data Model 1.0 - Expressing verifiable information on the Web (19 November 2019) - Section 6.3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vp',
|
|
||||||
long_name: 'Verifiable Presentation as specified in the W3C Recommendation',
|
|
||||||
ref: '[W3C Recommendation Verifiable Credentials Data Model 1.0 - Expressing verifiable information on the Web (19 November 2019) - Section 6.3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sph',
|
|
||||||
long_name: 'SIP Priority header field',
|
|
||||||
ref: '[RFC9027]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ace_profile',
|
|
||||||
long_name: 'The ACE profile a token is supposed to be used with.',
|
|
||||||
ref: '[RFC-ietf-ace-oauth-authz-46 - Section 5.10]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cnonce',
|
|
||||||
long_name:
|
|
||||||
'client-nonce. A nonce previously provided to the AS by the RS via the client. Used to verify token freshness when the RS cannot synchronize its clock with the AS.',
|
|
||||||
ref: '[RFC-ietf-ace-oauth-authz-46 - Section 5.10]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'exi',
|
|
||||||
long_name:
|
|
||||||
'Expires in. Lifetime of the token in seconds from the time the RS first sees it. Used to implement a weaker from of token expiration for devices that cannot synchronize their internal clocks.',
|
|
||||||
ref: '[RFC-ietf-ace-oauth-authz-46 - Section 5.10.3]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'roles',
|
|
||||||
long_name: 'Roles',
|
|
||||||
ref: '[RFC7643 - Section 4.1.2][RFC9068 - Section 2.2.3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'groups',
|
|
||||||
long_name: 'Groups',
|
|
||||||
ref: '[RFC7643 - Section 4.1.2][RFC9068 - Section 2.2.3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'entitlements',
|
|
||||||
long_name: 'Entitlements',
|
|
||||||
ref: '[RFC7643 - Section 4.1.2][RFC9068 - Section 2.2.3.1]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'token_introspection',
|
|
||||||
long_name: 'Token introspection response',
|
|
||||||
ref: '[RFC-ietf-oauth-jwt-introspection-response-12 - Section 5]',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
|
@ -4,48 +4,59 @@
|
||||||
<n-input v-model:value="rawJwt" type="textarea" placeholder="Put your token here..." rows="5" />
|
<n-input v-model:value="rawJwt" type="textarea" placeholder="Put your token here..." rows="5" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<n-table>
|
<n-table v-if="validation.isValid">
|
||||||
<tbody>
|
<tbody>
|
||||||
<td colspan="2" class="table-header"><strong>Header</strong></td>
|
<template v-for="section of sections" :key="section.key">
|
||||||
<tr v-for="[key, value] in Object.entries(decodedJWT.header)" :key="key">
|
<th colspan="2" class="table-header">{{ section.title }}</th>
|
||||||
<td class="claims"><claim-vue :claim="key" /></td>
|
<tr v-for="{ claim, claimDescription, friendlyValue, value } in decodedJWT[section.key]" :key="claim + value">
|
||||||
|
<td class="claims">
|
||||||
|
<n-space>
|
||||||
|
<n-text strong>{{ claim }}</n-text>
|
||||||
|
<template v-if="claimDescription">
|
||||||
|
<n-text depth="3">({{ claimDescription }})</n-text>
|
||||||
|
</template>
|
||||||
|
</n-space>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<value-vue :claim="key" :value="value" />
|
<n-space>
|
||||||
</td>
|
<n-text>{{ value }}</n-text>
|
||||||
</tr>
|
<template v-if="friendlyValue">
|
||||||
<td colspan="2" class="table-header"><strong>Payload</strong></td>
|
<n-text depth="3">({{ friendlyValue }})</n-text>
|
||||||
<tr v-for="[key, value] in Object.entries(decodedJWT.payload)" :key="key">
|
</template>
|
||||||
<td class="claims"><claim-vue :claim="key" /></td>
|
</n-space>
|
||||||
<td>
|
|
||||||
<value-vue :claim="key" :value="value" />
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</n-table>
|
</n-table>
|
||||||
</n-card>
|
</n-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
|
||||||
import jwt_decode from 'jwt-decode';
|
|
||||||
import { useValidation } from '@/composable/validation';
|
import { useValidation } from '@/composable/validation';
|
||||||
import { isNotThrowing } from '@/utils/boolean';
|
import { isNotThrowing } from '@/utils/boolean';
|
||||||
import { safeJwtDecode } from './jwt-parser.service';
|
import { withDefaultOnError } from '@/utils/defaults';
|
||||||
import claimVue from './claim.vue';
|
import { computed, ref } from 'vue';
|
||||||
import valueVue from './value.vue';
|
import { decodeJwt } from './jwt-parser.service';
|
||||||
|
|
||||||
const rawJwt = ref(
|
const rawJwt = ref(
|
||||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
|
||||||
);
|
);
|
||||||
|
|
||||||
const decodedJWT = computed(() => {
|
const decodedJWT = computed(() =>
|
||||||
return safeJwtDecode(rawJwt.value);
|
withDefaultOnError(() => decodeJwt({ jwt: rawJwt.value }), { header: [], payload: [] }),
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
{ key: 'header', title: 'Header' },
|
||||||
|
{ key: 'payload', title: 'Payload' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
const validation = useValidation({
|
const validation = useValidation({
|
||||||
source: rawJwt,
|
source: rawJwt,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
validator: (value) => value.length > 0 && isNotThrowing(() => jwt_decode(value, { header: true })),
|
validator: (value) => value.length > 0 && isNotThrowing(() => decodeJwt({ jwt: rawJwt.value })),
|
||||||
message: 'Invalid JWT',
|
message: 'Invalid JWT',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -56,8 +67,4 @@ const validation = useValidation({
|
||||||
.table-header {
|
.table-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.claims {
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<template>
|
|
||||||
<n-space>
|
|
||||||
{{ value.value }}
|
|
||||||
<em v-if="value.extension">({{ value.extension }})</em>
|
|
||||||
</n-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { parseClaimValue } from './jwt-parser.service';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
claim: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const value = computed(() => parseClaimValue(props.claim, props.value));
|
|
||||||
</script>
|
|
Loading…
Add table
Add a link
Reference in a new issue