From 94210b3ebc4f7e0c42d1fb5fd7b594f00433cae3 Mon Sep 17 00:00:00 2001 From: ClaireM <127452294+clairekinde11@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:12:12 +1000 Subject: [PATCH 01/12] New topic - Decode JWTs --- src/content/docs/build/tokens/decode-jwts.mdx | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 src/content/docs/build/tokens/decode-jwts.mdx diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx new file mode 100644 index 000000000..fae561834 --- /dev/null +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -0,0 +1,310 @@ +--- +page_id: 7a8b9c0d-1e2f-3456-7890-123456789abc +title: Decrypting JSON Web Tokens +description: Learn how to decrypt and decode JSON Web Tokens (JWTs) using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. +sidebar: + order: 9 +relatedArticles: + - 4ed081b0-7853-49be-b5fd-22a84a86bdad + - cf687bce-9732-4b67-9da5-580953c8549f + - d8069575-dfef-421d-8f3a-8f3efe9ad2f3 + - c58b1c76-349a-40a1-a539-3b1da3ccb239 +topics: + - tokens + - security + - jwt + - decryption +sdk: [] +languages: + - javascript + - typescript +audience: developers +complexity: intermediate +keywords: + - JWT decryption + - token decoding + - JWT validation + - token security + - JWT libraries + - token parsing +updated: 2024-01-15 +featured: false +deprecated: false +ai_summary: Comprehensive guide to decrypting and decoding JSON Web Tokens using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. +--- + +JWT decryption involves parsing and validating JSON Web Tokens to extract their payload information securely. While JWTs are typically signed (not encrypted), the term "decryption" in this context refers to the process of decoding and validating the token structure and claims. + +## Understanding JWT Structure + +Before decrypting a JWT, it's important to understand its structure. A JWT consists of three parts separated by dots (`.`): + +1. **Header** - Contains metadata about the token (algorithm, type) +2. **Payload** - Contains the claims (user data, permissions, etc.) +3. **Signature** - Used to verify the token's authenticity + +## Methods for Decrypting JWTs + +### Using Kinde JWT Decoder + +The [@kinde/jwt-decoder](https://github.com/kinde-oss/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens: + +#### Installation + +```bash +# npm +npm install @kinde/jwt-decoder + +# yarn +yarn add @kinde/jwt-decoder + +# pnpm +pnpm install @kinde/jwt-decoder +``` + +#### Basic Usage + +```javascript +import { jwtDecoder } from "@kinde/jwt-decoder"; + +// Simple decode +const decodedToken = jwtDecoder("eyJhbGc..."); + +console.log(decodedToken); +// Output: { header: {...}, payload: {...}, signature: "..." } +``` + +#### Type-Safe Decoding + +```typescript +import { jwtDecoder } from "@kinde/jwt-decoder"; + +// Decode with extended type +const decodedToken = jwtDecoder< + JWTDecoded & { + // Extra attributes here + custom_claim?: string; + feature_flags?: Record; + } +>("eyJhbGc..."); +``` + +### Using Kinde JWT Validator + +The [@kinde/jwt-validator](https://github.com/kinde-oss/jwt-validator) library provides comprehensive JWT validation with support for mobile and edge environments: + +#### Installation + +```bash +# npm +npm install @kinde/jwt-validator + +# yarn +yarn add @kinde/jwt-validator + +# pnpm +pnpm install @kinde/jwt-validator +``` + +#### Validation and Decoding + +```javascript +import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator"; + +const validationResult = await validateToken({ + token: "eyJhbGc...", + domain: "https://your-subdomain.kinde.com" +}); + +if (validationResult.isValid) { + console.log("Token is valid"); + console.log("Decoded payload:", validationResult.payload); +} else { + console.log("Token validation failed:", validationResult.error); +} +``` + +## Manual JWT Decoding + +If you need to decode JWTs without using Kinde's libraries, you can implement manual decoding: + +### Base64 URL Decoding + +```javascript +function decodeJWT(token) { + try { + // Split the token into its three parts + const parts = token.split('.'); + + if (parts.length !== 3) { + throw new Error('Invalid JWT format'); + } + + // Decode header and payload (base64url) + const header = JSON.parse(atob(parts[0].replace(/-/g, '+').replace(/_/g, '/'))); + const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'))); + + return { + header, + payload, + signature: parts[2] + }; + } catch (error) { + throw new Error('Failed to decode JWT: ' + error.message); + } +} + +// Usage +const token = "eyJhbGc..."; +const decoded = decodeJWT(token); +console.log(decoded.payload); +``` + +### TypeScript Implementation + +```typescript +interface JWTHeader { + alg: string; + typ: string; + kid?: string; +} + +interface JWTPayload { + iss: string; + sub: string; + aud: string | string[]; + exp: number; + iat: number; + jti?: string; + [key: string]: any; +} + +interface DecodedJWT { + header: JWTHeader; + payload: JWTPayload; + signature: string; +} + +function decodeJWT(token: string): DecodedJWT { + // Implementation same as above +} +``` + +## Security Considerations + +### Important Security Notes + +- **Decoding vs. Validation**: Decoding a JWT only extracts the payload - it doesn't verify the token's authenticity or integrity. +- **Always Validate**: After decoding, always validate the token using proper cryptographic verification. +- **Never Trust Client-Side Decoding**: Client-side decoding should only be used for display purposes, not for security decisions. +- **Check Expiration**: Always verify the `exp` claim to ensure the token hasn't expired. + +### Validation Checklist + +When decrypting JWTs, ensure you: + +- Verify the token signature using the public key +- Check the `iss` (issuer) claim matches your Kinde domain +- Validate the `aud` (audience) claim +- Verify the `exp` (expiration) claim +- Check the `iat` (issued at) claim is reasonable +- Validate any custom claims specific to your application + +## Common Use Cases + +### Displaying User Information + +```javascript +import { jwtDecoder } from "@kinde/jwt-decoder"; + +function displayUserInfo(token) { + try { + const decoded = jwtDecoder(token); + const { payload } = decoded; + + console.log(`User: ${payload.name || payload.email}`); + console.log(`Organization: ${payload.org_code}`); + console.log(`Permissions: ${payload.permissions?.join(', ')}`); + + return { + name: payload.name, + email: payload.email, + organization: payload.org_code, + permissions: payload.permissions || [] + }; + } catch (error) { + console.error('Failed to decode token:', error); + return null; + } +} +``` + +### Checking Feature Flags + +```javascript +function checkFeatureFlag(token, flagName) { + try { + const decoded = jwtDecoder(token); + const featureFlags = decoded.payload.feature_flags; + + if (featureFlags && featureFlags[flagName]) { + return featureFlags[flagName].v; // 'v' is the value + } + + return false; + } catch (error) { + console.error('Failed to check feature flag:', error); + return false; + } +} +``` + +### Extracting Permissions + +```javascript +function getUserPermissions(token) { + try { + const decoded = jwtDecoder(token); + return decoded.payload.permissions || []; + } catch (error) { + console.error('Failed to extract permissions:', error); + return []; + } +} +``` + +## Error Handling + +### Common Decoding Errors + +```javascript +function safeDecodeJWT(token) { + try { + if (!token) { + throw new Error('Token is required'); + } + + if (typeof token !== 'string') { + throw new Error('Token must be a string'); + } + + const parts = token.split('.'); + if (parts.length !== 3) { + throw new Error('Invalid JWT format - must have 3 parts'); + } + + return jwtDecoder(token); + } catch (error) { + console.error('JWT decoding error:', error.message); + return null; + } +} +``` + +## Best Practices + +- **Use Kinde Libraries**: Prefer Kinde's JWT libraries for production applications as they handle edge cases and provide type safety. +- **Validate Before Decoding**: Always validate the token's signature and claims before trusting the decoded payload. +- **Handle Errors Gracefully**: Implement proper error handling for malformed or invalid tokens. +- **Log Security Events**: Log failed decoding attempts for security monitoring. +- **Keep Libraries Updated**: Regularly update JWT libraries to get security patches and improvements. From 163d4e7c81aaf0beeb8ea846e9cbf058ff807498 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 18:33:09 +0600 Subject: [PATCH 02/12] replace the term 'decrypting' with'decoding' --- src/content/docs/build/tokens/decode-jwts.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index fae561834..65af98978 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -1,7 +1,7 @@ --- page_id: 7a8b9c0d-1e2f-3456-7890-123456789abc -title: Decrypting JSON Web Tokens -description: Learn how to decrypt and decode JSON Web Tokens (JWTs) using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. +title: Decoding JSON Web Tokens +description: Learn how to decode JSON Web Tokens (JWTs) using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. sidebar: order: 9 relatedArticles: @@ -13,7 +13,7 @@ topics: - tokens - security - jwt - - decryption + - decoding sdk: [] languages: - javascript @@ -21,7 +21,7 @@ languages: audience: developers complexity: intermediate keywords: - - JWT decryption + - JWT decoding - token decoding - JWT validation - token security @@ -30,20 +30,20 @@ keywords: updated: 2024-01-15 featured: false deprecated: false -ai_summary: Comprehensive guide to decrypting and decoding JSON Web Tokens using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. +ai_summary: Comprehensive guide to decoding JSON Web Tokens using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. --- -JWT decryption involves parsing and validating JSON Web Tokens to extract their payload information securely. While JWTs are typically signed (not encrypted), the term "decryption" in this context refers to the process of decoding and validating the token structure and claims. +JWT decoding involves parsing and validating JSON Web Tokens to extract their payload information securely. While JWTs are typically signed (not encrypted), decoding refers to the process of parsing the token structure and validating its claims. ## Understanding JWT Structure -Before decrypting a JWT, it's important to understand its structure. A JWT consists of three parts separated by dots (`.`): +Before decoding a JWT, it's important to understand its structure. A JWT consists of three parts separated by dots (`.`): 1. **Header** - Contains metadata about the token (algorithm, type) 2. **Payload** - Contains the claims (user data, permissions, etc.) 3. **Signature** - Used to verify the token's authenticity -## Methods for Decrypting JWTs +## Methods for Decoding JWTs ### Using Kinde JWT Decoder @@ -201,7 +201,7 @@ function decodeJWT(token: string): DecodedJWT { ### Validation Checklist -When decrypting JWTs, ensure you: +When decoding JWTs, ensure you: - Verify the token signature using the public key - Check the `iss` (issuer) claim matches your Kinde domain From 8f568c793736653a524f40b5510470cc6e6194f6 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 18:38:31 +0600 Subject: [PATCH 03/12] fix the hierarchy of headings --- src/content/docs/build/tokens/decode-jwts.mdx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index 65af98978..eebcee68b 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -43,13 +43,11 @@ Before decoding a JWT, it's important to understand its structure. A JWT consist 2. **Payload** - Contains the claims (user data, permissions, etc.) 3. **Signature** - Used to verify the token's authenticity -## Methods for Decoding JWTs - -### Using Kinde JWT Decoder +## Using Kinde JWT Decoder The [@kinde/jwt-decoder](https://github.com/kinde-oss/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens: -#### Installation +### Installation ```bash # npm @@ -62,7 +60,7 @@ yarn add @kinde/jwt-decoder pnpm install @kinde/jwt-decoder ``` -#### Basic Usage +### Basic Usage ```javascript import { jwtDecoder } from "@kinde/jwt-decoder"; @@ -74,7 +72,7 @@ console.log(decodedToken); // Output: { header: {...}, payload: {...}, signature: "..." } ``` -#### Type-Safe Decoding +### Type-Safe Decoding ```typescript import { jwtDecoder } from "@kinde/jwt-decoder"; @@ -89,11 +87,11 @@ const decodedToken = jwtDecoder< >("eyJhbGc..."); ``` -### Using Kinde JWT Validator +## Using Kinde JWT Validator The [@kinde/jwt-validator](https://github.com/kinde-oss/jwt-validator) library provides comprehensive JWT validation with support for mobile and edge environments: -#### Installation +### Installation ```bash # npm @@ -106,7 +104,7 @@ yarn add @kinde/jwt-validator pnpm install @kinde/jwt-validator ``` -#### Validation and Decoding +### Validation and Decoding ```javascript import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator"; From 09a50d7326be72ba3b63caa4df8a7e99db1aca0f Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 18:49:08 +0600 Subject: [PATCH 04/12] update the manual decoder functions --- src/content/docs/build/tokens/decode-jwts.mdx | 119 +++++++++++++++++- 1 file changed, 114 insertions(+), 5 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index eebcee68b..e6ac95585 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -35,8 +35,6 @@ ai_summary: Comprehensive guide to decoding JSON Web Tokens using Kinde's JWT li JWT decoding involves parsing and validating JSON Web Tokens to extract their payload information securely. While JWTs are typically signed (not encrypted), decoding refers to the process of parsing the token structure and validating its claims. -## Understanding JWT Structure - Before decoding a JWT, it's important to understand its structure. A JWT consists of three parts separated by dots (`.`): 1. **Header** - Contains metadata about the token (algorithm, type) @@ -128,7 +126,23 @@ If you need to decode JWTs without using Kinde's libraries, you can implement ma ### Base64 URL Decoding +#### Browser/Web Platform + +This implementation uses `atob()`, which is available in browsers, web workers, and service workers: + ```javascript +function base64UrlDecode(str) { + // Replace Base64URL characters with Base64 characters + let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + + // Add padding if needed (Base64 strings must be multiples of 4) + while (base64.length % 4) { + base64 += '='; + } + + return atob(base64); +} + function decodeJWT(token) { try { // Split the token into its three parts @@ -139,8 +153,8 @@ function decodeJWT(token) { } // Decode header and payload (base64url) - const header = JSON.parse(atob(parts[0].replace(/-/g, '+').replace(/_/g, '/'))); - const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'))); + const header = JSON.parse(base64UrlDecode(parts[0])); + const payload = JSON.parse(base64UrlDecode(parts[1])); return { header, @@ -158,6 +172,70 @@ const decoded = decodeJWT(token); console.log(decoded.payload); ``` +#### Node.js Platform + +For Node.js environments, use `Buffer` instead of `atob()`: + +```javascript +function base64UrlDecode(str) { + // Replace Base64URL characters with Base64 characters + let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + + // Add padding if needed (Base64 strings must be multiples of 4) + while (base64.length % 4) { + base64 += '='; + } + + return Buffer.from(base64, 'base64').toString('utf-8'); +} + +function decodeJWT(token) { + try { + // Split the token into its three parts + const parts = token.split('.'); + + if (parts.length !== 3) { + throw new Error('Invalid JWT format'); + } + + // Decode header and payload (base64url) + const header = JSON.parse(base64UrlDecode(parts[0])); + const payload = JSON.parse(base64UrlDecode(parts[1])); + + return { + header, + payload, + signature: parts[2] + }; + } catch (error) { + throw new Error('Failed to decode JWT: ' + error.message); + } +} +``` + +#### Universal/Cross-Platform + +For code that works in both browser and Node.js environments: + +```javascript +function base64UrlDecode(str) { + // Replace Base64URL characters with Base64 characters + let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + + // Add padding if needed (Base64 strings must be multiples of 4) + while (base64.length % 4) { + base64 += '='; + } + + // Use Buffer in Node.js, atob in browser + if (typeof Buffer !== 'undefined') { + return Buffer.from(base64, 'base64').toString('utf-8'); + } else { + return atob(base64); + } +} +``` + ### TypeScript Implementation ```typescript @@ -183,8 +261,39 @@ interface DecodedJWT { signature: string; } +function base64UrlDecode(str: string): string { + // Replace Base64URL characters with Base64 characters + let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + + // Add padding if needed (Base64 strings must be multiples of 4) + while (base64.length % 4) { + base64 += '='; + } + + return atob(base64); +} + function decodeJWT(token: string): DecodedJWT { - // Implementation same as above + try { + // Split the token into its three parts + const parts = token.split('.'); + + if (parts.length !== 3) { + throw new Error('Invalid JWT format'); + } + + // Decode header and payload (base64url) + const header = JSON.parse(base64UrlDecode(parts[0])) as JWTHeader; + const payload = JSON.parse(base64UrlDecode(parts[1])) as JWTPayload; + + return { + header, + payload, + signature: parts[2] + }; + } catch (error) { + throw new Error('Failed to decode JWT: ' + (error instanceof Error ? error.message : String(error))); + } } ``` From 9ff2fe20e106d8316d4e6d25c372d6849bacfc2e Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 18:54:19 +0600 Subject: [PATCH 05/12] update the headings --- src/content/docs/build/tokens/decode-jwts.mdx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index e6ac95585..6755dffb7 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -124,9 +124,7 @@ if (validationResult.isValid) { If you need to decode JWTs without using Kinde's libraries, you can implement manual decoding: -### Base64 URL Decoding - -#### Browser/Web Platform +### Browser/Web Platform This implementation uses `atob()`, which is available in browsers, web workers, and service workers: @@ -172,7 +170,7 @@ const decoded = decodeJWT(token); console.log(decoded.payload); ``` -#### Node.js Platform +### Node.js Platform For Node.js environments, use `Buffer` instead of `atob()`: @@ -213,7 +211,7 @@ function decodeJWT(token) { } ``` -#### Universal/Cross-Platform +### Universal/Cross-Platform For code that works in both browser and Node.js environments: From c803483ce8cc2f6378e74ae2c29e15171feb03d6 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 19:55:14 +0600 Subject: [PATCH 06/12] update the code example for typescript --- src/content/docs/build/tokens/decode-jwts.mdx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index 6755dffb7..ab2cef3da 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -268,7 +268,14 @@ function base64UrlDecode(str: string): string { base64 += '='; } - return atob(base64); + // Use Buffer in Node.js, atob in browser + if (typeof globalThis.Buffer !== 'undefined') { + return globalThis.Buffer.from(base64, 'base64').toString('binary'); + } else if (typeof globalThis.atob !== 'undefined') { + return globalThis.atob(base64); + } else { + throw new Error('Neither Buffer nor atob is available in this environment'); + } } function decodeJWT(token: string): DecodedJWT { From 7892c55be71eb29de11faa600eb0248842660437 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Thu, 27 Nov 2025 20:00:14 +0600 Subject: [PATCH 07/12] remove semicolon in js, ts code examples --- src/content/docs/build/tokens/decode-jwts.mdx | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index ab2cef3da..c0dae1aad 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -61,12 +61,12 @@ pnpm install @kinde/jwt-decoder ### Basic Usage ```javascript -import { jwtDecoder } from "@kinde/jwt-decoder"; +import { jwtDecoder } from "@kinde/jwt-decoder" // Simple decode -const decodedToken = jwtDecoder("eyJhbGc..."); +const decodedToken = jwtDecoder("eyJhbGc...") -console.log(decodedToken); +console.log(decodedToken) // Output: { header: {...}, payload: {...}, signature: "..." } ``` @@ -105,18 +105,18 @@ pnpm install @kinde/jwt-validator ### Validation and Decoding ```javascript -import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator"; +import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator" const validationResult = await validateToken({ token: "eyJhbGc...", domain: "https://your-subdomain.kinde.com" -}); +}) if (validationResult.isValid) { - console.log("Token is valid"); - console.log("Decoded payload:", validationResult.payload); + console.log("Token is valid") + console.log("Decoded payload:", validationResult.payload) } else { - console.log("Token validation failed:", validationResult.error); + console.log("Token validation failed:", validationResult.error) } ``` @@ -131,43 +131,43 @@ This implementation uses `atob()`, which is available in browsers, web workers, ```javascript function base64UrlDecode(str) { // Replace Base64URL characters with Base64 characters - let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + let base64 = str.replace(/-/g, '+').replace(/_/g, '/') // Add padding if needed (Base64 strings must be multiples of 4) while (base64.length % 4) { - base64 += '='; + base64 += '=' } - return atob(base64); + return atob(base64) } function decodeJWT(token) { try { // Split the token into its three parts - const parts = token.split('.'); + const parts = token.split('.') if (parts.length !== 3) { - throw new Error('Invalid JWT format'); + throw new Error('Invalid JWT format') } // Decode header and payload (base64url) - const header = JSON.parse(base64UrlDecode(parts[0])); - const payload = JSON.parse(base64UrlDecode(parts[1])); + const header = JSON.parse(base64UrlDecode(parts[0])) + const payload = JSON.parse(base64UrlDecode(parts[1])) return { header, payload, signature: parts[2] - }; + } } catch (error) { - throw new Error('Failed to decode JWT: ' + error.message); + throw new Error('Failed to decode JWT: ' + error.message) } } // Usage -const token = "eyJhbGc..."; -const decoded = decodeJWT(token); -console.log(decoded.payload); +const token = "eyJhbGc..." +const decoded = decodeJWT(token) +console.log(decoded.payload) ``` ### Node.js Platform @@ -177,36 +177,36 @@ For Node.js environments, use `Buffer` instead of `atob()`: ```javascript function base64UrlDecode(str) { // Replace Base64URL characters with Base64 characters - let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + let base64 = str.replace(/-/g, '+').replace(/_/g, '/') // Add padding if needed (Base64 strings must be multiples of 4) while (base64.length % 4) { - base64 += '='; + base64 += '=' } - return Buffer.from(base64, 'base64').toString('utf-8'); + return Buffer.from(base64, 'base64').toString('utf-8') } function decodeJWT(token) { try { // Split the token into its three parts - const parts = token.split('.'); + const parts = token.split('.') if (parts.length !== 3) { - throw new Error('Invalid JWT format'); + throw new Error('Invalid JWT format') } // Decode header and payload (base64url) - const header = JSON.parse(base64UrlDecode(parts[0])); - const payload = JSON.parse(base64UrlDecode(parts[1])); + const header = JSON.parse(base64UrlDecode(parts[0])) + const payload = JSON.parse(base64UrlDecode(parts[1])) return { header, payload, signature: parts[2] - }; + } } catch (error) { - throw new Error('Failed to decode JWT: ' + error.message); + throw new Error('Failed to decode JWT: ' + error.message) } } ``` @@ -218,18 +218,18 @@ For code that works in both browser and Node.js environments: ```javascript function base64UrlDecode(str) { // Replace Base64URL characters with Base64 characters - let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + let base64 = str.replace(/-/g, '+').replace(/_/g, '/') // Add padding if needed (Base64 strings must be multiples of 4) while (base64.length % 4) { - base64 += '='; + base64 += '=' } // Use Buffer in Node.js, atob in browser if (typeof Buffer !== 'undefined') { - return Buffer.from(base64, 'base64').toString('utf-8'); + return Buffer.from(base64, 'base64').toString('utf-8') } else { - return atob(base64); + return atob(base64) } } ``` @@ -261,43 +261,43 @@ interface DecodedJWT { function base64UrlDecode(str: string): string { // Replace Base64URL characters with Base64 characters - let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); + let base64 = str.replace(/-/g, '+').replace(/_/g, '/') // Add padding if needed (Base64 strings must be multiples of 4) while (base64.length % 4) { - base64 += '='; + base64 += '=' } // Use Buffer in Node.js, atob in browser if (typeof globalThis.Buffer !== 'undefined') { - return globalThis.Buffer.from(base64, 'base64').toString('binary'); + return globalThis.Buffer.from(base64, 'base64').toString('binary') } else if (typeof globalThis.atob !== 'undefined') { - return globalThis.atob(base64); + return globalThis.atob(base64) } else { - throw new Error('Neither Buffer nor atob is available in this environment'); + throw new Error('Neither Buffer nor atob is available in this environment') } } function decodeJWT(token: string): DecodedJWT { try { // Split the token into its three parts - const parts = token.split('.'); + const parts = token.split('.') if (parts.length !== 3) { - throw new Error('Invalid JWT format'); + throw new Error('Invalid JWT format') } // Decode header and payload (base64url) - const header = JSON.parse(base64UrlDecode(parts[0])) as JWTHeader; - const payload = JSON.parse(base64UrlDecode(parts[1])) as JWTPayload; + const header = JSON.parse(base64UrlDecode(parts[0])) as JWTHeader + const payload = JSON.parse(base64UrlDecode(parts[1])) as JWTPayload return { header, payload, signature: parts[2] - }; + } } catch (error) { - throw new Error('Failed to decode JWT: ' + (error instanceof Error ? error.message : String(error))); + throw new Error('Failed to decode JWT: ' + (error instanceof Error ? error.message : String(error))) } } ``` @@ -327,26 +327,26 @@ When decoding JWTs, ensure you: ### Displaying User Information ```javascript -import { jwtDecoder } from "@kinde/jwt-decoder"; +import { jwtDecoder } from "@kinde/jwt-decoder" function displayUserInfo(token) { try { - const decoded = jwtDecoder(token); - const { payload } = decoded; + const decoded = jwtDecoder(token) + const { payload } = decoded - console.log(`User: ${payload.name || payload.email}`); - console.log(`Organization: ${payload.org_code}`); - console.log(`Permissions: ${payload.permissions?.join(', ')}`); + console.log(`User: ${payload.name || payload.email}`) + console.log(`Organization: ${payload.org_code}`) + console.log(`Permissions: ${payload.permissions?.join(', ')}`) return { name: payload.name, email: payload.email, organization: payload.org_code, permissions: payload.permissions || [] - }; + } } catch (error) { - console.error('Failed to decode token:', error); - return null; + console.error('Failed to decode token:', error) + return null } } ``` @@ -356,17 +356,17 @@ function displayUserInfo(token) { ```javascript function checkFeatureFlag(token, flagName) { try { - const decoded = jwtDecoder(token); - const featureFlags = decoded.payload.feature_flags; + const decoded = jwtDecoder(token) + const featureFlags = decoded.payload.feature_flags if (featureFlags && featureFlags[flagName]) { - return featureFlags[flagName].v; // 'v' is the value + return featureFlags[flagName].v // 'v' is the value } - return false; + return false } catch (error) { - console.error('Failed to check feature flag:', error); - return false; + console.error('Failed to check feature flag:', error) + return false } } ``` @@ -376,11 +376,11 @@ function checkFeatureFlag(token, flagName) { ```javascript function getUserPermissions(token) { try { - const decoded = jwtDecoder(token); - return decoded.payload.permissions || []; + const decoded = jwtDecoder(token) + return decoded.payload.permissions || [] } catch (error) { - console.error('Failed to extract permissions:', error); - return []; + console.error('Failed to extract permissions:', error) + return [] } } ``` @@ -393,22 +393,22 @@ function getUserPermissions(token) { function safeDecodeJWT(token) { try { if (!token) { - throw new Error('Token is required'); + throw new Error('Token is required') } if (typeof token !== 'string') { - throw new Error('Token must be a string'); + throw new Error('Token must be a string') } - const parts = token.split('.'); + const parts = token.split('.') if (parts.length !== 3) { - throw new Error('Invalid JWT format - must have 3 parts'); + throw new Error('Invalid JWT format - must have 3 parts') } - return jwtDecoder(token); + return jwtDecoder(token) } catch (error) { - console.error('JWT decoding error:', error.message); - return null; + console.error('JWT decoding error:', error.message) + return null } } ``` From 954973ca697f7c2755003dc30df1211da4cd67f0 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Fri, 28 Nov 2025 16:47:02 +0600 Subject: [PATCH 08/12] update the code samples for accuracy --- src/content/docs/build/tokens/decode-jwts.mdx | 91 ++++++++++++------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index c0dae1aad..1996c7950 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -63,11 +63,14 @@ pnpm install @kinde/jwt-decoder ```javascript import { jwtDecoder } from "@kinde/jwt-decoder" +const token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" + // Simple decode -const decodedToken = jwtDecoder("eyJhbGc...") +const decodedToken = jwtDecoder(token) console.log(decodedToken) -// Output: { header: {...}, payload: {...}, signature: "..." } +// Output: { sub: '1234567890', name: 'John Doe', admin: true, iat: 1516239022 } ``` ### Type-Safe Decoding @@ -105,19 +108,35 @@ pnpm install @kinde/jwt-validator ### Validation and Decoding ```javascript -import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator" - -const validationResult = await validateToken({ - token: "eyJhbGc...", - domain: "https://your-subdomain.kinde.com" -}) - -if (validationResult.isValid) { - console.log("Token is valid") - console.log("Decoded payload:", validationResult.payload) -} else { - console.log("Token validation failed:", validationResult.error) +import { validateToken } from "@kinde/jwt-validator" +import { jwtDecoder } from "@kinde/jwt-decoder" + +const token = "ey..." // your JWT here + +const validateAndDecode = async () => { + try { + // Validate the token + const result = await validateToken({ + token, + domain: "https://your-subdomain.kinde.com", + }) + + if (result.valid) { + console.log("Token is valid") + + // Decode after validation + const decoded = jwtDecoder(token) + console.log("Decoded payload:", decoded) + } else { + console.log("Token validation failed:", result.message) + } + } catch (error) { + // The validator throws for JWKS or validation errors + console.error("Token is invalid:", error) + } } + +validateAndDecode() ``` ## Manual JWT Decoding @@ -331,13 +350,12 @@ import { jwtDecoder } from "@kinde/jwt-decoder" function displayUserInfo(token) { try { - const decoded = jwtDecoder(token) - const { payload } = decoded - - console.log(`User: ${payload.name || payload.email}`) - console.log(`Organization: ${payload.org_code}`) - console.log(`Permissions: ${payload.permissions?.join(', ')}`) - + const payload = jwtDecoder(token); + + console.log(`User: ${payload.name || payload.email}`); + console.log(`Organization: ${payload.org_code}`); + console.log(`Permissions: ${payload.permissions?.join(', ')}`); + return { name: payload.name, email: payload.email, @@ -345,8 +363,8 @@ function displayUserInfo(token) { permissions: payload.permissions || [] } } catch (error) { - console.error('Failed to decode token:', error) - return null + console.error('Failed to decode token:', error); + return null; } } ``` @@ -354,19 +372,22 @@ function displayUserInfo(token) { ### Checking Feature Flags ```javascript +import { jwtDecoder } from "@kinde/jwt-decoder" + function checkFeatureFlag(token, flagName) { try { - const decoded = jwtDecoder(token) - const featureFlags = decoded.payload.feature_flags - + const payload = jwtDecoder(token); + + const featureFlags = payload.feature_flags; + if (featureFlags && featureFlags[flagName]) { - return featureFlags[flagName].v // 'v' is the value + return featureFlags[flagName].v; } - - return false + + return false; } catch (error) { - console.error('Failed to check feature flag:', error) - return false + console.error('Failed to check feature flag:', error); + return false; } } ``` @@ -374,10 +395,12 @@ function checkFeatureFlag(token, flagName) { ### Extracting Permissions ```javascript -function getUserPermissions(token) { +import { jwtDecoder } from "@kinde/jwt-decoder" + +export function getUserPermissions(token) { try { - const decoded = jwtDecoder(token) - return decoded.payload.permissions || [] + const payload = jwtDecoder(token) + return payload.permissions || [] } catch (error) { console.error('Failed to extract permissions:', error) return [] From f7277c9da6656b395826cd4836b6ab8be0a31926 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Fri, 28 Nov 2025 18:15:51 +0600 Subject: [PATCH 09/12] improve the code example and explanations --- src/content/docs/build/tokens/decode-jwts.mdx | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index 1996c7950..c8006c847 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -76,16 +76,15 @@ console.log(decodedToken) ### Type-Safe Decoding ```typescript -import { jwtDecoder } from "@kinde/jwt-decoder"; +import { jwtDecoder, type JWTDecoded } from "@kinde/jwt-decoder" // Decode with extended type -const decodedToken = jwtDecoder< - JWTDecoded & { - // Extra attributes here - custom_claim?: string; - feature_flags?: Record; - } ->("eyJhbGc..."); +type CustomJWT = JWTDecoded & { + custom_claim?: string + feature_flags?: Record +} + +const decodedToken = jwtDecoder("ey...") ``` ## Using Kinde JWT Validator @@ -345,26 +344,36 @@ When decoding JWTs, ensure you: ### Displaying User Information +You can extract user information from decoded tokens, including email, organization code, feature flags, and permissions. + +By default, the email claim is not included in the `access_token`. To enable it: + +1. Go to **Application** > **View Details** > **Tokens** > **Access Token** and select **Customize**. +2. Enable the **Email (string)** claim. +3. Select **Save**. + +If you need to access the user's full name and profile picture, use the `id_token` instead of the access token. The `id_token` includes these claims by default. You can decode the `id_token` using the same method as the access token. Learn more about [ID tokens](/build/tokens/about-id-tokens/). + ```javascript import { jwtDecoder } from "@kinde/jwt-decoder" function displayUserInfo(token) { try { - const payload = jwtDecoder(token); + const payload = jwtDecoder(token) - console.log(`User: ${payload.name || payload.email}`); - console.log(`Organization: ${payload.org_code}`); - console.log(`Permissions: ${payload.permissions?.join(', ')}`); + // Note: Email must be enabled in token customization for access tokens + console.log(`User: ${payload.email}`) + console.log(`Organization code: ${payload.org_code}`) + console.log(`Permissions: ${payload.permissions?.join(", ")}`) return { - name: payload.name, - email: payload.email, - organization: payload.org_code, - permissions: payload.permissions || [] + email: payload?.email || "", + org_code: payload.org_code, + permissions: payload.permissions || [], } } catch (error) { - console.error('Failed to decode token:', error); - return null; + console.error("Failed to decode token:", error) + return null } } ``` From 18a3be0a5b6334abc33e6c18900f79b23576751f Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Fri, 28 Nov 2025 18:49:11 +0600 Subject: [PATCH 10/12] update the ordering of the page --- src/content/docs/build/tokens/decode-jwts.mdx | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index c8006c847..46d1fb8f4 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -43,7 +43,7 @@ Before decoding a JWT, it's important to understand its structure. A JWT consist ## Using Kinde JWT Decoder -The [@kinde/jwt-decoder](https://github.com/kinde-oss/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens: +The [@kinde/jwt-decoder](https://www.npmjs.com/package/@kinde/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens: ### Installation @@ -89,7 +89,7 @@ const decodedToken = jwtDecoder("ey...") ## Using Kinde JWT Validator -The [@kinde/jwt-validator](https://github.com/kinde-oss/jwt-validator) library provides comprehensive JWT validation with support for mobile and edge environments: +The [@kinde/jwt-validator](https://www.npmjs.com/package/@kinde/jwt-validator) library provides comprehensive JWT validation with support for mobile and edge environments: ### Installation @@ -320,26 +320,6 @@ function decodeJWT(token: string): DecodedJWT { } ``` -## Security Considerations - -### Important Security Notes - -- **Decoding vs. Validation**: Decoding a JWT only extracts the payload - it doesn't verify the token's authenticity or integrity. -- **Always Validate**: After decoding, always validate the token using proper cryptographic verification. -- **Never Trust Client-Side Decoding**: Client-side decoding should only be used for display purposes, not for security decisions. -- **Check Expiration**: Always verify the `exp` claim to ensure the token hasn't expired. - -### Validation Checklist - -When decoding JWTs, ensure you: - -- Verify the token signature using the public key -- Check the `iss` (issuer) claim matches your Kinde domain -- Validate the `aud` (audience) claim -- Verify the `exp` (expiration) claim -- Check the `iat` (issued at) claim is reasonable -- Validate any custom claims specific to your application - ## Common Use Cases ### Displaying User Information @@ -417,6 +397,26 @@ export function getUserPermissions(token) { } ``` +## Security Considerations + +### Important Security Notes + +- **Decoding vs. Validation**: Decoding a JWT only extracts the payload - it doesn't verify the token's authenticity or integrity. +- **Always Validate**: After decoding, always validate the token using proper cryptographic verification. +- **Never Trust Client-Side Decoding**: Client-side decoding should only be used for display purposes, not for security decisions. +- **Check Expiration**: Always verify the `exp` claim to ensure the token hasn't expired. + +### Validation Checklist + +When decoding JWTs, ensure you: + +- Verify the token signature using the public key +- Check the `iss` (issuer) claim matches your Kinde domain +- Validate the `aud` (audience) claim +- Verify the `exp` (expiration) claim +- Check the `iat` (issued at) claim is reasonable +- Validate any custom claims specific to your application + ## Error Handling ### Common Decoding Errors @@ -452,3 +452,10 @@ function safeDecodeJWT(token) { - **Handle Errors Gracefully**: Implement proper error handling for malformed or invalid tokens. - **Log Security Events**: Log failed decoding attempts for security monitoring. - **Keep Libraries Updated**: Regularly update JWT libraries to get security patches and improvements. + +## Resources + +- [Kinde Online JWT Decoder](https://kinde.com/tools/online-jwt-decoder/) +- [Kinde JWT Decoder Library](https://www.npmjs.com/package/@kinde/jwt-decoder) +- [Kinde JWT Validator Library](https://www.npmjs.com/package/@kinde/jwt-validator) + From a976789d96ba80503711f12a0c380a3bee3ea446 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Fri, 28 Nov 2025 19:02:40 +0600 Subject: [PATCH 11/12] update copy with additional info --- src/content/docs/build/tokens/decode-jwts.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index 46d1fb8f4..34aec541c 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -43,7 +43,9 @@ Before decoding a JWT, it's important to understand its structure. A JWT consist ## Using Kinde JWT Decoder -The [@kinde/jwt-decoder](https://www.npmjs.com/package/@kinde/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens: +The [@kinde/jwt-decoder](https://www.npmjs.com/package/@kinde/jwt-decoder) library provides a simple, type-safe way to decode JWT tokens. + +The decoder extracts and returns the decoded payload containing the token's claims. See an [example access token payload](/build/tokens/about-access-tokens/#example-access-token). ### Installation @@ -89,7 +91,7 @@ const decodedToken = jwtDecoder("ey...") ## Using Kinde JWT Validator -The [@kinde/jwt-validator](https://www.npmjs.com/package/@kinde/jwt-validator) library provides comprehensive JWT validation with support for mobile and edge environments: +The [@kinde/jwt-validator](https://www.npmjs.com/package/@kinde/jwt-validator) library provides cryptographic JWT validation with support for mobile and edge environments. The `validateToken` method verifies the token's signature and returns an object with a `valid` property indicating whether the token is valid. ### Installation From 7d070333cf80a478563d9139bed03c3a5b119d15 Mon Sep 17 00:00:00 2001 From: Tamal Anwar Chowdhury Date: Mon, 15 Dec 2025 19:21:10 +0600 Subject: [PATCH 12/12] updated the copy about non standard claims --- src/content/docs/build/tokens/decode-jwts.mdx | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/content/docs/build/tokens/decode-jwts.mdx b/src/content/docs/build/tokens/decode-jwts.mdx index 34aec541c..b36d86b81 100644 --- a/src/content/docs/build/tokens/decode-jwts.mdx +++ b/src/content/docs/build/tokens/decode-jwts.mdx @@ -27,7 +27,7 @@ keywords: - token security - JWT libraries - token parsing -updated: 2024-01-15 +updated: 2025-12-15 featured: false deprecated: false ai_summary: Comprehensive guide to decoding JSON Web Tokens using Kinde's JWT libraries, including validation methods, security considerations, and practical implementation examples. @@ -290,7 +290,7 @@ function base64UrlDecode(str: string): string { // Use Buffer in Node.js, atob in browser if (typeof globalThis.Buffer !== 'undefined') { - return globalThis.Buffer.from(base64, 'base64').toString('binary') + return globalThis.Buffer.from(base64, 'base64').toString('utf-8') } else if (typeof globalThis.atob !== 'undefined') { return globalThis.atob(base64) } else { @@ -360,6 +360,15 @@ function displayUserInfo(token) { } ``` +**About `org_code` and `permissions` claims** + +The `org_code` and `permissions` claims are **not included by default** in access tokens: + +- **`org_code`**: Only included when the user belongs to and signs in to an organization. If a user belongs to no organizations, this claim will be omitted. +- **`permissions`**: Only included when the user signs in to an organization AND has permissions assigned to them or their role. If no permissions are configured or assigned, this claim will be omitted. + +Always check if these claims exist before accessing them (as shown in the example above using optional chaining or default values). + ### Checking Feature Flags ```javascript @@ -383,6 +392,17 @@ function checkFeatureFlag(token, flagName) { } ``` +**About `feature_flags` claim** + +The `feature_flags` claim is **not included by default** in access tokens. It's only included when: + +- Feature flags are configured in your Kinde environment and assigned to the user or organization +- For Machine-to-Machine (M2M) applications, only flags explicitly enabled in the app's token configuration are included + +If no feature flags are configured or assigned, this claim will be omitted. Always check if the claim exists before accessing it (as shown in the example above). + +Feature flags use a compact format with short keys: `t` for type and `v` for value. + ### Extracting Permissions ```javascript @@ -399,6 +419,14 @@ export function getUserPermissions(token) { } ``` +**About `permissions` claim** + +The `permissions` claim is **not included by default** in access tokens. It's only included when: + +- The user signs in to an organization AND has permissions assigned to them or their role + +If no permissions are configured or assigned, this claim will be omitted. Always check if the claim exists before accessing it (as shown in the example above using `|| []` as a fallback). + ## Security Considerations ### Important Security Notes