Comments back to “Transaction Authorization or why we need to re-think OAuth scopes” by Torsten

Torsten Lodderstedt’s excellent blog titled “Transaction Authorization or why we need to re-think OAuth scopes” has been floating around for a few weeks. I always meant to have written this earlier, but did not have a chance, so here is my first attempt.

I. On the Claims parameter in OpenID Connect

OpenID Connect’s claims parameter actually came in quite late. We had Request Object (aka JWT Authorization Request) before it and the Claims parameter came in to make the picture complete so that encapsulating the authorization request simply into JWT will result in the Request Object.

The rationale for having claims parameter in the Request Object was that we as the WG determined that the introduction of structured syntax to scope was going against the philosophy of “space delimited strings” in RFC6749. So, instead, we decided to do what seemed minimally invasive to the implementations by introducing a scope called “openid” as a switch so that the implementations can switch the behaviour.

After the implementations detect “openid” in the scope, it was intended that the further scopes were to be interpreted in the “openid” way. In particular, it should look for claims parameter.

Claims parameter is a generic mechanism to pass potentially dynamic values to the Authorization Server. While OpenID Connect defined a few additional scope values such as profile, they are intended to be just an alias to defined claims structure, at least in spirit.

For more information about it, please refer to “Scopes And Claims In OpenID Connect” (2012-01-26).

II. Request Object / JWT Secured Authorization Request

A bit of history to start with: OpenID Connect started with a use case called Transaction Exchange (TX), which later was changed to Contract Exchange (CX). The CX working group is still there for us to go come back and complete the work.

Because Transaction or Contract Exchange was the original use case, it was quite clear that we had to have a signed authorization request and signed response. The Request Object took care of the former.

It is quite easy to imagine that the Request Object gets quite bulky as it may include various parameters that are to be included in the contract. So, sending it in the Authorization Request as part of the URI parameter in the HTTP redirection was quite unrealistic. Thus, we needed a facility to pass the Request Object to the Authorization Server by reference, aka “artifact” and such protocol binding was called “Artifact Binding” in SAML. Thus, a Working Group at OpenID Foundation called “Artifact Binding WG” was formed. This is the OpenID Connect WG now.

While you may have gotten an impression that the Request Object is to be stored in the Client from Torsten’s article, in fact, OpenID Connect deliberately does not specify where the Request Object should be stored. Section 6.1 of OIDC just says:

The Client stores the Request Object resource either locally or remotely at a URL the Server can access. 

(Source) https://openid.net/specs/openid-connect-core-1_0.html#CreateRequestUri

The part “The Client stores” above is just describing the fact that the client makes an action to store the request object and does not mean that it needs to be stored in the client. In fact, in the case of a pure mobile app, it cannot obviously stored in the client as the authorization server then cannot access it to retrieve the content referred by the reference.

There are three potential locations that could serve as the Request Object.

  1. Authorization Server
  2. A third party provider
  3. The client

However, OIDC does not specify how to store the request object in those locations. This is what needs to be further specified as an option. The reason we did not specify it in the OIDC Core is that there can even be a “manual way” of storing those request through a web page provided by the authorization server or a third party provider, where the content of the request object may be static. (Note we had state value excluded from the Request Object for the purpose. This is the delta between the OAuth JAR and OIDC.)

You might think the manual creation of a request object is absurd. It actually is not when you think about the legally binding contracts. For a digital signature, you are typically required in many jurisdiction that to sign it in person using your own certificate, such as that of eIDAS.

III. Pushed Request Object

Pushed request object described in FAPI Part 2 is an attempt to define the pattern 1 above. Sending the authorization request (request object) to the authorization server is the most logical pattern where the client can directly communicate with the Authorization Server. (Note that this is not always possible, e.g. the case of the AS being behind the firewall or running as the mobile app. )

As to the method of client authentication, we had two options: being explicit or doing it implicitly with the client signature over the Request Object. We have chosen the later as it will reduce the implementation burden.

In Torsten’s article, this approach is evaluated of having the following disadvantage:

But the pushed request object as described so far still does not overcome one of the key disadvantages of the “scope-specific URI query parameter” pattern: It requires the authorization server to be aware of application specific relationships between scope values and URI query parameters (e.g. “payment” & “payment”) making a fully generic implementation of this pattern in OAuth products difficult to achieve.

(Source) https://medium.com/oauth-2/transaction-authorization-or-why-we-need-to-re-think-oauth-scopes-2326e2038948

From the OpenID Connect point of view, I have some reservation against the statement.

First, since the payload will include an application specific content, the Authorization Server needs to be aware of the context and associated parameters. So, understanding the context should not be a problem.

Second, you do not have to map the scope and the parameters endlessly. In fact, in case of OpenID Connect, there is only one parameter: claims. The intent was that any extension goes into the protocol underneath it. The logic at the AS then is:

  1. First, look for openid in the scope value.
  2. If it finds it, transition to OpenID Connect mode and look for claims parameter. (There are several shortcut scope value in lieu of the associated claims parameter but they are just there for convenience.)

Similar conventions can be used for other applications. Or, why not just write as the sub-application of OpenID Connect? Then you do not need to extend the authorization parameter at all.

IV. Structured scope

By now, you must have become aware of it. Yes. There is a structured scope in OpenID Connect. The name of the parameter is called “claims”, and the switch to tell that the application is using a structured scope1 is a scope value “openid”.

Why do we need a new parameter structured_scope then? 😉

We designed them back in 2011. The world is finally catching us up!

Footnotes

  1. There is another behaviour change imposed. The responses are now being signed. ID Tokens are the detatched signature for the response. Without it, we cannot complete the original use-case of Transaction Exchange/Contract Exchange.

4 Replies to “Comments back to “Transaction Authorization or why we need to re-think OAuth scopes” by Torsten”

  1. Hi Nat,

    thanks for your review of my article. You gave very interesting background on rationale and evolution of some OpenID features.

    Let me comment on some of your comments:
    – request objects: all implementations I’m aware of host the request object in case of request _uri at the client side. In case of a mobile app this will indeed e the backend of this app.
    – claims as structured scope: the claims parameter, as the name suggests, is intended to carry claims definitions to the OP, e.g. requesting the claim “address” to go into the ID Token. The claims parameter’s syntax is not suited as general purpose scope transfer mechanism. Moreover, using claims requires to turn on OpenID Connect, which might not be necessary for the use case. As a subtle consequence, turning on OpenID requires the AS/OP to issue an ID Token containing a user id in the “sub” claim. What if the AS doesn’t want to assert a user id, like in the case of PSD2 ASPSP’s? It needs to make up a sub claim value that’s not a user id but something else, like in UK Open Banking.

    I think a structured scope would be a more generic and lightweight solution.

    1. Thanks, Torsten, for comment back.

      > request objects: all implementations I’m aware of host the request object
      > in case of request _uri at the client side. In case of a mobile app this will
      > indeed e the backend of this app.

      It would actually be good if I could know which implementation.
      One of the implementations that I did actually stored request objects in a third party location.

      > The claims parameter’s syntax is not suited as general purpose scope transfer mechanism.

      How so?
      Having something like:
      “claims”:{
      “payment”:{
      “instructedAmount”:{
      “currency”:”EUR”,
      “amount”:”123.50″
      },
      “debtorAccount”:{
      “iban”:”DE40100100103307118608″
      },
      “creditorName”:”Merchant123″,
      “creditorAccount”:{
      “iban”:”DE02100100109307118603″
      },
      “remittanceInformationUnstructured”:”Ref Number Merchant”
      }
      }

      seems perfectly fine. It is a “claim” for “payment”.

      > turning on OpenID requires the AS/OP to issue an ID Token containing a user id
      > in the “sub” claim. What if the AS doesn’t want to assert a user id,
      > like in the case of PSD2 ASPSP’s?

      Issuing ID Token is needed to achieve the response signing.
      If it were the user who is authorizing it, there has to be an identifier that identifies the user. The only requirement to the subject in ID Token is that it needs to be unique and never re-assigned within the issuer. That means, it can be ephemeral as well so that the client cannot track the user. In this case, `sub` claim value actually acts as something called partially unlinkable identifier (cf ISO/IEC 29191), which is a really good property to have in financial transactions etc. This is not an afterthought. It was thought through during the design process in parallel to the standardization of ISO/IEC 29191 that was published in 2012.

      In the case of Open Banking UK, they seem to be using the transaction number, which is fine. What’s important is to be able to trace back to the user who authorized the transaction if some problem happened.

  2. Hi Nat,

    I just realised I missed an important piece of my proposal: I’m proposing to replace/complement the simplistic scope parameter by a structured scope that is able to convey complex authorization request data. So instead of a simple scope value in combination with an additional JSON-based parameter or including a reference to some external resource there would be just a single self-contained scope object. I think that will greatly simplify to work with rich authorization request data.

    This new structured scope will allow clients to add multiple (typed) JSON objects carrying application-specific authorization request data. The AS/OP is supposed to use the type information to defer processing of a particular JSON object the respective application-specific module.

    Here is an example:

    “structured_scope”:{
    “sign”:{
    “type”:”signingapi.org/schema/sign”,
    “credentialID”:”qes_eidas”,
    “documentDigests”:[
    {
    “hash”:”sTOgwOm+474gFj0q0x1iSNspKqbcse4IeiqlDg/HWuI=”,
    “label”:”Mobile Subscription Contract”
    }
    ],
    “hashAlgorithmOID”:”2.16.840.1.101.3.4.2.1″
    },
    “payment”:{
    “type”:”financialapi.org/schema/sepa-credit-transfer”,
    “instructedAmount”:{
    “currency”:”EUR”,
    “amount”:”123.50″
    },
    “debtorAccount”:{
    “iban”:”DE40100100103307118608″
    },
    “creditorName”:”Merchant123″,
    “creditorAccount”:{
    “iban”:”DE02100100109307118603″
    },
    “remittanceInformationUnstructured”:”new Smartphone”
    }
    }

    1. My question then is this:
      Why cannot we just use “claims” instead of “structured_scope”?
      They are isomorphic.

Leave a Reply

Your email address will not be published. Required fields are marked *