The Claim Inside the Claim: Decoding a Device Profile Hidden in a Payload
The challenge
A Brightloom store handset rang through a basket at well under the shelf price, and the audit shows the discount was applied by the till software rather than typed in by anybody. The device's scope claim says it may read the catalogue and take a sale, nothing more, and the colleague who was holding it has no discount permission on their own account. The permission is not coming from the person. It is in the handset's token, one level further down than anybody looked. Give the discount, as a percentage, that this device is silently entitled to.
What you'll learn
- Recognise an encoded document embedded inside a token claim
- Decode a nested base64 value and read its structure
- Convert basis points into a percentage
- Explain why two sources of permission in one token eventually disagree
- Describe why embedding a profile in a token makes it public to the bearer
Skills tested
Prerequisites
- What a JWT is and that its payload is encoded, not encrypted
How it works
A JWT payload is JSON, and JSON values are strings. Nothing stops a service from putting an entire second document into one of those strings, base64 encoded, and plenty do: a device profile, a feature flag set, a serialised session from an older system. The outer decode shows you the wrapper, and if you stop there the interesting half stays as one long opaque line that looks like something you are not meant to read.
It is not. Base64 is transport encoding. Anyone holding the token holds the profile, in full, and on a shared shop-floor handset that is a wide audience. Anything embedded this way should be treated as published.
The deeper problem is having two answers to one question. scope says the device may read and sell. ctx says it is a colleague tier with a thirty percent entitlement and price override flags. Both are signed, both are genuine, and whichever one a given code path happens to read is what that path enforces. Permissions belong in one place that every gate consults, not in a claim that the gates skip and the application trusts.
Common mistakes
- Answering 3000. That is the value of
discount_bps. Basis points are hundredths of a percent, so it is 30. - Stopping at the scope claim.
pos.read pos.saleis true and is also the reason nobody looked further. - Treating ctx as the signature. The signature is the third segment, after the second dot.
ctxis a value inside the payload. - Answering with a flag name.
pos.price_overrideis how the discount gets applied, but the label asks for the percentage. - Answering h.mbatha. That is who the handset was provisioned for, not what it is entitled to.
How to defend against it
Put entitlements in one place and keep secrets out of tokens entirely.
- Express permissions in
scopealone, and have the till derive every decision from it. If a device needs a discount entitlement, it belongs there where the gates can see it. - Do not embed profiles in bearer tokens. Reference them by id and resolve them server-side, so the handset carries a pointer rather than a document.
- Keep discount authority on the person, not the terminal. A device that grants thirty percent to whoever picks it up survives every staff change and every shift.
- Reconcile applied discounts against the authorising identity nightly. A discount with no approving colleague is a one-line query and would have caught this at the first basket.