<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: rmb</title>
    <description>The latest articles on DEV Community by rmb (@rmb_37).</description>
    <link>https://dev.to/rmb_37</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3966227%2Fc35c73e7-e96c-4fdb-a196-fb4e4a145c93.png</url>
      <title>DEV Community: rmb</title>
      <link>https://dev.to/rmb_37</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rmb_37"/>
    <language>en</language>
    <item>
      <title>Stop pasting JWTs into jwt.io</title>
      <dc:creator>rmb</dc:creator>
      <pubDate>Wed, 03 Jun 2026 09:35:51 +0000</pubDate>
      <link>https://dev.to/rmb_37/stop-pasting-jwts-into-jwtio-12kn</link>
      <guid>https://dev.to/rmb_37/stop-pasting-jwts-into-jwtio-12kn</guid>
      <description>&lt;p&gt;You're debugging an authentication issue. The frontend is getting a 403. You copy the JWT from the Authorization header, open jwt.io in a new tab, paste it in, and check the claims.&lt;/p&gt;

&lt;p&gt;Most developers do this multiple times a week. I did too.&lt;/p&gt;

&lt;p&gt;The problem: jwt.io is a third-party website. When you paste a token there, you're sending your authentication credential to their servers.&lt;/p&gt;

&lt;p&gt;Here's what actually happens when you paste into jwt.io:&lt;/p&gt;

&lt;p&gt;The token appears in their server logs. It's in the referrer headers if you navigated from somewhere. It may be in Cloudflare or CDN edge cache logs. Their servers receive it at minimum for the TLS handshake. You have no visibility into their logging or retention practices.&lt;/p&gt;

&lt;p&gt;None of that is jwt.io's fault – they're a legitimate tool and they've been clear about their security model. The issue is that production tokens with real claims (user IDs, permission scopes, email addresses, or API credentials embedded in custom claims) probably shouldn't transit through third-party infrastructure at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why it feels fine but isn't
&lt;/h2&gt;

&lt;p&gt;JWT payloads are Base64url-encoded, not encrypted. The header and payload are readable to anyone. That's by design – JWTs are for integrity, not confidentiality. The signature is what prevents tampering; the contents are intentionally readable.&lt;/p&gt;

&lt;p&gt;This means decoding a JWT doesn't require any server-side computation. Your browser can do it in one line.&lt;/p&gt;

&lt;p&gt;Open your browser console right now and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your.jwt.here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/_/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No library. No server. No upload. The entire decode operation happens in your browser's JavaScript runtime.&lt;/p&gt;

&lt;p&gt;You can also do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Paste this function anywhere&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/_/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Can't decode without the secret key&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When tokens contain things that actually matter
&lt;/h2&gt;

&lt;p&gt;The real risk isn't a short-lived access token for a public API. It's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service account tokens&lt;/strong&gt; – Long-lived, often stored in CI/CD pipelines, granting broad API access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh tokens embedded in JWTs&lt;/strong&gt; – Some auth systems encode refresh credentials in custom claims&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal service tokens&lt;/strong&gt; – Tokens for your internal microservices that contain environment-specific URLs or config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens with PII in claims&lt;/strong&gt; – Email addresses, user IDs, phone numbers in the payload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens used for high-privilege operations&lt;/strong&gt; – Admin access, billing actions, data exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these, the transit risk is real. A compromised token for an internal admin service is more valuable to an attacker than you'd think.&lt;/p&gt;




&lt;h2&gt;
  
  
  The no-upload alternative
&lt;/h2&gt;

&lt;p&gt;If you want a full UI with field labeling, expiration parsing, and copy buttons, there are client-side decoders that run entirely in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://brevio.pro/tools/jwt-decoder" rel="noopener noreferrer"&gt;brevio JWT Decoder&lt;/a&gt; is one – it uses the same &lt;code&gt;atob&lt;/code&gt; approach in the browser console, just with a UI. The difference from jwt.io: no network request is made when you decode. Open DevTools → Network tab and verify this yourself; you'll see no outbound request after the initial page load.&lt;/p&gt;

&lt;p&gt;Squoosh, CyberChef (self-hosted), and browser extensions like &lt;code&gt;jwt-inspector&lt;/code&gt; are other options in the "stays local" category.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real question
&lt;/h2&gt;

&lt;p&gt;When you copy a JWT from a production request and paste it somewhere, ask: what service did this token come from, what can it do, and how long is it valid?&lt;/p&gt;

&lt;p&gt;If it's a 15-minute access token for read-only data on a public API, the risk is low. If it's a 24-hour admin token with write access to production data, pasting it into any external tool is a security incident waiting to happen.&lt;/p&gt;

&lt;p&gt;The browser console one-liner takes three seconds and involves zero third parties. Default to that.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;For developers who handle confidential files and sensitive data, &lt;a href="https://brevio.pro/guides/how-to-decode-jwt-without-third-party" rel="noopener noreferrer"&gt;brevio.pro&lt;/a&gt; offers browser-only tools for PDF processing, image compression, JSON formatting, and JWT decoding — nothing leaves your browser.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
