ASVS 5.0 V9.2.2 requires the receiving service to validate that a token is the correct type and meant for the intended purpose before accepting its contents. For example, only access tokens may drive authorization decisions, and only ID tokens may prove that a user authenticated. It is a Level 2 requirement.
Why it matters
An identity provider issues several tokens that look alike: ID tokens, access tokens, refresh tokens, logout tokens. They share a signing key and often a format. If an API accepts any validly signed JWT, an attacker can take an ID token (which the client app often sends around) and present it as an access token, or the reverse.
How to implement it
Make each token type distinguishable, then check it.
- For JWT access tokens, use the
typheader valueat+jwtas defined in RFC 9068, and require it. - Check claims that only exist on one type, such as
scopeorclient_idon access tokens andnonceon ID tokens. - Use a separate audience for each purpose.
const { payload } = await jwtVerify(token, jwks, {
issuer: ISSUER,
audience: "https://api.example.com",
typ: "at+jwt", // rejects ID tokens and other JWT types
algorithms: ["RS256"],
});
if (typeof payload.scope !== "string") throw new Error("not an access token");
On the client side, the reverse rule holds: never send the ID token to an API as a bearer credential, and never treat an access token as proof of sign in.
How to test it
Take an ID token from a normal login and send it to your API as Authorization: Bearer. Expect 401. Then send a refresh token. Expect 401. Then send an access token for a different API (see the audience requirement).
Common mistakes
- One verifier function used by every endpoint, accepting any signed token.
- Reading
emailfrom an access token as proof of login. - Skipping
typbecause the library does not check it by default.
Quick check
Can an API accept an ID token as a bearer credential?
No. ID tokens prove authentication to the client. APIs should accept only access tokens.