<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Navin Adhikari]]></title><description><![CDATA[Navin Adhikari]]></description><link>https://filereadynow.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 03:39:14 GMT</lastBuildDate><atom:link href="https://filereadynow.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Decoded JWT Is Only a Claim: What to Check Before You Trust It]]></title><description><![CDATA[Decoding a JWT only shows its claims. Here is the order to check alg, iss, aud and exp, with a tested expiry helper and the RFC references.]]></description><link>https://filereadynow.hashnode.dev/a-decoded-jwt-is-only-a-claim-what-to-check-before-you-trust-it</link><guid isPermaLink="true">https://filereadynow.hashnode.dev/a-decoded-jwt-is-only-a-claim-what-to-check-before-you-trust-it</guid><category><![CDATA[JWT]]></category><category><![CDATA[Security]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Navin Adhikari]]></dc:creator><pubDate>Wed, 28 Oct 2026 15:26:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adb89a1c414037f96348ed5/16487621-529d-4d1e-9a15-6fbbd6c9c47f.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Decoding a JWT tells you what the token says about itself. It does not tell you whether the claims are true. Anyone can build a token with any payload they like, and base64url is an encoding, not encryption. RFC 7519, section 3, describes a JWT as URL-safe parts separated by periods, and that is all the decoding step undoes.</p>
<p>So when a request fails with a 401 or 403, decoding the token is a good first look, but only if you know what to look at. Here is an order that works for me as a checklist, with the reasons.</p>
<p>Disclosure: I work on FileReadyNow, which has a JWT decoder, mentioned at the end. An AI assistant helped me research and draft this post. I checked the claims against the RFCs.</p>
<ol>
<li>Is it a token you can read at all?</li>
</ol>
<p>A signed JWT (a JWS) has three parts: header, payload, signature. If you see a different number of parts, it may be an encrypted token (JWE) and the payload will not be readable by decoding. Check this first so you do not chase a "bad token" that is simply a different kind.</p>
<ol start="2">
<li>Does the alg match what your server accepts?</li>
</ol>
<p>The header's alg field says how the token claims to be signed. RFC 8725 (JSON Web Token Best Current Practices, February 2020) says in section 3.1 that libraries must let the caller set a list of supported algorithms and must not use any others. It also says the alg in the header must match the algorithm actually used.</p>
<p>The practical rule: your server decides which algorithms it accepts. It does not read that from the token. If you see alg: "none", remember that section 3.2 says it should only be used when the JWT is protected by other means, and a library should not produce such tokens unless explicitly asked.</p>
<ol start="3">
<li>Is the issuer the one you expect?</li>
</ol>
<p>If the payload has an iss claim, RFC 8725 section 3.8 says the application must confirm that the signing keys belong to that issuer, and reject the token otherwise. When you are debugging, compare iss with the issuer your server is configured for. A token from a staging identity provider presented to a production API is a common cause of rejection that looks like a bug.</p>
<ol start="4">
<li>Is the audience you?</li>
</ol>
<p>Section 3.9 of RFC 8725 says that when an issuer serves several relying parties, the JWT must carry an aud claim, and the receiving side must check it and reject the token if it is missing or does not match. If decoding shows an aud that is not your API, you have found your answer.</p>
<ol start="5">
<li>Is it expired, and in which unit?</li>
</ol>
<p>RFC 7519 defines exp and iat as NumericDate values: seconds since 1970-01-01T00:00:00Z. JavaScript dates use milliseconds, so convert before comparing.</p>
<p>js function checkTimes(payload, nowMs = Date.now(), skewSeconds = 0) { const nowSec = Math.floor(nowMs / 1000); const out = {}; if (typeof payload.exp !== "number") { out.exp = "missing or not a number"; } else { out.exp = payload.exp + skewSeconds &lt;= nowSec ? "expired" : "still valid"; out.expiresAt = new Date(payload.exp * 1000).toISOString(); } if (typeof payload.iat === "number") { out.issuedAt = new Date(payload.iat * 1000).toISOString(); } return out; }</p>
<p>I ran this in Node v22.23.4. With { iat: 1760000000, exp: 1760003600 } it reports "still valid" at 2025-10-09T09:00:00Z and "expired" at 2026-01-01, with expiresAt of 2025-10-09T09:53:20.000Z. A string exp such as "1760003600" returns "missing or not a number". skewSeconds is an optional allowance you choose, not a value from a standard. Run it in a browser console before relying on it [CHECK].</p>
<ol start="6">
<li>Leave the signature to the server</li>
</ol>
<p>Decoding cannot verify a signature. That needs the secret or public key and a library that enforces the checks above. Do verification on the server, and do not make access decisions in client code from a decoded payload.</p>
<p>One more habit: keep secrets out of the payload. Anyone who holds the token can read it.</p>
<p>A decoder when you do not want to write one</p>
<p>I work on FileReadyNow, and its JWT Decoder shows the header, payload, signature and a Claims tab, with a "Decode Token" button. Its page says decoding runs in the browser with no network requests, and that it only decodes. Verify the first claim yourself: open DevTools, switch to the Network tab, click "Decode Token", and look for requests. Do not paste production tokens into any tool you cannot inspect.</p>
]]></content:encoded></item><item><title><![CDATA[Why Your OCR Output Is Messy, and 5 Fixes to Make Before You Run It]]></title><description><![CDATA[Messy OCR output usually starts with the image. Five fixes from Tesseract's documentation: resolution, cropping, straight lines, contrast and noise.]]></description><link>https://filereadynow.hashnode.dev/why-your-ocr-output-is-messy-and-5-fixes-to-make-before-you-run-it</link><guid isPermaLink="true">https://filereadynow.hashnode.dev/why-your-ocr-output-is-messy-and-5-fixes-to-make-before-you-run-it</guid><category><![CDATA[OCR ]]></category><category><![CDATA[tesseract]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[webdev]]></category><category><![CDATA[ImageProcessing ]]></category><dc:creator><![CDATA[Navin Adhikari]]></dc:creator><pubDate>Tue, 27 Oct 2026 15:31:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adb89a1c414037f96348ed5/221ed31a-58bb-4b61-a385-eaaf2e725684.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You run a screenshot through an OCR tool and get "rn" where there should be an "m", a few invented symbols, and a paragraph that reads like a ransom note. The engine is rarely the main problem. The picture is.</p>
<p>Tesseract is the OCR engine behind a lot of free tools, and its documentation has a page on improving output quality. This post turns that page into five checks you can make before you run an image. I also show where they apply in a browser-based tool, since that is where most people meet OCR.</p>
<p><strong>Disclosure:</strong> I work on FileReadyNow, which built the browser tool I use as the example below. The checks themselves apply to any Tesseract-based tool.</p>
<h2>1. Check the resolution</h2>
<p>Tesseract's documentation says it works best on images with a DPI of at least 300, and suggests resizing when yours is lower. DPI only means something for a scan or a print, but the idea carries over to screenshots and photos: the characters need enough pixels to be distinguishable. A tiny screenshot of small text is the classic failure. If you can, capture at a larger size, or enlarge the image before you extract text from it.</p>
<h2>2. Crop to the text, with a small margin</h2>
<p>The documentation warns about borders in both directions. Missing borders can hurt recognition, and it suggests a margin of about 10 pixels around the text. Large borders can cause "empty page" errors. So crop away the window chrome, the sidebar and the table gridlines, and leave a thin margin of plain background.</p>
<p>Dark edges on scans are a related trap. The documentation says scanner borders can be picked up as extra characters, which is where stray symbols at the start of lines often come from.</p>
<h2>3. Straighten the lines</h2>
<p>If a photo of a page is tilted, line segmentation gets worse. The documentation's advice is to rotate the image so the text lines are horizontal. With a phone photo, it is faster to retake it with the page flat than to fix the angle afterwards.</p>
<h2>4. Fix the contrast</h2>
<p>Tesseract applies its own thresholding step to turn the image into black and white, and the documentation says this can work poorly when the page background is unevenly dark. It also notes that dark text on a light background is the safe case.</p>
<p>That is why light grey text on a coloured card gives poor results. Raising contrast, or converting to grayscale first, often helps. If the background is patterned or shaded, expect to do some work.</p>
<h2>5. Deal with noise</h2>
<p>The documentation describes noise as random variation in brightness or colour, and says some kinds cannot be removed by Tesseract's own binarisation step. Compression artifacts and grainy low-light photos are common sources. A sharper original beats any filter.</p>
<h2>Where these checks show up in a browser tool</h2>
<p>FileReadyNow's Image to Text tool runs Tesseract.js, a JavaScript build of the engine, inside the browser. Its page names Tesseract.js and states that the processing happens on your device.</p>
<p>A few details from the live page that relate to the checks above:</p>
<ul>
<li>It has an "Enhance for OCR (grayscale + contrast)" toggle, which maps to check 4.</li>
<li>It accepts JPG, PNG, WEBP and BMP, up to 10 images per batch and 15 MB per file.</li>
<li>The result lands in an editable box with a Copy Text button.</li>
<li>The page does not state which languages it recognises, so test any non-Latin script before you trust it.</li>
</ul>
<p>If you want to try the five checks on one image, the tool is here: <a href="https://www.filereadynow.com/image-tools/image-to-text">Image to Text</a>. Run the image once untouched and once after cropping and enhancing, and compare the two outputs side by side.</p>
<h2>Verify the privacy claim yourself</h2>
<p>"Runs in your browser" is a statement from the tool's page, so test it. Open DevTools, switch to the Network tab, and run a harmless sample image, for example a screenshot of a sentence you wrote. A browser OCR engine may download its own program or language data on first run, so a few requests are normal. What you should not see is a request that uploads your image. Do this before you process a document that matters.</p>
<h2>What OCR still gets wrong</h2>
<p>Decorative fonts, logos, handwriting and complex table layouts are weak spots for general-purpose OCR. The page I used describes printed text and does not mention handwriting, so I would not rely on it for handwritten notes. Treat any extraction as a first draft and proofread names, numbers and codes.</p>
<h2>The short version</h2>
<p>Use enough resolution, crop to the text with a thin margin, keep the lines level, raise the contrast, and start from a clean original. These come straight from the engine's own documentation, and they cost less time than trying to repair a bad result afterwards.</p>
<p><strong>Source:</strong> <a href="https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html">Tesseract documentation, Improving the quality of the output</a></p>
]]></content:encoded></item></channel></rss>