Re: [Txauth] Polymorphism (Was: JSON Schema?)

Justin Richer <jricher@mit.edu> Wed, 08 July 2020 20:02 UTC

Return-Path: <jricher@mit.edu>
X-Original-To: txauth@ietfa.amsl.com
Delivered-To: txauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id B6B453A07A1 for <txauth@ietfa.amsl.com>; Wed, 8 Jul 2020 13:02:17 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.895
X-Spam-Level:
X-Spam-Status: No, score=-1.895 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, HTML_FONT_LOW_CONTRAST=0.001, HTML_MESSAGE=0.001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id TFSxGltH51Nm for <txauth@ietfa.amsl.com>; Wed, 8 Jul 2020 13:02:14 -0700 (PDT)
Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 3882C3A07A2 for <txauth@ietf.org>; Wed, 8 Jul 2020 13:02:13 -0700 (PDT)
Received: from [192.168.1.7] (static-71-174-62-56.bstnma.fios.verizon.net [71.174.62.56]) (authenticated bits=0) (User authenticated as jricher@ATHENA.MIT.EDU) by outgoing.mit.edu (8.14.7/8.12.4) with ESMTP id 068K2BGH028128 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 8 Jul 2020 16:02:11 -0400
From: Justin Richer <jricher@mit.edu>
Message-Id: <F41A8F88-C1B4-4CE2-8573-7A03C086D25B@mit.edu>
Content-Type: multipart/alternative; boundary="Apple-Mail=_326DB65B-BB0B-4378-A729-BFDC20322A20"
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.80.23.2.2\))
Date: Wed, 8 Jul 2020 16:02:11 -0400
In-Reply-To: <CAD9ie-saoc2FUm46r4h1B27iYK04j_skf5-zJR7EXLmWBzj=hA@mail.gmail.com>
Cc: txauth@ietf.org
To: Dick Hardt <dick.hardt@gmail.com>
References: <CAD9ie-vnA98pobbboS00SAHneEG52_8eMxh_sE3r3jg6gyooGg@mail.gmail.com> <E9EC90C9-7A9A-4909-8627-A161B33E941F@mit.edu> <CAD9ie-vyB8+5jS=K_qUHfvxsF2wPV5APRo+7WUDfJxNzJONJpg@mail.gmail.com> <8CC8B466-FD6F-4C23-8DAA-99B8A9BDF548@mit.edu> <CAD9ie-u9z7Mc-wNjztoOTy4N_Z9jFDc2Sb6quLspasMGAMKdSw@mail.gmail.com> <097FB93E-96DA-4DF6-8511-0B32FD321211@mit.edu> <CAD9ie-tpuisauOFGiUj65-RcYPtcvW_gZP1CAadqq5cE6P36HQ@mail.gmail.com> <EE4A7D91-1106-44CB-92BF-C3AA3649BDFE@mit.edu> <CAD9ie-saoc2FUm46r4h1B27iYK04j_skf5-zJR7EXLmWBzj=hA@mail.gmail.com>
X-Mailer: Apple Mail (2.3608.80.23.2.2)
Archived-At: <https://mailarchive.ietf.org/arch/msg/txauth/L97ehMJqkq8v3LydVP6UOwJW14Q>
Subject: Re: [Txauth] Polymorphism (Was: JSON Schema?)
X-BeenThere: txauth@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: <txauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/txauth>, <mailto:txauth-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/txauth/>
List-Post: <mailto:txauth@ietf.org>
List-Help: <mailto:txauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/txauth>, <mailto:txauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 08 Jul 2020 20:02:18 -0000

On Jul 8, 2020, at 3:16 PM, Dick Hardt <dick.hardt@gmail.com> wrote:
> 
> I think representing the request as an array is simplistic, and complicated at the same time.
> 
> On the simplistic front, as there is no clear mechanism for extending the request with properties that apply to all of the request. 

The elements of the array are taken as a set, to be tied to the same resulting access token. If one of those elements is defined, by the AS and/or the RS’s it’s protecting, to apply across some or all of the other elements, then that’s up to the AS’s policy. Much like the “openid” scope in OIDC, which switches on all sorts of contextual stuff in the request. So to handle something like this, an AS can easily declare that a given scope-style string or a given object property applies to different parts of the request. You don’t need to externalize it here.

Do you have a concrete use case that requires that feature to be done in the way that you describe? I am trying to separate the driving use case from the proposed solutions to see what the differences are. 

> 
> Using JSON type polymorphism requires the AS to test each member of the array to determine if it is a string or an object. Only after detecting a RAR object does the AS know the client is making a RAR request.

That’s correct — but the AS needs to parse the whole resources request in order to figure out what the client is asking for, anyway, whether it’s by strings or objects or whatever else might be defined by an extension. Is there an argument for having an AS do an early dispatch on a request before it’s fully parsed everything?

> This also limits the request to be composed only of scope strings or RAR objects. I don't see how other strings or objects could be used in the array, so there is no clear extension point in the "resources" array for other query mechanisms.

That’s not the case in XYZ since we aren’t declaring that a string has to be an OAuth2-style scope. It can be, but ultimately it’s just a string that the AS can understand. And the objects are just that — objects. If the AS understands what the object is, it can be a RAR object or it can be something completely API-specific with another query language entirely. (Point, though: RAR already pretty much allows this by letting them be extended infinitely, a feature it inherits from XYZ)

I’m proposing that we do the same thing with GNAP: it’s an array of strings or objects and each of those means the same thing, “something the client is asking for”.

> 
> Just as RAR has a "type" property, I propose the "resources" ("authorizations" in XAuth) be an object, where the other properties are determined by the "type" property. This allows extensions to define new ways to query for an authorization rather than having to fit into scopes or RAR.
> 

It’s my stance that this is an unnecessary limitation at this level. The objects within the array should be typed, like RAR, but it doesn’t make sense for the overall request to be typed. Instead, there should be one “type" of query in the core, what XYZ calls the “resources” request. Other query languages should be added as extensions either to the RAR-style objects (by defining a type at that level) or as a separate top-level member.

For example, let’s take the OIDC “claims” query language. My current thought is that this really shouldn’t be a part of the “resources” or “claims” part of the request, but instead as its own top-level member, like it is in the OIDC protocol today. The main reason for this is the nature of the OIDC claims language: it specifies targets for the resulting data, and those targets cross different ways to return things. So it doesn’t actually affect just resources like the UserInfo Endpoint, or the ID Token, but both and potentially others out there. If your system supported such an extension, it could theoretically forego both the built-in “claims” and “resources” parts of the request, and use the “oidc_claims_query” member (or whatever it would be called). This would let such an extension use the OIDC claims processing mechanism as it is today.

To me, this remains much more understandable, extensible, and clean.

 — Justin

> /Dick
> 
> 
> 
> On Wed, Jul 8, 2020 at 10:46 AM Justin Richer <jricher@mit.edu <mailto:jricher@mit.edu>> wrote:
> I’m going to clip the personal attack here on the list and focus on the technical discussion, if that’s ok with everyone...
> 
>> 
>> wrt. your proposal to represent an authorization request as an array of scopes is overly simplistic. Both XYZ and XAuth represent the request as an object to enable a request richer than just an array of scopes. 
> 
> I’m not sure why you think it’s overly simplistic: the example I have given is valid XYZ syntax that would exist at the root of the client’s request. Yes, XYZ also allows objects within the same array, to allow for richer requests, but it does not require you do that. This is again one of the values of doing polymorphism in this particular way. It’s as simplistic or as complex as you need it to be, but it’s consistent in its model and presentation for both parsing and generation. So let’s take your example of “read” and “write” scopes. If a client needs to do this, but also needs to do a rich request, it can put them all together like this:
> 
> {
>   “resources”: [ 
>     “read”, 
>     “write”,
>     {
>       "type": "payment_initiation",
>       "locations": [
>          "https://example.com/payments <https://example.com/payments>"
>       ],
>       "instructedAmount": {
>          "currency": "EUR",
>          "amount": "123.50"
>       },
>       "creditorName": "Merchant123",
>       "creditorAccount": {
>          "iban": "DE02100100109307118603"
>       },
>       "remittanceInformationUnstructured": "Ref Number Merchant"
>    }
>   ]
> }
> 
> Notice that the “resources” element is an array here. The first two elements of the array are strings — scopes. The third element of the array is an object — the multi-dimensional request (this example taken from the RAR spec). But it’s always a “list of things that I want”. Some of those things are references as strings, some of them are multi-dimensional specific rich objects.
> 
> This has been in XYZ’s design and implementations for well over a year, and it works well. This is also the basis that I’ve used for implementing XYZ on top of an old OAuth 2 server, which doesn’t speak RAR or any rich requests but knows how to dispatch against scopes. 
> 
>  — Justin
> 
> 
>> /Dick
>> 
>> 
>> 
>> On Wed, Jul 8, 2020 at 7:03 AM Justin Richer <jricher@mit.edu <mailto:jricher@mit.edu>> wrote:
>> I’m glad that you’re looking at polymorphism as a possible solution to this, though I would contend that this particular style of polymorphism is not doing much more than pushing the mutual-exclusivity check down a layer instead of solving it. 
>> 
>> Using multiple types can in fact solve this problem, and several others, as long as you’re willing to let go of the syntax that OAuth 2 invented to solve a problem that we don’t have to solve here (passing an array-type value over the front channel). In XYZ’s syntax, the request for a single access token would look like this:
>> 
>> {
>>   “resources”: [ “read”, “write” ]
>> }
>> 
>> And the request for the multiple access tokens would look like this:
>> 
>> {
>>   “resources": {
>>      “reader": [ “read” ],
>>      “writer”: [ “write” ]
>>   }
>> }
>> 
>> I find this to be much simpler to parse and generate, as you no longer need to check for a specially-reserved field name (“type”), and you no longer have to do a sub-parse on one of the values to get what you really want (the space-separated scope string into a set). It’s also a lot simpler for the developers that need to write this.
>> 
>>  — Justin
>> 
>>> On Jul 7, 2020, at 7:30 PM, Dick Hardt <dick.hardt@gmail.com <mailto:dick.hardt@gmail.com>> wrote:
>>> 
>>> 
>>> 
>>> On Tue, Jul 7, 2020 at 3:40 AM Justin Richer <jricher@mit.edu <mailto:jricher@mit.edu>> wrote:
>>> I wanted to respond to this comment more fully:
>>> 
>>> > wrt. my authorization / authorizations oddness, polymorphism would not solve it as the contents of both authorization / authorizations in XAuth are objects. 
>>> 
>>> It’s not surprising that this is the case, as the XAuth protocol was not designed with polymorphism as a tool to consider. This is exactly the reason that I say we should have polymorphism in the toolbox from the start, as it allows us to avoid this kind of awkwardness in many cases.
>>> 
>>>  What evidence do you have to make this statement? "XAuth protocol was not designed with polymorphism as a tool to consider"
>>> 
>>> It sounds like you are saying I did not consider polymorphism in the XAuth protocol design.
>>> 
>>> I will restate my comment above about polymorphism. 
>>> 
>>> Using different JSON types does not solve the problem, but as I suggest in my comments, polymorphism of different JSON objects is one solution. An authorization, or a dictionary of authorizations. It has the restriction that the string "type" cannot be used as a label in the dictionary. An example:
>>> 
>>> {
>>>     "authorizations" {
>>>         "type": "oauth_scope",
>>>         "scope": "read write"
>>>     }
>>> }
>>> 
>>> {
>>>     "authorizations" {
>>>         "reader": {
>>>             "type": "oauth_scope",
>>>             "scope": "read"
>>>         },
>>>         "writer": {
>>>             "type": "oauth_scope",
>>>             "scope": "write"
>>>         },
>>>     }
>>> }
>>> 
>>> 
>>> I am looking at making this change in XAuth and in the implementation.
>>> 
>>> 
>>> 
>>> ᐧ
>> 
> 
> ᐧ