skip to content

Your client validates RFC 9207's iss authorization-response parameter whenever it is present - why is that not enough?

level: seniorimportance: should knowfreq 40%

answer

  1. who answered, not who asked
  2. compare against the server you sent to
  3. simple string comparison, no normalisation
  4. absence is the bypass
  5. metadata says whether to expect it

basics

~20 s

An attacker can simply omit the parameter. A client must record, per authorization server, whether that server returns iss, and reject a response from such a server when the parameter is missing as well as when it does not match.

solid answer

~40 s

The `iss` authorization-response parameter carries the `issuer` identifier of the authorization server that produced the response. The client decodes it from the `application/x-www-form-urlencoded` response and compares it, by simple string comparison with no normalisation, against the issuer identifier of the server the request was sent to; on a mismatch it rejects the response and does not proceed with the grant. Validating only when the parameter appears turns that into an optional check an attacker controls, because a crafted response can leave it out. The client therefore needs per-server state - a server advertising `authorization_response_iss_parameter_supported` as true must always produce it, so its absence is itself a rejection, while a server that does not support it needs a different mix-up defence.

code

http · 4 lines
http
HTTP/1.1 302 Found
Location: https://timetable.example/callback?code=SplxlOBeZQQYbYS6WxSbIA
    &state=af0ifjsldkj
    &iss=https%3A%2F%2Fauth.partner-a.example

go deeper

for a junior

Recall that one parameter in the authorization response names the authorization server that produced it, and that the client compares it with the server it sent the request to.

for a middle

Explain the comparison precisely: decode the form-encoded value, compare it as a plain string with no normalisation, and reject the response rather than continuing when it differs.

for a senior

Demonstrate the per-server state a real integration needs - which partners owe the parameter, what a missing value means from each, and that a mismatch abandons the grant rather than being logged and waved through.

for a principal

Own the mixed estate: some partners support the parameter and some never will, so decide what structural attribution you require of every integration before a partner is onboarded rather than per client.

## What the parameter carries RFC 9207 adds one parameter, `iss`, to the authorization response. Its value is the **issuer identifier** of the authorization server that produced that response - the same string that appears as `issuer` in the server's own metadata. It answers the question the rest of the response cannot: *which server is speaking*. Two obligations sit on the server side and the client depends on both. The server's `issuer` metadata value **must be identical** to the `iss` it returns, and a server that supports the parameter advertises `authorization_response_iss_parameter_supported` as `true` so a client can know in advance to expect it. ## How the comparison is made 1. Take the authorization response's parameters and decode them from `application/x-www-form-urlencoded`, so the percent-encoded value becomes the issuer identifier itself. 2. Compare that value with the issuer identifier of the authorization server **the request was sent to**, using simple string comparison as RFC 3986 section 6.2.1 defines it - no case folding, no default-port removal, no trailing-slash adjustment, no re-encoding. 3. On a mismatch, reject the response: do not exchange the `code`, and do not continue the grant against either server. The direction in step 2 is the whole point and it is easy to state backwards. The comparison is against the server the client *asked*, held in the client's own record of this flow. Comparing `iss` against something inside the response, or against the set of all servers the client trusts, checks nothing an attacker cannot satisfy. ## Why validating only when present is a bypass The authorization response reaches the client through the browser, so its parameters are whatever the party producing the redirect chose to include. A check written as *if `iss` is present, compare it* is a check the attacker turns off by omission. What makes the check binding is the client knowing, per authorization server, whether the parameter is owed to it. | Server supports `iss` | Parameter present | Client behaviour | |---|---|---| | Yes | Yes, matching | Continue the grant | | Yes | Yes, different value | Reject; the response came from another server | | Yes | No | Reject; a response that owes the parameter and omits it is not acceptable | | No | Either | The parameter settles nothing; another mix-up defence must be carrying the flow | That last row is the one that turns a parameter check into per-server state. A client federating with a mixture of partners must remember which of them supports the parameter, because the check is only as strong as its ability to notice an expected value that did not arrive. Where a partner does not support it, the flow needs another way to attribute the response - the usual one being a separate redirection endpoint per authorization server, so the callback path itself names the flow. ## Error responses are responses too The check is not only for the success case. An authorization error response arriving at the redirection endpoint is equally unattributed, and a client should not assume it came from the server it asked. Treating errors as exempt gives an attacker a free channel into the client's flow-state handling - a way to have a flow abandoned, retried or redirected on someone else's say-so. ## Required defence, recommended mechanism The two statements in the specifications are worth keeping apart, because promoting one of them teaches a false certainty: - **A mix-up defence is required** for a client that can start flows at more than one authorization server. There is no reading in which such a client may simply trust the response. - **The `iss` parameter is the recommended mechanism.** RFC 9700 says clients should use it; the alternative it leaves open is distinct redirection endpoints per authorization server. A client that has neither is not merely unconventional, it has no defence at all. ## What the check does not do The parameter attributes the **response**. It does not attest to anything the response carries: the `code` is still a one-time value to be exchanged, and the `iss` parameter in an authorization response is a different thing from the `iss` claim inside an issued token, which is validated separately and by a different party. Reading one as the other is the most common way this check ends up satisfying nobody.

  • What must an authorization server guarantee about its own issuer identifier?
    That the `issuer` value in its metadata and the `iss` it returns in an authorization response are identical strings. A client compares them without normalising, so a server that publishes one spelling and returns another breaks every conforming client's check while looking correct to a human reader.
  • Does the check apply to an authorization error response as well?
    Yes. An error response arriving at the redirection endpoint is as unattributed as a successful one, and a client should not assume it came from the server it asked. Exempting errors hands an attacker a way to steer a client's flow handling without producing a single valid code.
  • A partner authorization server does not support the parameter at all. What now?
    The flow still needs a mix-up defence, so the client supplies attribution structurally: a distinct redirection endpoint per authorization server, so the callback path identifies the flow. The per-server record also stops the client silently treating that partner as if a missing `iss` were normal everywhere.
  • How is this parameter different from the iss claim inside a token?
    The response parameter attributes the authorization response on the front channel and is checked by the client against the server it asked. The claim inside an issued token attributes the token itself and is validated wherever that token is accepted. Different carrier, different checker, different moment.

saying these in an interview costs you the question

  • Compares iss against whatever the response itself claims
  • Accepts a response with no iss because nothing can be compared
  • Normalises both strings before comparing them
  • Treats an authorization error response as exempt from the check
  • Confuses the iss response parameter with the iss claim in a token