Binding the authentication to the request
All domains, identifiers, and codes in these examples are fictional.
The kiosk asked for one thing before it contacted the photo service: your email address. Anyone can type that. It is on your business card and in every message you have sent.
Suppose someone at a kiosk in another shop, across town, types [email protected]. The kiosk is genuine, and so is the photo service, so your phone shows a genuine notification: Print kiosk wants to sign you in and view your photos. If you approve it, the person at that kiosk sees your albums.
A request you did not make
Device authorization had a similar risk, with one important difference. There, an attacker had to persuade you to open the verification page and enter their code, usually through a message that might look suspicious. In CIBA, the provider delivers the request itself, in its own app, looking exactly like the requests you approve. The only thing standing between a stranger and your photos is your decision on that screen.
Codes, links, and approval prompts described what happens when people receive prompts they did not expect. Some approve one out of habit, and a stream of them can wear a person down until they approve just to make them stop. A request that anyone can send by knowing your email address invites exactly that. Even if you decline every one, the prompts are a nuisance, and someone could send them simply to harass you.
One protection is already built in. Every backchannel request is authenticated, so the photo service knows which client sent it. It can limit how many requests a client sends for one account and cut off a client that misbehaves. That limits the damage, but it cannot tell your request from a stranger's when both come from the same kind of kiosk.
CIBA and its higher-security profiles add three further tools, which act at different points. A binding message helps you recognize your own request. A user code stops unwanted requests before they reach you. And a better hint makes you harder to name in the first place.
Matching the binding message
The kiosk sent binding_message=H4PX with its request and showed the same code on its screen. The photo service shows it on your phone as part of the approval: Approve only if the kiosk shows H4PX. The message binds the two devices together for this one transaction. You compare what is in front of you with what is in your hand, and approve only if they match.
If a stranger across town started a request for you at the same moment, your phone would show two requests with two different codes, and only one would match the kiosk in front of you. If you are not at a kiosk at all, there is nothing to compare with, and that is the moment to decline, just as with a device code that arrives in an email.
The specification asks for a value that lets you reliably tell that the two screens belong to the same transaction, such as a random code, and for it to be short and written in plain characters, since it has to fit small displays and be read by a person. A provider that finds a message unsuitable answers invalid_binding_message.
A binding message depends on you. It helps only if you actually compare it, and a tired glance at four characters is not much of a check. It is also chosen by the client, so it protects you from strangers using honest kiosks, not from a dishonest client that copies a code from a screen it can see. Where that risk matters, the FAPI profile for CIBA suggests carrying the code to the phone in a way that does not rely on the eye, such as scanning it as a QR code, or identifying the person with a value that starts on their own phone, as the last section describes.
A code only you know
A binding message helps you choose correctly among requests that have already reached your phone. A user code stops a request before it gets there. It is a secret you set up with the photo service in advance, such as a short PIN, and it is not your password. When the photo service supports user codes and your account has one, the kiosk must ask you for it and send it with the request:
POST /bc-authorize HTTP/1.1
Host: auth.photos.example
Content-Type: application/x-www-form-urlencoded
scope=openid%20photos.read
&login_hint=robin%40mail.example
&user_code=7193
&binding_message=H4PX
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6Imtpb3NrLTIwMjYtMTAiLCJ0eXAiOiJKV1QifQ...
The assertion is shortened here for display. The photo service checks the code before it sends anything to your phone. A stranger who knows your email address but not your code receives invalid_user_code, and your phone stays quiet. A request that leaves the code out for an account that needs one receives missing_user_code, which also tells the kiosk to ask for the code and try again. A four-digit code could be guessed, so the photo service should limit failed attempts for each account, as it would for any short code.
The photo service advertises support in its metadata as backchannel_user_code_parameter_supported, and a client records in its registration, as backchannel_user_code_parameter, that it can send one. The kiosk must never store the code. It asks you for it each time. Setting up and changing the code happen at the photo service, for example in its app, and are outside CIBA itself.
Not every client needs one. The specification suggests that a client that has already established a relationship with you before sending a request can go without, such as a website using CIBA to ask for stronger authentication after you signed in, or a call center that identified you by the number you called from. So can a client that does not use a fixed identifier, such as an email address, as its hint. A kiosk where anyone can type any address is the case user codes were made for.
Choosing a better hint
The root of the problem is the hint. An email address is a fixed, widely known identifier that works from any kiosk. It also has privacy costs: the kiosk learns it, and someone standing behind you can read it. The privacy section of the CIBA specification and the FAPI profile both point toward hints that are harder to borrow.
One option starts on your phone, where the photo service already knows you. The specification describes a design in which the photo service's app shows a QR code made for this one sign-in, and you scan it at the kiosk. The kiosk passes what it read to the photo service in a login_hint_token. Only someone holding your unlocked phone could produce that code, and it works once. The format of a login_hint_token is left to the deployment. The specification recommends that whoever issues one signs it, so the provider can tell it is genuine and unaltered, and a provider that receives an expired one answers expired_login_hint_token.
A client that has signed you in before can send that earlier ID token as an id_token_hint. With a pairwise subject identifier and no profile claims, it names you without revealing anything like an email address. The photo service accepts it only if it issued the token itself and the presenting client is in its audience. It may accept a token that has expired, since the hint identifies you rather than proving a recent sign-in. The kiosk would still need a way to find your earlier token, such as a loyalty card, and that brings trade-offs of its own.
Hints remain hints. Whichever one the kiosk uses, the photo service still authenticates you on your phone, and the decision is still yours. When you decline a request you did not start, the stranger's kiosk receives access_denied the next time it asks the token endpoint, and nothing more. If the request turns out to have been yours after all, you can simply start again at the kiosk.