Every language
12 lenguajes, copy-ready. One at a time with syntax highlighting, or all inline.
JSJavaScript
function parseJwt(token) {
const part = token.split('.')[1]; // payload
let b64 = part.replace(/-/g, '+').replace(/_/g, '/');
b64 += '='.repeat((4 - (b64.length % 4)) % 4); // restore stripped padding
return JSON.parse(atob(b64));
}atob yields Latin-1 — claims with non-ASCII names need the TextDecoder route shown in TypeScript. Reading claims client-side is fine; trusting them is not.
TSTypeScript
interface Claims {
sub: string;
exp: number;
[key: string]: unknown;
}
function parseJwt(token: string): Claims {
const part = token.split('.')[1];
const b64 = part.replace(/-/g, '+').replace(/_/g, '/');
const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
const bytes = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes)); // UTF-8 safe
}Uint8Array.from(atob(...)) is the byte bridge that keeps non-ASCII claims intact. Check exp against Date.now() / 1000 after parsing.
GoGo
func parseJwt(token string) (map[string]any, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, errors.New("jwt: want 3 dot-separated parts")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, err
}
var claims map[string]any
return claims, json.Unmarshal(payload, &claims)
}RawURLEncoding is EXACTLY the JWT alphabet — unpadded URL-safe — so Go needs none of the string surgery other languages do. golang-jwt verifies.
RsRust
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use serde_json::Value;
fn parse_jwt(token: &str) -> Option<Value> {
let payload = token.split('.').nth(1)?;
let bytes = URL_SAFE_NO_PAD.decode(payload).ok()?;
serde_json::from_slice(&bytes).ok()
}URL_SAFE_NO_PAD matches the JWT spec byte-for-byte. The jsonwebtoken crate does sign and verify; this only reads.
PHPPHP
function parseJwt(string $token): array {
$payload = explode('.', $token)[1];
$b64 = strtr($payload, '-_', '+/'); // base64url → base64 alphabet
$b64 .= str_repeat('=', (4 - strlen($b64) % 4) % 4);
return json_decode(
base64_decode($b64),
true,
flags: JSON_THROW_ON_ERROR,
);
}strtr swaps the URL-safe alphabet in one call. firebase/php-jwt is the verify-with-a-real-library answer.
PyPython
import base64
import json
def parse_jwt(token: str) -> dict:
payload = token.split('.')[1]
padded = payload + '=' * (-len(payload) % 4) # pad to multiple of 4
return json.loads(base64.urlsafe_b64decode(padded))-len % 4 is the concise pad-to-multiple-of-4 (0 stays 0). urlsafe_b64decode expects the padded form even though JWT omits it.
C#C#
using System.Buffers.Text;
using System.Text.Json;
static JsonDocument ParseJwt(string token) =>
JsonDocument.Parse(
Base64Url.DecodeFromChars(token.Split('.')[1]));.NET 9's Base64Url kills the whole alphabet-and-padding dance in one call. Before it: Replace('-', '+'), Replace('_', '/'), pad, Convert.FromBase64String.
JvJava
String payload = token.split("\\.")[1];
// TRAP: getUrlDecoder REQUIRES padding — JWT strips it, put it back
String padded = payload + "=".repeat((4 - payload.length() % 4) % 4);
byte[] json = Base64.getUrlDecoder().decode(padded);
Map<String, Object> claims =
new ObjectMapper().readValue(json, Map.class); // JacksonJava's getUrlDecoder is the one mainstream decoder that rejects unpadded input — the padding restore is mandatory, not optional.
SwSwift
import Foundation
func parseJwt(_ token: String) throws -> [String: Any] {
let payload = token.split(separator: ".")[1]
var b64 = payload
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
while b64.count % 4 != 0 { b64 += "=" }
guard let data = Data(base64Encoded: b64) else {
throw URLError(.badServerResponse)
}
return try JSONSerialization.jsonObject(with: data) as! [String: Any]
}Store tokens in the Keychain, never UserDefaults. JWTKit / JWTDecoder for verification — this only reads.
KtKotlin
import java.util.Base64
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
fun parseJwt(token: String) = Json.parseToJsonElement(
String(
Base64.getUrlDecoder().decode(
token.split('.')[1]
.let { it + "=".repeat((4 - it.length % 4) % 4) },
),
),
).jsonObjectSame JVM padding trap as Java. The jsonObject result reads like a map: claims["sub"] — kotlinx.serialization keeps it allocation-light.
RbRuby
require 'base64'
require 'json'
def parse_jwt(token)
payload = token.split('.')[1]
b64 = payload.tr('-_', '+/')
b64 += '=' * ((4 - b64.length % 4) % 4)
JSON.parse(Base64.strict_decode64(b64))
endString#tr swaps alphabets in place. For rails-size code, jwt gem — decode(token, key, true) verifies while parsing.
ZigZig
const std = @import("std");
pub fn main() !void {
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.sig";
var it = std.mem.splitScalar(u8, token, '.');
_ = it.next(); // header
const payload = it.next() orelse return error.BadToken;
const dec = std.base64.url_safe_no_pad.Decoder;
const len = try dec.calcSizeForSlice(payload);
const buf = try std.heap.page_allocator.alloc(u8, len);
defer std.heap.page_allocator.free(buf);
try dec.decode(buf, payload);
var parsed = try std.json.parseFromSlice(
std.json.Value, std.heap.page_allocator, buf, .{});
defer parsed.deinit();
std.debug.print("{s}\n", .{parsed.value.object.get("sub").?.string});
}std.base64.url_safe_no_pad is the exact JWT alphabet — no surgery needed, like Go. parseFromSlice returns an owned value; deinit() it.