Subject: [Txauth] "Compatibility" with OAuth 2

From: Justin Richer <[email-masked]>
Date: Fri, 15 May 2020 09:35:19 -0400

While one of the key aspects of the work here is going to be that we’re not bound by OAuth 2’s syntax or its exact semantics, it will still be important for many people to be able to run TxAuth and OAuth2 in parallel with each other, at least for some time. That’s why our proposed charter text includes language around easing the transition, but does not require compatibility. The hard part of course is balancing re-use without dragging in the baggage of older concepts that we’re trying to move past. After all, if you just want to re-use all of OAuth 2 … just go use OAuth 2. It’s not going away any time soon!

This was an important goal in my initial designs of XYZ from the start, and if you’ve seen any of my presentations on it you’ll know that even though the building blocks are different, the key parts of OAuth 2 can map to XYZ’s protocol. Back in the day, I had a number of projects that I had to support both OAuth 1 and OAuth 2 on simultaneously, and so I was very aware of the impact these design decisions would have on developers. I realize that this has been hard for people to see, because the constructs in XYZ all look very different from those in OAuth 2.

This last week I finally sat down with an old code base, MITREid Connect, to prove that point. MITREid Connect is an implementation of OAuth 2 and OpenID Connect in Java, and I set out to add XYZ support to the AS. The results were really encouraging. After only a couple days of on and off hacking, much of which was around getting an old code base to be happy in my modern dev environment, I was able to hook up the XYZ native Java client to successfully interact with the MITREid Connect AS and get an access token. More importantly, the changes to the MITREid Connect code were fairly minimal. This uses the existing client, scope, key, and access token objects. It even re-uses the approval form that the authorization endpoint uses, but the authorization endpoint code itself is bypassed in favor of a new service that knows how to deal with the pending transaction. 

I used a statically registered client with a public key registered at the server as the basis for this. The XYZ client was configured to send the static client ID as its key handle and to send the set of scopes that it was registered with as resource handles. The AS simply looked at the values in these fields and mapped them to the existing data objects in order to do the processing. A new table was added to store pending transaction objects, but no other data model changes were made. All of the OAuth 2 functionality in the server is completely unaffected, and if you talk OAuth 2 with the same registered client information it will work exactly as you’d expect it to.

If you’re interested, you can see the code here:

https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/pull/1536/files <https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/pull/1536/files>

A few things to note that aren’t quite all the way there, since this was a proof of concept:

 - A bunch of the code changes in the pull request above are just to make the old code base play nice with Java 14, since it was previously written for Java 8 (and originally for Java 6, that’s how old this project is). That was way more painful than I expected it to be.

 - Right now it’s hardcoded to only support the JWSD key-proofing type, but that could be made switchable with an additional field on the Client objects or by expanding the Client object’s “token endpoint auth method” field to cover XYZ’s additional methods. I would probably do the latter if I were to add this functionality to this branch, though it would mean having to choose between OAuth 2 and XYZ functionality on a per-client basis. Other implementations might have different needs here.

 - It only does the redirect based flow, the equivalent of the authorization code flow. The AS does have support for the OAuth 2 Device Flow so that can probably be added.

 - MITREid Connect doesn’t handle RAR style requests, so XYZ’s rich authorization objects are ignored as well. If we added RAR support it could be mapped directly to this using the same patterns as the scope processing. In fact, I even re-used our existing scope processing services for this experiment. 

 - It matches redirect URIs based on a prefix pattern. While this is problematic in the OAuth 2 world, we’re even seeing that with PAR and an authenticated client these kinds of restrictions can be relaxed, and XYZ is based on that same concept. 

 - It doesn’t do the dynamic registration piece yet. MITREid Connect supports OAuth DynReg (and the management extension) though so it wouldn’t be a big step to add support in a parallel fashion.

 - It does the parsing by hand using GSON instead of pulling things in through the object handling system in Spring. This is actually not really out of line for how MITREid does much of its API, though, so not actually a big change there. 

 - I strongly believe that a successful protocol at this level shouldn’t depend on everyone using the same library. Therefore, I didn’t pull over the XYZ java code as a library or dependency. There were a couple bits of functionality that I did copy over, and they were both security library calls: the hash calculation functions that deal with the interaction hash, and the JWSD signature check. Both were copied over to the MITREid Connect codebase and adapted to that environment.

All in all, this experiment proved to me that the concepts we’d built in to XYZ to allow for bridging from OAuth 2 are workable and sound. I was really happy to see it come together, and I’d be interested in seeing how anyone else does this.

 — Justin