Definition
When you receive a check made out to “John’s Hardware Store,” you can’t cash it at “Jane’s Coffee Shop” - even if it’s signed by a legitimate bank. The check specifies who it’s intended for. JWT tokens have a similar concept called “audience” - a field that specifies which service or API the token was issued for.
The audience claim (abbreviated as “aud” in JWT) prevents a token from being used in places it wasn’t intended for. Let’s say you log into a company portal and get a token for accessing the HR system. That token shouldn’t work if someone tries to use it at the finance system, even though it’s a valid token from the same company. The audience claim ensures tokens stay in their intended lane.
This might seem overly cautious, but it prevents a whole class of security attacks. Without audience validation, an attacker could steal a token from a less-secure service (like an internal wiki) and use it to access a highly-sensitive service (like payroll). With proper audience checking, the payroll system rejects the wiki token because it was never intended for payroll. It’s like each door in a building only accepting keys made specifically for that door.
Example
Multi-Service Company: A company has separate APIs for billing, inventory, and shipping. When you authenticate, the auth server issues a token with "aud": "https://billing.company.com". If someone intercepts this token and tries to use it against the inventory API, the inventory API rejects it - wrong audience. Each service only accepts tokens explicitly intended for it.
Third-Party Integrations: When you connect your bank account to a budgeting app like Mint, the bank issues a token with a specific audience (the budgeting app’s identifier). That token can’t be used by a different app, even another legitimate financial app. The audience restriction limits the token’s scope.
Mobile and Web Apps: A company might have different token audiences for their mobile app vs. web app. The mobile app’s tokens have "aud": "mobile.myapp.com" while web has "aud": "web.myapp.com". This lets them apply different security policies to each platform.
Microservices Architecture: In a system with dozens of microservices, tokens include audiences for specific services. The order-processing service only accepts tokens with its audience. Even if an attacker compromises the notification service and steals tokens, they can’t use those tokens to access orders.
Partner API Access: When you integrate with a partner’s API, they issue tokens with their service as the audience. You can’t take that token and use it with a different partner’s API - the audience mismatch causes rejection.
Analogy
The Concert Ticket: A ticket for the Friday night show at Madison Square Garden only gets you into that specific show at that specific venue. You can’t use it for Saturday’s show (wrong time) or at a different arena (wrong audience). JWT audience works the same way - the token is only valid for the specific “venue” (API) it was issued for.
The Addressed Envelope: When mail is addressed to “123 Main Street,” it should only be delivered there, not to 456 Oak Avenue. Even if the letter has a valid stamp and was properly sent, delivering it to the wrong address would be wrong. The audience claim is like the address - it specifies the intended recipient.
The Prescription: A prescription is written for a specific pharmacy or chain. You can’t take a Walgreens prescription to CVS and expect them to fill it. The prescription specifies its intended audience, and other pharmacies won’t accept it even though it’s a legitimate prescription.
The Company ID Badge: Your company badge might get you into your building but not into a different company’s building, even if both use the same badge system. The badge is programmed for a specific audience (your company’s building). Each building only honors badges made for it.
Code Example
// Audience validation in JWT middleware
const jwt = require('jsonwebtoken')
const validateAudience = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401).json({ error: 'No token provided' })
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
// CRITICAL: Always validate audience
audience: 'https://api.myservice.com',
// Also validate issuer
issuer: 'https://auth.myservice.com',
algorithms: ['RS256'] // Prevent algorithm confusion
})
req.user = decoded
next()
} catch (err) {
if (err.name === 'JsonWebTokenError' && err.message.includes('audience')) {
return res.status(403).json({
error: 'Token not intended for this service',
details: 'Audience mismatch'
})
}
return res.status(401).json({ error: 'Invalid token' })
}
}
// Multi-audience support (when token is valid for multiple services)
const validateMultiAudience = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
// Accept token if ANY of these audiences match
audience: [
'https://api.myservice.com',
'https://api-v2.myservice.com'
]
})
req.user = decoded
next()
}