Re: [Txauth] XYZ-08 vs XAuth-08

Justin Richer <jricher@mit.edu> Tue, 09 June 2020 16:10 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 050DE3A08FD for <txauth@ietfa.amsl.com>; Tue, 9 Jun 2020 09:10:52 -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_MESSAGE=0.001, RCVD_IN_DNSWL_BLOCKED=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 0ap5gyIO9c-s for <txauth@ietfa.amsl.com>; Tue, 9 Jun 2020 09:10:48 -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 051E33A08E7 for <txauth@ietf.org>; Tue, 9 Jun 2020 09:10:47 -0700 (PDT)
Received: from [192.168.1.14] (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 059GAeXd006642 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 9 Jun 2020 12:10:41 -0400
From: Justin Richer <jricher@mit.edu>
Message-Id: <3D57381A-DA98-4DD7-8960-A6D94A643E43@mit.edu>
Content-Type: multipart/alternative; boundary="Apple-Mail=_E550DFD4-59B0-4B17-A802-15F7C9BC8C6A"
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.80.23.2.2\))
Date: Tue, 09 Jun 2020 12:10:40 -0400
In-Reply-To: <CAD9ie-s_shGvafWoMwMEpvkJLQpUV5iS-eWKVtHMM5dxuyckPg@mail.gmail.com>
Cc: txauth@ietf.org
To: Dick Hardt <dick.hardt@gmail.com>
References: <CAD9ie-uH5Zun_jhiqnoP=Gye19TVyvgqa4b+Z=a3_Y830yqLtg@mail.gmail.com> <44332CBC-83B1-411C-B518-EE2F3D030301@mit.edu> <CAD9ie-sSr3NBe=d4y02J7kYzkHnm=VRQgfbr5oH3_zfKyzcKuQ@mail.gmail.com> <A1F8BEC9-A312-494B-8CE5-BE0422CA1C91@mit.edu> <CAD9ie-vA13jLONjNwbVbvwVgKYEQCCQTtDdfg66fs7hjtoBR7Q@mail.gmail.com> <01A1051E-30AB-419B-A0F1-C9AED6BFA357@mit.edu> <CAD9ie-s_shGvafWoMwMEpvkJLQpUV5iS-eWKVtHMM5dxuyckPg@mail.gmail.com>
X-Mailer: Apple Mail (2.3608.80.23.2.2)
Archived-At: <https://mailarchive.ietf.org/arch/msg/txauth/ROHSB3CqSFsiJbqDjOZ84s9v9OQ>
Subject: Re: [Txauth] XYZ-08 vs XAuth-08
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: Tue, 09 Jun 2020 16:10:52 -0000


> On Jun 8, 2020, at 5:33 PM, Dick Hardt <dick.hardt@gmail.com> wrote:
> 
> 
> 
> On Mon, Jun 8, 2020 at 1:02 PM Justin Richer <jricher@mit.edu <mailto:jricher@mit.edu>> wrote:
> 
> <snip>
>> A GS implementation can decide to only return an authorization from doing a GET on the AZ URL. Returning only an AZ URL is an option in XAuth. Similarly, we could do the same for a Grant URI. 
> 
> And that’s a lot of complex code paths for both the GS and client to deal with. With more ways that it might happen, the client has to be prepared for any of them — and get them all right. 
> 
> I don't see it being complex. The data either moves by reference or by value. Both parties will have to enable support by reference. Passing by value is an optimization so that the client does not have to make an additional call.

“Both parties will have to enable” is where the complexity comes into play. It’s putting a requirement on the client to anticipate several different ways to get the same information.

> 
>  <snip>
> 
>>  
>> 
>> Using a different URI, optionally, isn’t the problem, and that could easily be added to the. Removal of the separate handle is the problem I have with the XAuth approach.
>> 
>> In XAuth, the Grant URI is the GS URI + TBD + handle
>> 
>> Given we have asymmetric crypto as a requirement, it is unclear what having two pieces of random signal provide.
> 
> Asymmetric crypto is an implementation requirement in both the input drafts but it isn’t a requirement in the charter, and there are likely symmetric use cases and key proofing mechanisms that are going to be desirable for a lot of people.
> 
>>  
>> 
>>>  
>>> 
>>> Additionally, I find XAuth’s restrictions on the structure of the Grant URI potentially problematic, namely that it has to start with the server’s URL. This will lead to deployments needing to bend their setups with proxies or redirectors to make things fit, which you yourself have said is going to be an issue for things like supporting a short redirect URL vs. a long redirect URL. Your complaint there was one of latency and complexity, why does that not apply here? But most fundamentally, I do not see what value this restriction brings to the system. If the value is coming back from the GS endpoint, and the client is getting all of its pointers from there, what’s the point of dictating how that has to look?
>>> 
>>> Requiring the Grant URI to start with the GS URI is open to discussion. It is not clear to me why a deployment would need to have a redirector. Large scale deployments I have worked on have a router / proxy that routes requests to internal services. Having all the URIs start with the GS URI enables all GNAP operations to be routed in the same place.
>> 
>> You still haven’t addressed why you think that this is a reasonable requirement or assumption here but not when dealing with long/short URLs for QR codes. What is the difference?
>> 
>> Short URLs generally use a short host name as well as a short path. 
>> 
>> Most REST interfaces have the pattern I am proposing. A mental model many developers are familiar with. 
> 
> This is assuming a lot on the part of the GS implementation, still, and all of my arguments stand. 
> 
> 
> That is not entirely correct. A pre-registered client can still pass its key by value, and a dynamic client can still use a (dynamically-acquired) handle. In all cases, the client is identifying itself by its key. The difference is how the server looks up that key — it’s either from the handle, or it’s from the key value itself. 
> 
> I don't understand this. 
> 
> How is the Client authenticating that it is a specific pre-registered client?

The client is identified by its key. There is no external client identifier. I think you’re confused because you’re still thinking in terms of OAuth 2’s client ID based model and I am trying to move us past that. It took me time to realize that we really can let it go, so hopefully this is helpful in explaining why.

When a credential is cryptographically random enough to be unique, it can be used as its own identifier, particularly when you don’t need to identify the entity outside of the context that it can present and prove its credential. To stretch a metaphor, passwords don’t always need usernames. This is, after all, the driving design pattern behind OAuth access tokens. An access token doesn’t have a “username” portion to it, even though it would have been trivial to require the client to send its client ID alongside the access token. Why does that work? Because our model of what the RS does with the access token is built around it answering the questions: is this token valid and does it allow what’s being requested? If the RS needs to know which client was issued the token, it can discover that information without being told separately from the token -- perhaps it’s in the token itself or it’s in an introspection response. And in a lot of cases, the RS doesn’t care about the client software, it just wants to know if the token’s any good. Even in constrained tokens such as MTLS, PoP, and DPoP, we aren’t really authenticating the client at the RS so much as making sure the right key is presented alongside the right token. 

XYZ takes that same approach with the client talking to the AS and uses the credential (key) to identify the client as well as authenticate and protect the request. We can’t just say “OAuth 2 had client IDs and people are used to it so it must be good and we have to keep using it”. We need to ask WHY OAuth 2 needs client IDs and if that still makes sense. I argue that it doesn’t make sense anymore and we need to step back and look at a better model. OAuth 2 needs a client ID because it gets passed in the front channel and the client’s credentials can’t be used there. The access token doesn’t need an equivalent because it’s passed in the back channel and can be used directly. Now that the client no longer needs to be identified, but not authenticated, through untrusted third parties (such as the browser), we don’t need to have a separate identifier as part of the protocol. With an intent-based protocol, that starts in the back channel, you don’t need a client identifier anymore. Once the AS finds that key, the AS can then figure out what policies, rights, and restrictions are attached to that key. In many implementations, there’s going to be some kind of “registered client” object in a database somewhere that drives those policies. That’s the classical OAuth model, and it works in many cases. But in other cases, the key is going to just be a value used to protect the request chain (and possibly the token itself), and the policy is going to be built up by other things like a device posture or signature on the calling software or verified user information. It’s not just about client authentication, even though it can be used for it. DPoP, PKCE, and DynReg have shown us the value in dynamic systems, in different ways. 

On top of that, PAR is showing us that a lot of the constraints that we have in OAuth 2 don’t really apply anymore. For instance, Redirect URI restrictions can be relaxed because now you CAN identify and authenticate the software sending it, which isn’t true with a front-channel-first approach. All of the things that are required in OAuth 2 start to become redundant, and it leads to things like requiring that “client_id” still always be passed in the front channel even though the information could be looked up from an internal request_uri reference using PAR and JAR together.

That’s why a key handle isn’t exactly the same as a client ID and I did not call it a client ID in the XYZ protocol. It’s a shortcut to refer to the key material for certain optimized cases, but ultimately it’s pointing to a key. This has an interesting and beneficial side effect — if you HAVE a client ID as part of your internal data model, it can FUNCTION as a key handle because it’s a unique value the AS can use to look up key information. It’s not “authenticating the client”, it’s pointing to a key which in turn identifies and authenticates the software making the call. The fact that it’s a “client ID” is an artifact of the implementation, which in this case has an OAuth 2 legacy to work with. 

If we’re going to move past the constraints of OAuth 2 we need to stop thinking so strictly in its terms and models. There are better ways to approach this now and client IDs are not required by this protocol model.

>  
> <snip>
>>>  
>>> 
>>>> 5) Interaction Capabilities vs Modes
>>>> 
>>>> In XYZ, the capabilities are expressed by the client, and the GS states which capabilities it will accept. This can make it difficult for the GS to enforce the interaction choices as the client can mix and match which capabilities are returned. For example, a GS may not want to support a redirect without a callback to protect itself from session fixation attacks. In XAuth, the interaction modes provide clarity on the mode of interaction and the security properties. For example the GS may support both user_code and redirect modes, but not the indirect mode which is subject to session fixation attacks.
>>>> 
>>> 
>>> If the GS doesn’t want to accept a redirect without a callback, it can because the request will have a redirect, but not a callback, and it can reject the request. What makes you believe that this is not detectable or not possible in XYZ? 
>>> 
>>> I did not say it could not be done, I'm saying it is more difficult in XYZ than in XAuth
>> 
>> Can you explain to me how it’s more difficult in XYZ? I have explained how it can be done and I am failing to see why you think it’s harder.
>> 
>> Having both a handle and a URI seems more complicated than just a URI.
> 
> You’re conflating things: This is a separate discussion. Here we are talking about how it’s more difficult for an AS to determine the presence or absence of the “callback” field vs. detecting the presence or absence of the “direct” and/or “indirect” fields.
> 
> My bad. I got confused which topic we were on.
> 
> In XAuth, if the server wants to protect itself from a session fixation attack in a given request, and it wants to support both "redirect" and "user_code" modes, 
> the server will return only those two modes and not "indirect". The 
> 
> In XAuth, if the server wants to protect itself from a session fixation attack in a given request, and it wants to support both "redirect" and "user_code" modes, 
> the server MUST return callback, redirect, and user_code. The client does not know that the "indirect" mode is not supported, and may try that.
> 

In XYZ, if the server wants to protect against a session fixation attack, it will reject a request that doesn’t have a “callback” field in it. The AS always gets to choose which things it supports for any given request. If the client wants to support both “redirect” and “user_code’ modes AND has the ability to handle session fixation issues, it sends the “redirect”, “user_code’, and “callback” fields in its interaction request. 

>  
> 
>> 
>> If you think that having a URI in addition to a handle makes sense, how about you put that into your draft and see how it works?
> 
> Because I haven’t had an opportunity to implement it in this way yet, and I’m not yet convinced the extra complexity is worth it. I said it MIGHT make sense and it’s worth talking about, which has been my public stance since before you proposed XAuth. That’s still the case, and I’ve gone to length here on the list (which is under the IETF IPR rules) to discuss it. Are you suggesting that I need to prove this to you in a particular way, instead?
> 
> No. I'm saying that stating it could be done is different than writing it up. I would expect your draft to be the culmination of your convictions.

My draft is exactly that, and as you can see I’m open to having more discussions beyond it.

>  
> As this is an individual draft, it should represent what I, personally, think is the best approach. Part of my criteria for adding things to my individual draft is making sure it at least makes some amount of sense with real code. Your draft should likewise follow your own convictions.
> 
> Agreed. 
> 
>  
> 
>> 
>> For example: 
>> 
>> How is a transaction updated? 
>> How are separate access tokens refreshed? 
>> Refreshing an access token in XYZ returns a full transaction response per Section 9.3 which refers to Section 8.
>> 
>> 
>> By using URIs and methods, XAuth has an easy to understand API for CRUD operations on Grants and Authorizations.
>> 
>>     +--------------+-----------+--------+-----------------------------+
>>     | request      | http verb | uri    | response                    |
>>     +==============+===========+========+=============================+
>>     | Create Grant | POST      | GS URI | interaction, wait, or grant |
>>     +--------------+-----------+--------+-----------------------------+
>>     | List Grants  | GET       | GS URI | grant list                  |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Verify Grant | PATCH     | Grant  | grant                       |
>>     |              |           | URI    |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Read Grant   | GET       | Grant  | wait, or grant              |
>>     |              |           | URI    |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Update Grant | PUT       | Grant  | interaction, wait, or grant |
>>     |              |           | URI    |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Delete Grant | DELETE    | Grant  | success                     |
>>     |              |           | URI    |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Read AuthZ   | GET       | AZ URI | authorization               |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Update AuthZ | PUT       | AZ URI | authorization               |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Delete AuthZ | DELETE    | AZ URI | success                     |
>>     +--------------+-----------+--------+-----------------------------+
>>     | GS Options   | OPTIONS   | GS URI | metadata                    |
>>     +--------------+-----------+--------+-----------------------------+
>>     | Grant        | OPTIONS   | Grant  | metadata                    |
>>     | Options      |           | URI    |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>     | AuthZ        | OPTIONS   | AZ URI | metadata                    |
>>     | Options      |           |        |                             |
>>     +--------------+-----------+--------+-----------------------------+
>>  
> 
> While this looks good on paper, there are very pragmatic reasons that many APIs have moved away from purely RESTful patterns over the last decade, including limitations on what can be sent with GET and DELETE requests, for example. I don’t think it’s as clean a win as you’re presenting it, but I think it’s worth checking out.
> 
> Agreed that RESTful does not work for everything. It does look like it maps well here.

I disagree that it maps well. I think this is an over-application of a design pattern and the details will be problematic in implementation.

>  
> 
>> 
>>>  
>>> 
>>> The modes in XAuth are much more limiting, as the mixing of different interaction methods is already something that we need to start figuring out. Let’s say, for example, a client can do a redirect, accept a CIBA-style ping, or do a direct app2app communication. There’s a natural preference the client will have here: if it can talk to another app directly, it’ll try that first. If that doesn’t work, it can get a push notification sent, and if all that fails, it can pop open a browser. If I have to pick just one of those modes when I make the request, then the client needs to make three different requests to the AS before I get anything that works. 
>>> 
>>> Have you read the revised draft? As I noted above, I have added negotiation. The Client can state all the modes it wants, the GS can respond with the modes it will support, and the Client can offer the User any modes returned from the GS.
>> 
>> Yes, did you read what I wrote? I think we’re talking past each other.
>> 
>> This is not how XAuth is written currently. The Client can list all of the modes it wants to use. The Server will return all the modes that fit in its policy for the Grant Request. Why would the Client need to make different requests?
>> 
>> per
>> 
>> https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.4.2 <https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.4.2>  
>> 
>> The interaction object contains one or more interaction mode objects per Section 5
>> 
>> Three modes are defined here:
>> 
>> https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-5 <https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-5>
>> 
>> More modes may defined in extensions, or in this document.
> 
> Yes, this is an improvement, and I’m glad you’re moving your thinking in this direction. However, it’s still not as clear how things combine to solve different use cases, and it conflates the means of getting the user TO the interaction page with the way of getting them BACK from it. It’s these flexible combinations that I think are important, and I don’t think XAuth gets this quite right yet. 
> 
> I think how the user gets to and from the server is CRITICAL to the server if it wants to protect itself from a session fixation attack.
> See above where the client does not know what it can actually do that the server will allow.

It is critical that the server knows how to protect itself, yes. It’s not critical that the way there and the way back are tightly bound to each other in this way. I think that model is limiting.

>  
> <snip>
>> 
>> It is an OR in that if the client is using the type "oauth_scope", they cannot have an "authorization_details" attribute, they can only have "scope"
>> 
>> If the client is using the type "oauth_rich", the client MUST include "authorization_details", and MAY include "scope"
>>  
>> I have updated the the doc to better capture that:
>> 
>> https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.4.4 <https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.4.4>
>> 
>> and example 2 now is of type "oauth_rich"
>> 
>> https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.2 <https://tools.ietf.org/html/draft-hardt-xauth-protocol-10#section-3.2>
>> 
> 
> It was not clear to me that both could be sent with that mode, so thank you for updating that. But the client still has to choose one or the other up front. Why not have a mechanism that it can send both at all times? Why have a “mode” type switch at all? XYZ allows clients to make these combined requests with a single consistent syntax.
> 
> And by completely externalizing this to OAuth 2, I would argue that we lose an opportunity to more clearly define how resources are described and used, and we inherit the same combination issues that are facing RAR today. We can do better because we get to define the context.
> 
> This group can define a new syntax if it wants, and it will be unencumbered by the OAuth 2 and RAR legacy if deployments that want to use the OAuth 2 and RAR syntax can use them directly. Ie, there can be a type "gnap" or some such.
> 
>  <snip>
>> I don't follow how this would work. Perhaps you could provide an example in JSON?
> 
> One method is defining a new value inside the XYZ “claims” request object that maps to a specific sub-schema:
> 
> claims: {
>    “subject”: true,
>    “email”: true,
>    “oidc”: {
>        "userinfo":
>         {
>          "given_name": {"essential": true},
>          "nickname": null,
>          "email": {"essential": true},
>          "email_verified": {"essential": true},
>          "picture": null,
>          "http://example.info/claims/groups <http://example.info/claims/groups>": null
>         },
>        "id_token":
>         {
>          "auth_time": {"essential": true},
>          "acr": {"values": ["urn:mace:incommon:iap:silver"] }
>         }
>       }
> }
> 
> That should look pretty familiar. Alternatively, this could be done with a separate top-level request object field:
> 
> 
> claims: {
>    “subject”: true,
>    “email”: true
> },
> “oidc_claims_request”: {
>        "userinfo":
>         {
>          "given_name": {"essential": true},
>          "nickname": null,
>          "email": {"essential": true},
>          "email_verified": {"essential": true},
>          "picture": null,
>          "http://example.info/claims/groups <http://example.info/claims/groups>": null
>         },
>        "id_token":
>         {
>          "auth_time": {"essential": true},
>          "acr": {"values": ["urn:mace:incommon:iap:silver"] }
>         }
> }
> 
> In both cases the AS is going to need to knit them together into a sensible request policy, especially since the OIDC claims query language has overlapping implications to one particular resource, the UserInfo endpoint. My contention wasn’t with your proposed solution, my contention was you claiming that XYZ has a fixed schema and is therefore limited in extensibility.
> 
> One of the objectives of this work is to have extension points. My point was that XAuth had a clear extension point for adding schemas. How to extend XYZ was not clear in your proposal.
> 

I am sorry that the extensibility of the protocol was not clear. It is stated in each section that additional items can be added, and I have stated repeatedly that it’s extensible and demonstrated how, here on this list. 

> I think the XAuth proposal is better than the two examples you proposed. In your first example, the schema is a second class schema, and in your second example, claims are spread across to top level options. Both of these pollute other schemas.
> 

Not surprisingly, I disagree about the cleanliness of XAuth’s proposed approach. The proposal here adds external schemas as extensions instead of relying on them internally. 

If anything, I think that the OpenID Foundation would be the ones to define how to make an OIDC claims request using this protocol, not us, since they own and control that query syntax and everything it implies. We can provide a means of extension.

I also think there’s value in defining a set of core interoperable identity fields, which themselves could also be extended. 

All of these mechanisms should be controlled by some combination of registries and collision-resistant namespaces, which is the approach I’ve taken for extensibility throughout XYZ.

 — Justin