-
Notifications
You must be signed in to change notification settings - Fork 48
feat: Guide - Decoding JSON Web Tokens #579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new MDX documentation page at Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (8)
src/content/docs/build/tokens/decode-jwts.mdx (8)
30-30: Update the “updated” date to this PR’s timeline.Current: 2024-01-15. Suggest 2025-09-29 for accuracy.
-updated: 2024-01-15 +updated: 2025-09-29Please confirm the doc site’s date policy (authored vs updated).
114-125: Show explicit expected algorithms and domain/issuer guidance.Add recommended options and a note to pin expected algs (e.g., RS256) and issuer.
-const validationResult = await validateToken({ - token: "eyJhbGc...", - domain: "https://your-subdomain.kinde.com" -}); +const validationResult = await validateToken({ + token: "eyJhbGc...", + domain: "https://your-subdomain.kinde.com", + // Optional but recommended hardening: + expectedAlgs: ["RS256"], + expectedIssuer: "https://your-subdomain.kinde.com", +});I can add a short “why pin algorithms” callout if helpful.
166-191: Provide a complete TS implementation rather than a placeholder.Avoid “Implementation same as above”; include the base64url helper and typed return.
-interface DecodedJWT { +interface DecodedJWT { header: JWTHeader; payload: JWTPayload; signature: string; } -function decodeJWT(token: string): DecodedJWT { - // Implementation same as above -} +const b64urlToString = (b64url: string): string => { + const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/'); + const padded = b64 + '==='.slice((b64.length + 3) % 4); + // Browser vs Node + return typeof atob === 'function' + ? decodeURIComponent(escape(atob(padded))) + : Buffer.from(padded, 'base64').toString('utf8'); +}; + +export function decodeJWT(token: string): DecodedJWT { + const parts = token.split('.'); + if (parts.length !== 3) throw new Error('Invalid JWT format'); + const header = JSON.parse(b64urlToString(parts[0])) as JWTHeader; + const payload = JSON.parse(b64urlToString(parts[1])) as JWTPayload; + return { header, payload, signature: parts[2] }; +}
197-201: Tighten the security bullets with explicit “don’t trust until validated.”Clarify sequencing to prevent misinterpretation.
-**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. +**Decoding vs. Validation**: Decoding only parses claims; it does not prove authenticity/integrity. +**Always Validate**: Perform cryptographic validation (signature + claims) and only then trust any decoded data.
204-212: Expand the validation checklist (alg, kid/JWKS, nbf/leeway, issuer, audience).Add commonly missed checks and adjust terminology.
-When validating JWTs, ensure you: +When validating 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 +- Enforce expected `alg` (e.g., RS256) and reject `none` or unexpected algorithms +- Use `kid` to select the correct key from JWKS and cache JWKS with rotation in mind +- Check `nbf` (not before) and allow small clock skew (e.g., ±60s leeway) +- For OIDC ID tokens, also verify `nonce` when applicableI can add a short JWKS caching callout if you want.
245-260: Harden feature‑flag access with nullish chaining and typing.Avoid runtime errors when claims are absent.
-function checkFeatureFlag(token, flagName) { +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; + const v = decoded?.payload?.feature_flags?.[flagName]?.v; + return v ?? false; } catch (error) { console.error('Failed to check feature flag:', error); return false; } }
306-308: Rephrase “Validate Before Decoding.”You must parse to validate; intent is “don’t trust decoded contents until validated.”
-**Validate Before Decoding**: Always validate the token's signature and claims before trusting the decoded payload. +**Validate before trusting**: Parse, validate signature and claims, then trust the decoded payload.
68-75: Add explicitJWTDecodedimport and tighten the decoded payload exampleImport the exported
JWTDecodedtype and pass a concrete payload shape tojwtDecoder:-import { jwtDecoder } from "@kinde/jwt-decoder"; +import { jwtDecoder, type JWTDecoded } from "@kinde/jwt-decoder";Define a custom payload type and apply it:
-const decodedToken = jwtDecoder("eyJhbGc..."); +type MyClaims = JWTDecoded["payload"] & { + custom_claim?: string; + feature_flags?: Record<string, { v: unknown }>; +}; + +const decodedToken = jwtDecoder<MyClaims>("eyJhbGc...");Apply the same pattern in the example at lines 79–90.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🔇 Additional comments (2)
src/content/docs/build/tokens/decode-jwts.mdx (2)
5-11: Inspect front-matter in decode-jwts.mdx
Please confirm presence and naming ofsdkorsdkskey in the front-matter once we have the file’s first lines.
112-112: Remove unused type import
jwtValidationResponseisn’t referenced in this file; drop it to avoid confusion.-import { validateToken, type jwtValidationResponse } from "@kinde/jwt-validator"; +import { validateToken } from "@kinde/jwt-validator";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/content/docs/build/tokens/decode-jwts.mdx (1)
304-304: Minor style: Replace "only" with more varied language.To improve readability and flow, consider rephrasing "should only be used for display purposes" with an alternative structure.
Apply this diff for improved style:
-- **Never Trust Client-Side Decoding**: Client-side decoding should only be used for display purposes, not for security decisions. +- **Never Trust Client-Side Decoding**: Client-side decoding is intended for display purposes only and must not be used for security decisions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🧰 Additional context used
🪛 LanguageTool
src/content/docs/build/tokens/decode-jwts.mdx
[style] ~304-~304: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...Decoding**: Client-side decoding should only be used for display purposes, not for s...
(ONLY_EXCLUSIVELY_STYLE)
🔇 Additional comments (2)
src/content/docs/build/tokens/decode-jwts.mdx (2)
3-4: ✓ Terminology corrected from previous review feedback.Title and keywords now properly use "Decoding and validating" terminology instead of "Decrypting," avoiding confusion between JWS (signed) and JWE (encrypted) tokens. This aligns with the previous review guidance.
Also applies to: 16-16, 24-24, 33-33
1-415: Documentation comprehensively covers JWT decoding patterns with strong security guidance.The document provides:
- Clear introduction distinguishing decoding from validation
- Two production-ready Kinde library options (Decoder & Validator) with installation and usage examples
- Three manual decoding implementations (browser, Node.js, universal) with proper error handling
- TypeScript types and interfaces for type-safe implementations
- Practical use cases (user info, feature flags, permissions extraction)
- Security considerations and validation checklist
- Error handling patterns and best practices
This is a solid foundation for developers learning JWT handling. The previous review's terminology and base64 decoding concerns have been substantially addressed.
Deploying kinde-docs-preview with
|
| Latest commit: |
7d07033
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://5bca755c.kinde-docs-preview.pages.dev |
| Branch Preview URL: | https://feat-decodejwts.kinde-docs-preview.pages.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/content/docs/build/tokens/decode-jwts.mdx (1)
311-311: Minor: Consider rewording to strengthen impact.The phrase "should only be used for display purposes" is clear, but you can strengthen it by restructuring to emphasize the security principle: "Client-side decoding is suitable only for display; do not make security decisions based on client-side decoded payloads."
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🧰 Additional context used
🪛 LanguageTool
src/content/docs/build/tokens/decode-jwts.mdx
[style] ~311-~311: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...Decoding**: Client-side decoding should only be used for display purposes, not for s...
(ONLY_EXCLUSIVELY_STYLE)
🔇 Additional comments (6)
src/content/docs/build/tokens/decode-jwts.mdx (6)
1-34: Front matter looks good.Terminology has been correctly aligned to "decoding" and "validating" (not "decrypting"), and metadata is well-structured.
36-43: Clear introduction with appropriate emphasis on JWS.The distinction between signed (JWS) and encrypted (JWE) tokens is clarified upfront, and the JWT structure explanation is well-presented.
44-121: Kinde libraries section is well-structured and practical.Installation instructions and usage examples for both decoder and validator are clear and easy to follow.
262-279: Align TypeScript base64UrlDecode encoding to match cross-platform version.The TypeScript implementation (line 273) uses
.toString('binary')while the cross-platform version (line 230) uses.toString('utf-8'). For consistency and clarity, both should use'utf-8', which is semantically correct since JWT payloads are UTF-8 encoded before base64url encoding.Apply this diff to align with the cross-platform approach:
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 += '='; } // 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 { throw new Error('Neither Buffer nor atob is available in this environment'); } }
305-324: Security section is comprehensive and appropriate.The validation checklist, security notes, and emphasis on validation before trusting the payload are well-articulated and important.
388-422: Error handling and best practices sections are solid.The examples demonstrate defensive programming, and the best practices provide clear, actionable guidance for developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/content/docs/build/tokens/decode-jwts.mdx (1)
311-311: Minor style: Consider rewording to avoid "only."Line 311 states "Client-side decoding should only be used for display purposes." Per style guidance, this could be rephrased as "Client-side decoding is suitable for display purposes" or "Client-side decoding should be limited to display purposes" for variety, but the current phrasing is clear and acceptable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🧰 Additional context used
🪛 LanguageTool
src/content/docs/build/tokens/decode-jwts.mdx
[style] ~311-~311: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...Decoding**: Client-side decoding should only be used for display purposes, not for s...
(ONLY_EXCLUSIVELY_STYLE)
🔇 Additional comments (6)
src/content/docs/build/tokens/decode-jwts.mdx (6)
1-34: Front matter and metadata look solid.The documentation page is well-structured with comprehensive front matter, clear topic categorization, and appropriate audience/complexity settings. Content across the page correctly uses "decoding and validating" terminology and avoids earlier "decrypting" confusion.
44-121: Kinde library sections are clear and actionable.Installation instructions, basic usage, and validation workflows are well-documented with type-safe examples. Examples correctly reference the Kinde packages (@kinde/jwt-decoder, @kinde/jwt-validator) and show both synchronous and async patterns.
132-235: Cross-platform manual decoding implementations are correct.The browser (atob), Node.js (Buffer), and universal approaches all properly handle base64url padding and character replacement. The progression from environment-specific to universal is pedagogically sound for the audience.
262-279: TypeScript implementation properly handles cross-platform environments.The function correctly checks for Buffer availability via
globalThisand falls back toatobin browser contexts, with an error fallback for unexpected environments. Type annotations on interfaces and function parameters are accurate.
305-323: Security section appropriately emphasizes validation over decoding.The distinction between "decoding vs. validation" is clearly stated, and the validation checklist covers signature, issuer, audience, expiration, and custom claims—all critical for production use.
325-414: Use cases and error handling patterns provide practical guidance.Examples for displaying user info, feature flags, and permissions are realistic; error handling uses try-catch with graceful fallbacks. The
safeDecodeJWThelper properly validates token structure before decoding.
|
Hi @Arobce thanks a lot for checking the code for accuracy. Would you be able to check the correctness of the 3 functions used for manual decoding? |
|
@tamalchowdhury did that along with my other tests. Its working perfectly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/content/docs/build/tokens/decode-jwts.mdx (1)
402-420: Minor: Rephrase to avoid overuse of "only" (optional refactor).Line 408 uses "only" in a way that can be strengthened for clarity. Consider:
- - **Never Trust Client-Side Decoding**: Client-side decoding should only be used for display purposes, not for security decisions. + - **Never Trust Client-Side Decoding**: Use client-side decoding exclusively for display purposes; never rely on it for security decisions.The security checklist and best practices sections are otherwise comprehensive and well-structured.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.29.1)
src/content/docs/build/tokens/decode-jwts.mdx
[high] 69-69: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
🪛 LanguageTool
src/content/docs/build/tokens/decode-jwts.mdx
[style] ~408-~408: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...Decoding**: Client-side decoding should only be used for display purposes, not for s...
(ONLY_EXCLUSIVELY_STYLE)
🔇 Additional comments (5)
src/content/docs/build/tokens/decode-jwts.mdx (5)
1-42: Terminology and structure are correct.Front matter and introduction properly use "decoding/validation" terminology and accurately describe JWT structure. Prior terminology feedback has been appropriately addressed.
44-90: jwtDecoder behavior correctly documented.The example output (line 75) correctly shows the payload only, not header and signature. This accurately reflects Kinde's jwtDecoder implementation per the PR feedback.
92-141: Validation and decoding flow is correct.The example properly separates concerns: validateToken returns
{ valid, message }(not payload), flow checksresult.validbefore calling jwtDecoder, and error handling wraps with try/catch. This implements the recommended pattern from PR feedback.
143-323: Manual decoding implementations are cross-platform and correct.Browser, Node.js, universal, and TypeScript implementations all properly handle environment detection and base64url decoding with padding. TypeScript version correctly uses
globalThis.Bufferwith browser fallback. Prior feedback on cross-platform support has been addressed.
69-69: Gitleaks flag is expected for documentation examples.The example JWT on line 69 is a standard token used to demonstrate jwtDecoder output in documentation. This is not a real secret or credential and does not represent a security risk in this context. The alert is a false positive for documentation examples.
|
@Arobce would you be willing to have another look at the overall structure of this guide? thanks. |
|
@tamalchowdhury I am on it. Sorry for the late reply. I missed your mention. |
|
@tamalchowdhury looks perfect! Thanks for the edits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/content/docs/build/tokens/decode-jwts.mdx (1)
436-436: Minor style suggestion: consider varying word choice for "only."The phrase "Client-side decoding should only be used for display purposes" is clear and correct, but LanguageTool suggests varying the word "only" for stylistic diversity. This is entirely optional but could read as: "Client-side decoding should be used exclusively for display purposes" or "Client-side decoding is appropriate only for display purposes."
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/content/docs/build/tokens/decode-jwts.mdx(1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.30.0)
src/content/docs/build/tokens/decode-jwts.mdx
[high] 69-69: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
🪛 LanguageTool
src/content/docs/build/tokens/decode-jwts.mdx
[style] ~436-~436: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...Decoding**: Client-side decoding should only be used for display purposes, not for s...
(ONLY_EXCLUSIVELY_STYLE)
🔇 Additional comments (10)
src/content/docs/build/tokens/decode-jwts.mdx (10)
1-34: Front matter and metadata look comprehensive. ✓The page metadata is well-structured with appropriate sidebar order, related articles, topics, and keywords. All required disclaimers about optional JWT claims (email, org_code, permissions, feature_flags) have been properly added throughout the document.
44-91: Excellent alignment with Kinde library documentation. ✓The jwtDecoder section correctly documents that it returns only the decoded payload (not header/signature). The example output at lines 72–75 accurately shows payload-only structure, and the type-safe decoding pattern is well-presented.
109-141: Validation and decoding flow is correct and properly addresses prior feedback. ✓The example correctly demonstrates:
- Calling
validateToken({ token, domain })and awaiting the result- Checking
result.validbefore proceeding- Calling
jwtDecoder(token)separately to obtain the payload- Wrapping the flow in try/catch for error handling
This matches the corrected pattern recommended in PR review feedback.
143-254: Manual decoding implementations are complete and cross-platform. ✓All three implementations (Browser, Node.js, Universal) are provided and appropriate:
- Browser version (lines 152–162) uses
atob()for browser environments- Node.js version (lines 198–208) uses
Bufferwith UTF-8 encoding- Universal version (lines 239–254) intelligently detects and uses the appropriate runtime API
The progression from simplest (browser-only) to most robust (universal) is pedagogically clear.
257-323: TypeScript implementation is robust with proper cross-platform and error handling. ✓The TypeScript version correctly:
- Uses
globalThis.BufferandglobalThis.atobfor cross-runtime compatibility- Includes comprehensive error handling with explicit Error type checking
- Defines clear interfaces (JWTHeader, JWTPayload, DecodedJWT)
- Throws a descriptive error if neither Buffer nor atob is available
This matches and extends the universal pattern appropriately for a TypeScript context.
327-370: User information example correctly incorporates all corrected patterns and disclaimers. ✓The
displayUserInfo()function:
- Decodes using
jwtDecoder(token)- Includes safe defaults (optional chaining, fallback values)
- Returns structured object or null on error
- Has disclaimers for email, org_code, and permissions requirements
All feedback from prior review has been addressed.
372-404: Feature flags example includes proper error handling and disclaimer. ✓The
checkFeatureFlag()function correctly:
- Decodes the payload
- Checks for feature flag existence before accessing nested value
- Returns false on missing flag or error
- Includes disclaimer that feature_flags must be enabled in Kinde dashboard
406-429: Permissions example properly handles optional claim with fallback and disclaimer. ✓The
getUserPermissions()function:
- Decodes using
jwtDecoder(token)- Returns
payload.permissions || []for safe fallback- Handles errors gracefully
- Includes complete disclaimer about when permissions are included
430-485: Security, error handling, and best practices sections are well-written and complete. ✓Clear separation of validation vs. decoding, comprehensive validation checklist, robust error handling example, and actionable best practices. The "Use Kinde Libraries" guidance appropriately recommends production-ready approaches while documenting manual implementations for educational purposes.
69-69: Gitleaks warning about example JWT is a false positive. ✓The JWT token at line 69 (
eyJhbGciOiJIUzI1NiIs...) is a well-known example token commonly used in JWT documentation and tests. It is not a real secret and poses no security risk. No action needed.
|
@tamalchowdhury please address the CR comments |
|
@onderay CR issue resolved earlier. |


New topic about JWT decoding, referencing the Kinde utils we have to do this.
Needs review by @DanielRivers at minimum.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.