JWT-Generator

Generieren Sie JSON Web Tokens mit Multi-Algorithmus-Unterstützung und benutzerdefinierten Claims

What is a JSON Web Token (JWT)?

A JSON Web Token is a compact, URL-safe string that represents claims transferred between two parties. Defined in RFC 7519, a JWT consists of three Base64URL-encoded segments separated by dots: a header declaring the signing algorithm, a payload containing the claims, and a cryptographic signature binding the two together. The jwt-generator tool focuses on the creation side of this process — selecting the appropriate signing algorithm, configuring claims with correct semantics, and assembling a properly signed token ready for use in authentication flows, API authorization, or inter-service communication.

Unlike the jwt-decoder (which only parses and inspects existing tokens without verifying their authenticity) or the jwt-verifier (which validates signatures against a known key), this tool performs the inverse operation: it takes a set of claims and a signing key, then produces a cryptographically signed token. Understanding token generation is essential for backend developers implementing OAuth 2.0 resource servers, microservice-to-microservice authentication, and stateless session management.

Signing Algorithm Selection: HS256, RS256, and ES256

The algorithm specified in the JWT header (alg field) determines how the signature is computed and verified. RFC 7518 (JSON Web Algorithms) defines the complete set of allowed algorithms, but three dominate production usage:

  • HS256 (HMAC-SHA-256): A symmetric algorithm where the same secret key is used to both sign and verify the token. Fast and simple — ideal when the token issuer and verifier are the same service or share a secret securely. The secret must be at least 256 bits (32 bytes) for full security. Trade-off: every party that verifies tokens must hold the secret, creating a key distribution problem in distributed systems.
  • RS256 (RSA-SHA-256): An asymmetric algorithm using RSA key pairs. The issuer signs with a private key; any consumer can verify with the corresponding public key. This eliminates secret sharing — publish your public key via a JWKS endpoint and any service can validate tokens without possessing signing credentials. RSA keys are large (2048+ bits) and signing is computationally heavier than HMAC, but verification is fast. Preferred for OAuth 2.0 identity providers and multi-tenant systems.
  • ES256 (ECDSA P-256 with SHA-256): An asymmetric algorithm based on Elliptic Curve Cryptography. Provides the same separation of signing/verification as RS256 but with significantly smaller keys (256-bit vs 2048-bit RSA) and faster signing operations. Signatures are compact (64 bytes vs 256 bytes for RS256), reducing token size. Increasingly adopted in modern systems, mobile clients, and environments where bandwidth matters.

Choose HS256 when the signer and verifier are the same entity or tightly coupled. Choose RS256 or ES256 when tokens are verified by multiple independent services — the public key can be distributed without risk, while the private key stays isolated on the signing authority.

Claims Configuration: Registered, Public, and Private Claims

The JWT payload carries claims — statements about the subject and metadata controlling token behavior. RFC 7519 defines registered claim names with standardized semantics:

  • iss (issuer): Identifies the principal that issued the token. Typically a URL or service identifier (e.g., "https://auth.example.com"). Verifiers should reject tokens from unrecognized issuers.
  • sub (subject): Identifies the principal that is the subject of the token — usually a user ID or service account identifier. Must be unique within the issuer's context.
  • aud (audience): Identifies the recipients the token is intended for. A resource server should reject tokens whose aud does not include its own identifier, preventing token misuse across services.
  • exp (expiration time): A NumericDate (Unix timestamp in seconds) after which the token MUST be rejected. Critical for security — tokens without expiration live forever if the signing key is never rotated.
  • iat (issued at): A NumericDate indicating when the token was issued. Useful for determining token age and enforcing maximum lifetime policies.
  • nbf (not before): A NumericDate before which the token MUST NOT be accepted. Used for pre-issuing tokens that activate at a future time.
  • jti (JWT ID): A unique identifier for the token. Enables revocation lists and prevents replay attacks by ensuring each token can only be consumed once.

Beyond registered claims, applications define private claims carrying domain-specific data: roles, permissions, tenant IDs, feature flags, or subscription tiers. Keep payloads minimal — every byte increases token size, network overhead, and storage in cookies or headers. Sensitive data (passwords, credit card numbers) must never appear in JWT payloads because the payload is only Base64URL-encoded, not encrypted.

Token Assembly: From Claims to Signed Output

JWT generation follows a deterministic three-step process defined by RFC 7515 (JSON Web Signature):

  1. Header construction: Build a JSON object with alg (the signing algorithm) and typ (always "JWT"). Optional fields include kid (key ID) for key rotation support. Base64URL-encode this JSON to produce the first segment.
  2. Payload construction: Build a JSON object with all configured claims — registered claims (iss, sub, exp, iat) plus any custom claims. Compute iat as the current Unix timestamp and exp as iat + lifetime. Base64URL-encode this JSON to produce the second segment.
  3. Signature computation: Concatenate the encoded header and payload with a dot separator (header.payload), then apply the signing algorithm with the secret or private key. Base64URL-encode the resulting signature bytes to produce the third segment.

The final token is the concatenation: encodedHeader.encodedPayload.encodedSignature. This structure ensures that any modification to the header or payload invalidates the signature, providing tamper detection without encryption. The token is self-contained — a verifier only needs the appropriate key (shared secret for HS256, public key for RS256/ES256) to validate authenticity without contacting the issuer.

Common mistakes during token generation include: using exp in milliseconds instead of seconds, omitting aud (allowing token reuse across services), choosing overly long expiration times (hours instead of minutes for access tokens), and including the signing key in the payload itself.

Security Considerations for Token Generation

Generating JWTs carries direct security implications. A poorly generated token can grant unauthorized access, leak sensitive information, or enable escalation attacks:

  • Never use "alg": "none" in production. The "none" algorithm produces unsigned tokens that any party can forge. Some libraries accept alg: none by default — always enforce algorithm whitelisting on both generation and verification sides.
  • Key strength matters. HS256 secrets must be at least 256 bits of cryptographically random data. Weak secrets (short strings, dictionary words, predictable values) can be brute-forced offline once an attacker obtains a single valid token.
  • Minimize token lifetime. Access tokens should expire in minutes (5-15), not hours. Use refresh tokens (stored securely server-side) for longer sessions. Short-lived tokens limit the damage window if a token is leaked.
  • Validate all claims on the verification side. Generation correctness does not guarantee security — the verifier must check exp, iss, aud, and algorithm to prevent token reuse, expired token acceptance, and algorithm confusion attacks.

Code Examples

Generating a signed JWT with HS256 (Node.js, no dependencies)

import { createHmac } from 'node:crypto';

function base64url(input) {
  return Buffer.from(input)
    .toString('base64')
    .replace(/=/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

function generateJwt(payload, secret, options = {}) {
  const header = {
    alg: options.algorithm || 'HS256',
    typ: 'JWT',
  };

  const now = Math.floor(Date.now() / 1000);
  const claims = {
    iat: now,
    exp: now + (options.expiresIn || 3600),
    ...payload,
  };

  const encodedHeader = base64url(JSON.stringify(header));
  const encodedPayload = base64url(JSON.stringify(claims));
  const signingInput = encodedHeader + '.' + encodedPayload;

  const signature = createHmac('sha256', secret)
    .update(signingInput)
    .digest('base64url');

  return signingInput + '.' + signature;
}

// Generate a token for a user session
const token = generateJwt(
  { sub: 'user-123', iss: 'https://api.example.com', role: 'admin' },
  'your-256-bit-secret-key-here-min-32-chars!',
  { expiresIn: 900 } // 15 minutes
);

console.log(token);
// → eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDk...

Output:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDkwMDAwMDAsImV4cCI6MTcwOTAwMDkwMCwic3ViIjoidXNlci0xMjMiLCJpc3MiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiJ9.abc123...

Generating a JWT with RS256 using Web Crypto API (browser)

async function generateRs256Jwt(payload, privateKey) {
  const header = { alg: 'RS256', typ: 'JWT' };
  const now = Math.floor(Date.now() / 1000);
  const claims = { iat: now, exp: now + 3600, ...payload };

  const encoder = new TextEncoder();
  const encodedHeader = btoa(JSON.stringify(header))
    .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  const encodedPayload = btoa(JSON.stringify(claims))
    .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');

  const signingInput = encoder.encode(encodedHeader + '.' + encodedPayload);

  const signature = await crypto.subtle.sign(
    { name: 'RSASSA-PKCS1-v1_5' },
    privateKey,
    signingInput
  );

  const encodedSignature = btoa(String.fromCharCode(...new Uint8Array(signature)))
    .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');

  return encodedHeader + '.' + encodedPayload + '.' + encodedSignature;
}

// Generate RSA key pair for signing
const keyPair = await crypto.subtle.generateKey(
  { name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([1,0,1]), hash: 'SHA-256' },
  true, ['sign', 'verify']
);

const jwt = await generateRs256Jwt(
  { sub: 'service-account', aud: 'https://api.target.com' },
  keyPair.privateKey
);

Standards & Specifications

  • RFC 7519 — JSON Web Token specification — defines the token structure, registered claims, and processing rules
  • RFC 7518 (JWA) — JSON Web Algorithms — specifies cryptographic algorithms for JWS signatures including HS256, RS256, and ES256
  • RFC 7515 (JWS) — JSON Web Signature — defines the signing and serialization process used to produce JWT signatures

Häufig Gestellte Fragen

What's the difference between HMAC and RSA algorithms?

HMAC algorithms (HS256, HS384, HS512) use a shared secret key for both signing and verification. They're faster and simpler but require both parties to have the same secret. RSA algorithms (RS256, RS384, RS512) use public/private key pairs - you sign with the private key and verify with the public key. RSA is better for distributed systems where you can't safely share secrets.

Should I use this tool for production JWTs?

No, this tool is for testing and development only. Never use production secrets or private keys in browser-based tools. Always generate JWTs on your secure backend server in production environments where secrets can be properly protected.

How long should my secret key be?

For HS256, use at least 256 bits (32 characters) of random data. For HS384, use at least 384 bits (48 characters). For HS512, use at least 512 bits (64 characters). Longer keys are more secure. Use a cryptographically secure random generator to create your secrets.

What expiration time should I set?

Shorter expiration times are more secure. For access tokens, 15-60 minutes is common. For refresh tokens, hours to days. Consider your security requirements and user experience - shorter times mean more frequent re-authentication but better security if tokens are compromised.

Can I include sensitive data in the payload?

JWT payloads are Base64URL-encoded, not encrypted - anyone can decode and read them. Never include sensitive data like passwords, credit card numbers, or personal information. Only include data that's safe to expose, like user IDs and roles.

What format should my RSA private key be in?

The private key must be in PKCS#8 PEM format, starting with '-----BEGIN PRIVATE KEY-----' and ending with '-----END PRIVATE KEY-----'. You can generate RSA key pairs using OpenSSL or other cryptographic tools.

Is my data sent to a server?

No, all JWT generation happens in your browser. Your secrets, keys, and payload data never leave your device. However, remember this is for testing only - never use production secrets in any browser-based tool.