mirror of
				https://github.com/CorentinTh/it-tools.git
				synced 2025-10-31 03:53:52 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			next
			...
			feat/jwt-p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 98b226de42 | ||
|  | 9f2874b23d | ||
|  | b0d9fbbbaf | ||
|  | 7c100f4030 | ||
|  | 8d0a4273fb | ||
|  | bb0eca80a7 | ||
|  | 3e4a64551f | 
							
								
								
									
										17251
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										17251
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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,6 +53,7 @@ import { | ||||
|   NTooltip, | ||||
|   NUpload, | ||||
|   NUploadDragger, | ||||
|   NPopover, | ||||
| } from 'naive-ui'; | ||||
|  | ||||
| const components = [ | ||||
| @@ -109,6 +110,7 @@ const components = [ | ||||
|   NIcon, | ||||
|   NSwitch, | ||||
|   NCollapseTransition, | ||||
|   NPopover, | ||||
| ]; | ||||
|  | ||||
| export const naive = create({ components }); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { LockOpen } from '@vicons/tabler'; | ||||
| import type { ToolCategory } from './tool'; | ||||
|  | ||||
| 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'; | ||||
| @@ -67,6 +68,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       metaTagGenerator, | ||||
|       otpCodeGeneratorAndValidator, | ||||
|       mimeTypes, | ||||
|       jwtParser, | ||||
|     ], | ||||
|   }, | ||||
|   { | ||||
|   | ||||
							
								
								
									
										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