Claims (Registered/Public/Private)

Authorization Security Notes Jan 6, 2025 JAVASCRIPT

Definition

When you check into a hotel, the front desk gives you a key card. But that little card contains more than just the ability to open your door - it might encode what floor you can access, whether you have gym privileges, if you’re in the VIP program, and when your stay ends. In the digital world, claims work exactly the same way: they’re pieces of information embedded in security tokens that tell systems who you are and what you’re allowed to do.

Claims are statements about a user (or sometimes a machine) that travel along with a security token like a JWT. Think of them as the “facts” that your digital ID card carries. When you log into an application, the authentication server doesn’t just say “yes, this person is valid” - it packages up relevant information about you into claims and sends them along. The application receiving these claims can then make decisions based on this information without having to go back and ask the authentication server every time.

There are three types of claims, and understanding the difference helps you use them correctly. Registered claims are the standard ones defined by official specifications - things like when the token was issued, when it expires, and who it was issued for. These are like the standard fields on any government ID: name, date of birth, expiration date. Public claims are custom information that uses a naming convention to avoid conflicts - like using a URL prefix to ensure your “roles” claim doesn’t clash with someone else’s “roles” claim. Finally, private claims are the wild west - any information the token issuer and receiver agree upon, like your department, subscription level, or favorite color if that somehow matters to your application.

Example

Real-World Scenario 1: Netflix Profile Switching When you switch between profiles on Netflix, the token you receive contains claims about which profile you’re using, whether it’s a kids profile (with content restrictions), your subscription tier (determining video quality), and your viewing preferences. The Netflix app uses these claims to personalize your entire experience without checking back with the server for every decision.

Real-World Scenario 2: Google Workspace Access When you’re logged into Google, your token contains claims about which organization you belong to, your email address, whether you’re an admin, and what services you’re allowed to access. When you try to open Google Analytics, the system checks your claims to see if your organization has access and if your role permits viewing data.

Real-World Scenario 3: Hospital Information Systems In a hospital, Dr. Smith’s token might contain claims like “role: physician”, “department: cardiology”, “clearance: level-3”, and “licensed-states: [CA, NV]”. When she tries to access a patient’s cardiac records, the system checks these claims to verify she has the right role, department affiliation, and clearance level - all without making a database call.

Real-World Scenario 4: E-commerce Membership Tiers Amazon Prime membership status is essentially a claim. Your token might contain “prime: true”, “prime_since: 2019-03-15”, “prime_video: true”, “prime_music: false”. Different parts of the Amazon ecosystem check these claims to determine whether you see free shipping options, can access Prime Video content, or get member-exclusive deals.

Analogy

The Driver’s License Model: Your driver’s license is full of claims. Some are registered and standardized - your name, date of birth, license expiration date. Every state uses these same fields because they’re universally understood. Some are more like public claims - endorsements for motorcycles or commercial vehicles, coded in ways that differ slightly by state but follow general conventions. And some are like private claims - maybe your state includes whether you’re an organ donor or have a veteran status, which only matter within certain contexts.

The Concert Wristband System: At a music festival, different wristbands grant different access. The basic wristband might just say “general admission.” A VIP wristband contains additional claims: “backstage: true”, “vip_lounge: true”, “priority_entry: true”. The wristband itself carries all the information - security doesn’t need to look you up in a database; they just read your wristband’s claims.

The Library Card Analogy: Your library card encodes claims about you: your membership level, your home branch, whether you can access special collections, your borrowing limit, and your account status. Different sections of the library check different claims - the rare books room cares about your special collections access, while the checkout desk cares about your borrowing limit and whether you have overdue items.

The Airline Boarding Pass: A boarding pass is packed with claims. Your name, flight number, seat assignment, boarding group, and status (economy, business, frequent flyer tier) are all claims. The gate agent scans it and immediately knows everything they need: Can you board now? Do you get priority? Are you in the right terminal? It’s all encoded right there.

Code Example


// Creating JWT with different claim types
const jwt = require('jsonwebtoken')

function createToken(user) {
  const payload = {
    // REGISTERED CLAIMS ([RFC 7519](https://reference.apios.info/terms/rfc-7519/))
    iss: 'https://auth.myapp.com',           // Issuer
    sub: user.id,                             // Subject (user identifier)
    aud: 'https://api.myapp.com',             // Audience
    exp: Math.floor(Date.now() / 1000) + 3600, // Expiration (1 hour)
    nbf: Math.floor(Date.now() / 1000),       // Not before
    iat: Math.floor(Date.now() / 1000),       // Issued at
    jti: generateUniqueId(),                  // JWT ID (for revocation)

    // PUBLIC CLAIMS (use URIs to avoid collisions)
    'https://myapp.com/claims/roles': user.roles,
    'https://myapp.com/claims/permissions': user.permissions,

    // PRIVATE CLAIMS (application-specific, agreed between parties)
    department: user.department,
    email: user.email,
    email_verified: user.emailVerified,
    plan: user.subscriptionPlan
  }

  return jwt.sign(payload, process.env.JWT_PRIVATE_KEY, {
    algorithm: 'RS256'
  })
}

// Validating and using claims
function validateAndExtractClaims(token) {
  try {
    const decoded = jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
      // Validate registered claims
      issuer: 'https://auth.myapp.com',
      audience: 'https://api.myapp.com',
      algorithms: ['RS256'],
      // Automatically checks exp, nbf, iat
    })

    // Extract claims safely with defaults
    const claims = {
      userId: decoded.sub,
      roles: decoded['https://myapp.com/claims/roles'] || [],
      permissions: decoded['https://myapp.com/claims/permissions'] || [],
      department: decoded.department,
      email: decoded.email,
      emailVerified: decoded.email_verified || false
    }

    return claims
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      throw new Error('Token expired')
    }
    throw new Error('Invalid token')
  }
}

// Authorization using claims
function hasPermission(token, requiredPermission) {
  const claims = validateAndExtractClaims(token)
  return claims.permissions.includes(requiredPermission)
}

Security Notes

SECURITY NOTES

CRITICAL: Claims are BASE64 encoded but NOT encrypted; anyone can read them. Never store passwords, secrets, or sensitive data in claims.

Claim Injection & Validation:

  • Never trust client-provided claims: Always validate signature first before trusting claim values
  • Signature validation mandatory: Attacker can modify JWT payload if signature is not checked
  • Validate claim types: Roles should be arrays, not strings; implement type checking
  • Validate data types: Ensure claims match expected types (exp should be number, roles should be array)

Token Expiration & Lifecycle:

  • Check “exp” claim: Tokens without expiration never expire; always validate expiration
  • Validate “nbf” and “iat”: Ensure “not before” and “issued at” claims are reasonable
  • Implement token revocation: Use “jti” (JWT ID) claim to implement revocation lists
  • Time synchronization: Ensure server clocks are synchronized to prevent acceptance of expired tokens

Vulnerability Prevention:

  • Algorithm confusion attack: Never allow “alg: none”; always specify allowed algorithms
  • Weak HS256 keys: Use RS256 (asymmetric) instead of HS256 (symmetric) with proper key management
  • Key rotation: Rotate signing keys regularly; implement key versioning

Claim Management:

  • Minimize PII: Reduce personally identifiable information; claims are often logged
  • Namespace URIs: Use URIs for public claims to prevent collisions (e.g., https://myapp.com/claims/roles)
  • Keep payload small: Tokens are sent with every request; minimize size for performance
  • Validate “iss” and “aud”: Always validate issuer and audience claims match expectations

Best Practices:

  • Always validate signature first before any claim processing
  • Check expiration time before using token
  • Use HTTPS for token transmission
  • Implement audit logging for claim validation failures
  • Document custom claim types and their meanings

Standards & RFCs