mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-10-30 03:23:51 +00:00
Compare commits
7 Commits
v2.14.0
...
feat/jwt-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98b226de42 | ||
|
|
9f2874b23d | ||
|
|
b0d9fbbbaf | ||
|
|
7c100f4030 | ||
|
|
8d0a4273fb | ||
|
|
bb0eca80a7 | ||
|
|
3e4a64551f |
@@ -2,13 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [2.14.0](https://github.com/CorentinTh/it-tools/compare/v2.13.0...v2.14.0) (2022-11-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **new-tool:** chmod calculator ([35b5187](https://github.com/CorentinTh/it-tools/commit/35b518711938c2bc88f35d104bb35d9956f0c267))
|
||||
|
||||
## [2.13.0](https://github.com/CorentinTh/it-tools/compare/v2.11.0...v2.13.0) (2022-11-14)
|
||||
|
||||
|
||||
|
||||
17251
package-lock.json
generated
Normal file
17251
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "it-tools",
|
||||
"version": "2.14.0",
|
||||
"version": "2.13.0",
|
||||
"description": "Collection of handy online tools for developers, with great UX. ",
|
||||
"keywords": [
|
||||
"productivity",
|
||||
@@ -47,6 +47,7 @@
|
||||
"figue": "^1.2.0",
|
||||
"highlight.js": "^11.6.0",
|
||||
"json5": "^2.2.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mathjs": "^10.6.4",
|
||||
"mime-types": "^2.1.35",
|
||||
|
||||
@@ -53,11 +53,10 @@ import {
|
||||
NTooltip,
|
||||
NUpload,
|
||||
NUploadDragger,
|
||||
NCheckbox,
|
||||
NPopover,
|
||||
} from 'naive-ui';
|
||||
|
||||
const components = [
|
||||
NCheckbox,
|
||||
NDynamicInput,
|
||||
NDatePicker,
|
||||
NCode,
|
||||
@@ -111,6 +110,7 @@ const components = [
|
||||
NIcon,
|
||||
NSwitch,
|
||||
NCollapseTransition,
|
||||
NPopover,
|
||||
];
|
||||
|
||||
export const naive = create({ components });
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { expect, describe, it } from 'vitest';
|
||||
import { computeChmodOctalRepresentation } from './chmod-calculator.service';
|
||||
|
||||
describe('chmod-calculator', () => {
|
||||
describe('computeChmodOctalRepresentation', () => {
|
||||
it('get the octal representation from permissions', () => {
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: true, write: true, execute: true },
|
||||
group: { read: true, write: true, execute: true },
|
||||
public: { read: true, write: true, execute: true },
|
||||
},
|
||||
}),
|
||||
).to.eql('777');
|
||||
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: false, write: false, execute: false },
|
||||
group: { read: false, write: false, execute: false },
|
||||
public: { read: false, write: false, execute: false },
|
||||
},
|
||||
}),
|
||||
).to.eql('000');
|
||||
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: false, write: true, execute: false },
|
||||
group: { read: false, write: true, execute: true },
|
||||
public: { read: true, write: false, execute: true },
|
||||
},
|
||||
}),
|
||||
).to.eql('235');
|
||||
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: true, write: false, execute: false },
|
||||
group: { read: false, write: true, execute: false },
|
||||
public: { read: false, write: false, execute: true },
|
||||
},
|
||||
}),
|
||||
).to.eql('421');
|
||||
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: false, write: false, execute: true },
|
||||
group: { read: false, write: true, execute: false },
|
||||
public: { read: true, write: false, execute: false },
|
||||
},
|
||||
}),
|
||||
).to.eql('124');
|
||||
|
||||
expect(
|
||||
computeChmodOctalRepresentation({
|
||||
permissions: {
|
||||
owner: { read: false, write: true, execute: false },
|
||||
group: { read: false, write: true, execute: false },
|
||||
public: { read: false, write: true, execute: false },
|
||||
},
|
||||
}),
|
||||
).to.eql('222');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import type { GroupPermissions, Permissions } from './chmod-calculator.types';
|
||||
|
||||
export { computeChmodOctalRepresentation };
|
||||
|
||||
function computeChmodOctalRepresentation({ permissions }: { permissions: Permissions }): string {
|
||||
const permissionValue = { read: 4, write: 2, execute: 1 };
|
||||
|
||||
const getGroupPermissionValue = (permission: GroupPermissions) =>
|
||||
_.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(permissionValue, key, 0) : 0), 0);
|
||||
|
||||
return [
|
||||
getGroupPermissionValue(permissions.owner),
|
||||
getGroupPermissionValue(permissions.group),
|
||||
getGroupPermissionValue(permissions.public),
|
||||
].join('');
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export type Scope = 'read' | 'write' | 'execute';
|
||||
export type Group = 'owner' | 'group' | 'public';
|
||||
|
||||
export type GroupPermissions = {
|
||||
[k in Scope]: boolean;
|
||||
};
|
||||
|
||||
export type Permissions = {
|
||||
[k in Group]: GroupPermissions;
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-table :bordered="false" :bottom-bordered="false" single-column class="permission-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" scope="col"></th>
|
||||
<th class="text-center" scope="col">Owner (u)</th>
|
||||
<th class="text-center" scope="col">Group (g)</th>
|
||||
<th class="text-center" scope="col">Public (o)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="{ scope, title } of scopes" :key="scope">
|
||||
<td class="line-header">{{ title }}</td>
|
||||
<td v-for="group of groups" :key="group" class="text-center">
|
||||
<!-- <n-switch v-model:value="permissions[group][scope]" /> -->
|
||||
<n-checkbox v-model:checked="permissions[group][scope]" size="large" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
|
||||
<div class="octal-result">
|
||||
{{ octal }}
|
||||
</div>
|
||||
|
||||
<input-copyable :value="`chmod ${octal} path`" readonly style="margin-bottom: 5px" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computeChmodOctalRepresentation } from './chmod-calculator.service';
|
||||
import InputCopyable from '../../components/InputCopyable.vue';
|
||||
|
||||
import type { Group, Scope } from './chmod-calculator.types';
|
||||
|
||||
const themeVars = useThemeVars();
|
||||
|
||||
const scopes: { scope: Scope; title: string }[] = [
|
||||
{ scope: 'read', title: 'Read (4)' },
|
||||
{ scope: 'write', title: 'Write (2)' },
|
||||
{ scope: 'execute', title: 'Execute (1)' },
|
||||
];
|
||||
const groups: Group[] = ['owner', 'group', 'public'];
|
||||
|
||||
const permissions = ref({
|
||||
owner: { read: false, write: false, execute: false },
|
||||
group: { read: false, write: false, execute: false },
|
||||
public: { read: false, write: false, execute: false },
|
||||
});
|
||||
|
||||
const octal = computed(() => computeChmodOctalRepresentation({ permissions: permissions.value }));
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.octal-result {
|
||||
text-align: center;
|
||||
font-size: 50px;
|
||||
font-family: monospace;
|
||||
color: v-bind('themeVars.primaryColor');
|
||||
margin: 20px 0;
|
||||
}
|
||||
.permission-table {
|
||||
td,
|
||||
th {
|
||||
padding: 15px;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.line-header {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
max-width: 80px;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,22 +0,0 @@
|
||||
import { FileInvoice } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Chmod calculator',
|
||||
path: '/chmod-calculator',
|
||||
description: 'Compute your chmod permissions and commands with this online chmod calculator.',
|
||||
keywords: [
|
||||
'chmod',
|
||||
'calculator',
|
||||
'file',
|
||||
'permission',
|
||||
'files',
|
||||
'directory',
|
||||
'folder',
|
||||
'recursive',
|
||||
'generator',
|
||||
'octal',
|
||||
],
|
||||
component: () => import('./chmod-calculator.vue'),
|
||||
icon: FileInvoice,
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LockOpen } from '@vicons/tabler';
|
||||
import type { ToolCategory } from './tool';
|
||||
|
||||
import { tool as chmodCalculator } from './chmod-calculator';
|
||||
import { tool as jwtParser } from './jwt-parser';
|
||||
import { tool as mimeTypes } from './mime-types';
|
||||
import { tool as otpCodeGeneratorAndValidator } from './otp-code-generator-and-validator';
|
||||
import { tool as base64FileConverter } from './base64-file-converter';
|
||||
@@ -68,6 +68,7 @@ export const toolsByCategory: ToolCategory[] = [
|
||||
metaTagGenerator,
|
||||
otpCodeGeneratorAndValidator,
|
||||
mimeTypes,
|
||||
jwtParser,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -78,7 +79,7 @@ export const toolsByCategory: ToolCategory[] = [
|
||||
{
|
||||
name: 'Development',
|
||||
icon: LockOpen,
|
||||
components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, sqlPrettify, chmodCalculator],
|
||||
components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, sqlPrettify],
|
||||
},
|
||||
{
|
||||
name: 'Math',
|
||||
|
||||
29
src/tools/jwt-parser/claim.vue
Normal file
29
src/tools/jwt-parser/claim.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<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 { get_claim_label } from './jwt-parser.service';
|
||||
|
||||
const props = defineProps({
|
||||
claim: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const label = computed(() => get_claim_label(props.claim ? props.claim : ''));
|
||||
</script>
|
||||
11
src/tools/jwt-parser/index.ts
Normal file
11
src/tools/jwt-parser/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ArrowsShuffle } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'JWT parser',
|
||||
path: '/jwt-parser',
|
||||
description: '',
|
||||
keywords: ['jwt', 'parser'],
|
||||
component: () => import('./jwt-parser.vue'),
|
||||
icon: ArrowsShuffle,
|
||||
});
|
||||
429
src/tools/jwt-parser/jwt-parser.service.ts
Normal file
429
src/tools/jwt-parser/jwt-parser.service.ts
Normal file
@@ -0,0 +1,429 @@
|
||||
import jwt_decode, { InvalidTokenError } from 'jwt-decode';
|
||||
|
||||
interface JWT {
|
||||
header: Map<string, unknown>;
|
||||
payload: Map<string, unknown>;
|
||||
}
|
||||
|
||||
export function safe_jwt_decode(raw_jwt: string): JWT {
|
||||
try {
|
||||
const header = jwt_decode(raw_jwt, { header: true }) as Map<string, unknown>;
|
||||
const payload = jwt_decode(raw_jwt) as Map<string, unknown>;
|
||||
return { header: header, payload: payload };
|
||||
} catch (e) {
|
||||
if (e instanceof InvalidTokenError) {
|
||||
return { header: new Map<string, unknown>(), payload: new Map<string, unknown>() };
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function get_claim_label(claim: string): { label: string; ref: string } {
|
||||
const infos = STANDARD_CLAIMS.find((info) => info.name === claim);
|
||||
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 parse_claim_value(claim: string, value: unknown): { value: unknown; extension?: unknown } {
|
||||
switch (claim) {
|
||||
case 'exp':
|
||||
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
|
||||
const STANDARD_CLAIMS = [
|
||||
{
|
||||
name: 'iss',
|
||||
long_name: 'Issuer',
|
||||
ref: '[RFC7519 - Section 4.1.1]',
|
||||
},
|
||||
{
|
||||
name: 'sub',
|
||||
long_name: 'Subject',
|
||||
ref: '[RFC7519 - Section 4.1.2]',
|
||||
},
|
||||
{
|
||||
name: 'aud',
|
||||
long_name: 'Audience',
|
||||
ref: '[RFC7519 - Section 4.1.3]',
|
||||
},
|
||||
{
|
||||
name: 'exp',
|
||||
long_name: 'Expiration Time',
|
||||
ref: '[RFC7519 - Section 4.1.4]',
|
||||
},
|
||||
{
|
||||
name: 'nbf',
|
||||
long_name: 'Not Before',
|
||||
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]',
|
||||
},
|
||||
];
|
||||
69
src/tools/jwt-parser/jwt-parser.vue
Normal file
69
src/tools/jwt-parser/jwt-parser.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<n-card>
|
||||
<n-form-item label="JWT to decode" :feedback="validation.message" :validation-status="validation.status">
|
||||
<n-input v-model:value="raw_jwt" type="textarea" placeholder="Put your token here..." rows="5" />
|
||||
</n-form-item>
|
||||
<n-form-item label="Display parsed value?" label-placement="left" :show-feedback="false">
|
||||
<n-switch v-model:value="showParsedValues" />
|
||||
</n-form-item>
|
||||
|
||||
<n-table>
|
||||
<tbody>
|
||||
<td colspan="2" class="table-header"><strong>Header</strong></td>
|
||||
<tr v-for="[key, value] in Object.entries(decodedJWT.header)" :key="key">
|
||||
<td class="claims"><claim-vue :claim="key" /></td>
|
||||
<td>
|
||||
<span v-if="!showParsedValues">{{ value }}</span>
|
||||
<value-vue v-else :claim="key" :value="value" />
|
||||
</td>
|
||||
</tr>
|
||||
<td colspan="2" class="table-header"><strong>Payload</strong></td>
|
||||
<tr v-for="[key, value] in Object.entries(decodedJWT.payload)" :key="key">
|
||||
<td class="claims"><claim-vue :claim="key" /></td>
|
||||
<td>
|
||||
<span v-if="!showParsedValues">{{ value }}</span>
|
||||
<value-vue v-else :claim="key" :value="value" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { useValidation } from '@/composable/validation';
|
||||
import { isNotThrowing } from '@/utils/boolean';
|
||||
import { safe_jwt_decode } from './jwt-parser.service';
|
||||
import claimVue from './claim.vue';
|
||||
import valueVue from './value.vue';
|
||||
|
||||
const raw_jwt = ref(
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
|
||||
);
|
||||
const showParsedValues = ref(true);
|
||||
|
||||
const decodedJWT = computed(() => {
|
||||
return safe_jwt_decode(raw_jwt.value);
|
||||
});
|
||||
const validation = useValidation({
|
||||
source: raw_jwt,
|
||||
rules: [
|
||||
{
|
||||
validator: (value) => value.length > 0 && isNotThrowing(() => jwt_decode(value, { header: true })),
|
||||
message: 'Invalid JWT',
|
||||
},
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table-header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.claims {
|
||||
width: 20%;
|
||||
}
|
||||
</style>
|
||||
24
src/tools/jwt-parser/value.vue
Normal file
24
src/tools/jwt-parser/value.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<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 { parse_claim_value } from './jwt-parser.service';
|
||||
|
||||
const props = defineProps({
|
||||
claim: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const value = computed(() => parse_claim_value(props.claim, props.value));
|
||||
</script>
|
||||
Reference in New Issue
Block a user