Also known as “XIVAuth has garbage design”, “which idiot made this API?”, “why did you think it was a good idea to put data in your authentication layer”, and similar appropriate titles.
It has been brought up a few times that XIVAuth is Not Quite OAuth2. This is correct. XIVAuth’s use of OAuth2 is an implementation detail to make the service feel more “usable” in a few cases (at least, in the developer’s opinion). It is far better to think of XIVAuth as being UMA-lite, OpenID-lite, or even more appropriately as “Discord-Flavored SSO.”
While the use of a UMA-lite/Discord-like authentication process may seem cumbersome at best and a significant design flaw at worst, it was chosen for a few reasons:
- It is easily extensible for future ideas. XIVAuth will eventually support the ability to manage characters and even log in to plugins from inside the game itself (Dalamud Plugin Pending), which facilitates the use of a full API. OAuth serves this purpose very well and makes it very easy to extend.
- It allows services to request information even after authentication. If XIVAuth were to ever offer additional data-driven services (e.g. reporting information about a player’s free company or rank), the same familiar API routes and scopes would be usable for these new features.
- XIVAuth has no plans to provide API or data services that fall outside the scope of a specific user or character. As an example, if free company information were to be added, XIVAuth would not start advertising
/freecompany
API routes for general FC information.
- It’s already relatively familiar. Discord’s OAuth2 authentication process is extremely well documented and sample code is widely available. It is relatively trivial to adapt an existing version of a Discord OAuth2 client (which exist for virtually every language out there!) to bring XIVAuth support to a new application.
- While, yes, OpenID would offer much the same benefit (and even offer the ability to just use a well-written library), OpenID does not provide the same level of extensibility at the same level of ease (especially around refresh tokens).
- In perhaps in its most fatal design flaw, XIVAuth’s OAuth2 implementation does not follow the paradigm of “allow an app to act on behalf of a user,” (that is, the relying party/client is not the same the resource owner/user). Issued bearer tokens are (in almost all cases) representations of a specific app’s access to a user’s data, as defined by that user. Scopes define what resource types the application wants, but the user decides what resources the application gets. This is somewhat counter to how OAuth2 works, but is essential to a couple of the operating principles of XIVAuth.
- As discussed above (and below, and basically throughout this entire page), I suspect that this will be (mostly) a non-issue to most developers wishing to integrate with XIVAuth.
- This is a very good argument in favor of doing OpenID proper and not making a weird half-baked affront to the specification. This is, again, a good idea. It does, however, make certain secondary goals of the application harder or less useful.
- Also, in my opinion, this is a silly problem with OAuth2. Say I want to use a service to print photos, and they offer an easy “Sign in with Google” button so that I can import photos from Google Photos. I don’t necessarily want to share all my photos with that third-party service (nor do I necessarily trust that service to go look at photos they have no reason to look at), but “OAuth2 as described” would require me grant access to my entire photo library because the intent is to allow the service to act on my behalf.
I do not expect the decision to use a non-standard system to be popular, and I’m certain it has flaws. These flaws will be addressed when they come up to the best of my ability, and I very much do want to provide full OpenID support in the future, but that will not be a thing in the initial release of XIVAuth (whenever that is meant to happen).
Despite these paradigms and shortcomings, I expect that XIVAuth will feel natural and relatively easy to integrate into existing (or new) codebases. My goal for designing the authentication service this way (and the API this way) is that a developer will still find it easy to set up and use, even if it’s not to the letter of the relevant standards.
And because FAQs are fun:
- Why not just use UMA proper?
Did you know about UMA’s existence before this page? Neither did I. Additionally, there are really… not many libraries out there that support UMA, but a ton that support OAuth2. Seeing as OAuth2 client applications can usually deal with the UMA-like paradigm without too much pain (and it’s easy for me to make my server deal with the same paradigm), it makes sense to just piggy-back off of a (much more) widely known standard.
- Doesn’t not implementing UMA as described cause security issues?
I don’t think they’re notable security issues by any sense of the word, but if you can prove me wrong, by all means report it to me. I want this application to be as secure as it can be.
- But some things in your app (like the management API) are “real” OAuth2?
Yes. In cases like the game plugin providing XIVAuth account management, this behaves just like any sane person would normally expect OAuth2 to behave. The UMA-like layer is used only when certain scopes aren’t present (
character
has the UMA engine applied, character:manage
doesn’t).
- How do I use the management scopes, then?
In most cases (e.g. the intended use case for XIVAuth: authenticating users and characters), you will not need it. If you do, however, have a use case that needs
*:manage
, just reach out and I’ll enable it for you. As per normal OAuth, your client will be able to choose if it actually wants to request that scope or not.
- Shouldn’t you have separated the authentication API and the management API?
Probably. A lot of logic is shared between the two though, so I made a decision to combine them.
- How does the Plugin JWT stuff fit into this?
It’s basically OpenID without actually complying to OpenID. It’s also a bit more limited than OpenID and is built for a specific purpose. If/when I actually add proper OpenID support, I will re-evaluate how plugin JWT is done.
- Why not just use standards for everything? Why your own JWT rather than OpenID? Why even have an Authentication API rather than OpenID everywhere?
Standards are a good idea and I should have just used them. Standards are, however, sometimes a bit limiting and certain libraries don’t like it when you try to do something not exactly how the library implementer envisioned. Sometimes (usually) the library implementer is right, and this would have probably been one of those times where any reasonable person would look at the standards and libraries and figure out a compliant way to do it. But where’s the fun in that? (Also, I have a suspicion that the relying party’s side would be more complex if XIVAuth were fully standards compliant! Scary thought.)
Prior Art