NoelDeMartin / media-kraken

Track your movies with Media Kraken
https://noeldemartin.github.io/media-kraken/
GNU General Public License v3.0
71 stars 6 forks source link

Consider switching auth client #9

Closed megoth closed 3 years ago

megoth commented 3 years ago

The auth library solid-auth-client doesn't work with the newer Solid servers (e.g. Inrupts Enterprise Solid Server, the Community Solid server). I recommend switching to Inrupts Solid auth client, using the @inrupt/solid-authn-client-browser package. It works for both old and new Solid servers, and the APIs should be very similar.

NoelDeMartin commented 3 years ago

Thanks, I'll check it out! I'm going to start a new app soon, and I'll try it there first. If that goes well I'll update Media Kraken afterwards.

NoelDeMartin commented 3 years ago

@megoth I've done a small app as a proof of concept using the new authentication libraries, can you confirm that it works properly for you? If all's well, I'll move that code into Media Kraken.

Here it is: https://ramen.noeldemartin.com

I've done my own tests and it should work, but there are a couple of things I'd like to ask you.

You mentioned Inrupt's auth client works for old Solid servers as well, but for me it didn't work for node-solid-server v5.2.2. Is there something I am doing wrong? Or you meant to say that it works with the latest version of old servers?

Related to that, since I am falling back to solid-auth-client for servers that don't support DPoP (like NSS v5.2.2), do you know how can I tell if a server supports DPoP? For example, I know that Inrupt's servers support it, but looking at the response from this url it doesn't seem like they do: https://broker.pod.inrupt.com/.well-known/openid-configuration. In constrast to that, latest versions of NSS and other servers that support DPoP include "dpop" within a token_types_supported key that is missing in this response.

megoth commented 3 years ago

@NoelDeMartin sorry for late response, have had a vacation for the last three weeks ^_^

I've tested with Inrupt's Solid servers, and the app works with the following:

But it fails with pod-compat.inrupt.com:

bilde

I'm not quite sure what goes wrong here, but I noticed that you haven't given a clientName in https://github.com/NoelDeMartin/ramen/blob/main/src/authentication/DPoPAuthenticator.ts#L29-L32 (as compared to what I've done in https://github.com/megoth/dnd5e/blob/main/src/models/session/index.tsx#L20-L22, or using the Solid UI React Login Button at https://github.com/inrupt/pod-browser/blob/master/components/login/provider/index.jsx#L155). I thought clientName was optional, but it's worth a try.

megoth commented 3 years ago

And to answer more of your questions, yes, I did mean the newest versions of the old servers. I don't know to much about the auth algorithms in general, so I'll ping @NSeydoux on this in hopes that he knows more about this.

NSeydoux commented 3 years ago

Hi @NoelDeMartin, hi @megoth ! Unfortunately, there is no standard way to know if an identity provider and/or a resource server supports DPoP or not a priori. The token_types_supported entry in the NSS Solid Identity Provider's openid-configuration is not part of the OpenID spec, which is why it doesn't appear in https://broker.pod.inrupt.com/.well-known/openid-configuration.

You are absolutely right @megoth, clientName is optional, so that's not what causes the issue. Actually, I'm not even sure what is the problem here, because while being two separate resource servers, pod.inrupt.com and pod-compat.inrupt.com should use the same Solid identity provider (as far as I can tell)...

From what I can see, it looks like Ramen is trying to see if the identity that's provided is either an IdP or a Pod root, to which it appends /profile/card#me. That's not an ideal solution, since a WebID can take any form as long as it's a valid URL that dereferences to a FOAF profile (e.g. Sarven's infamous https://csarven.ca/#i). We are looking at adding a convenience function for this in our libraries, but basically the best way to authenticate a user based on a given IRI that may be either its Solid identity provider or its WebID is:

I'm not sure that this is causing the issue, but after two failed requests against https://broker.pod-compat.inrupt.com/ (there seems to be some CORS errors, because the IdP is only expecting foreign requests on its defined endpoints, and not on https://broker.pod-compat.inrupt.com/profile/card for instance), another authentication attempt happens, which tries to go through the implicit login flow, which isn't supported by https://broker.pod-compat.inrupt.com/ because it has some security issues. It's not a flow that is implemented by our library, so I guess at this point Ramen switched to solid-auth-client, the legacy library, which explains the failure.

So in a nutshell, what's the rule to try the different auth libraries, and is my explanation making any sense ^^ ?

NoelDeMartin commented 3 years ago

@megoth pod-compat should work now, read below for details (TLDR: I hard-coded the domain :/).

@NSeydoux Yes, your explanation makes sense! As you guessed, the problem was that it was using solid-auth-client. I am already doing something similar to what you mention, here's what I'm doing exactly:

First, I try to obtain the webId. I am doing this to obtain the solid:oidcIssuer, before doing this app I was not too familiar with the distinction between idp/webId and I just sent whatever the user introduced as the idp (that's how media-kraken works at the moment). So now I try to get the solid:oidcIssuer and if I can't find it, I use the domain of the url introduced as the idp.

Then, I try to guess if the server supports DPoP. I do this with some heuristics, because I haven't found a way to know for sure, and seems like there isn't. At the moment, I am reading /.well-known/openid-configuration to search for the token_types_supported, if that doesn't work I make a request to the solid:privateTypeIndex url (obtained from the webId) using an invalid DPoP token. If the server supports DPoP I should get a 401 error and if it doesn't a different error code. If both of these fail, I fall back to a regular expression for known DPoP providers.

Finally, depending on the information I got from these two steps, I use a @inrupt/client-authn-client-browser or solid-auth-client.

If you want to see the actual code doing this, it's here: src/services/Auth.ts:74

And sadly, it seems like I'm not able to guess that Inrupt's servers support DPoP authentication. The only reason why the others worked was that their domains are hard-coded in my code, and I've fixed pod-compat by hard-coding it as well. The regex I'm using now is this one: /^https?:\/\/broker\.(pod|pod-compat|demo-ess)\.inrupt\.com/. If you think I could improve it by just looking for inrupt.com or adding some other subdomains, let me know.

You may be wondering why is solid-auth-client the fallback and not the other way around, since it'd make more sense to use the modern authentication paradigm by default. The problem with that is that I have no way of knowing for sure that a server does not support DPoP authentication (as we've discussed). So if I decide to fall back to DPoP, I'd never use solid-auth-client because I wouldn't be able to distinguish between a server that doesn't support DPoP and a server that supports DPoP but I couldn't tell.


So, to summarize the current status of this issue. I have been working on the new authentication strategy in the Ramen app, and once I'm confident with that I'll add those improvements to Media Kraken.

Right now I have two blockers:

NoelDeMartin commented 3 years ago

TLDR: Media Kraken should work with new servers now :). Let me know if there's any issues.


After some more tinkering, I decided to use DPoP by default and allow users to switch the authentication method if they want. It seems like it is too difficult to know whether a server supports DPoP or not, and most servers should already support it so I think this is a good compromise.

As per the refresh problem, I still haven't seen any movement in Inrupt's issue and I'm not sure of when it'll be fixed. So I decided to go ahead with a simple UI that remembers the url so that users can log in again without too much hassle.

Since we're talking about Inrupt's PODs, I'll also mention a problem I found that could be an issue for heavy users of the application. It seems like listing container resources doesn't return their modified date, and that's what I was using to know whether a movie has been updated or not since the last log in. The consequences of this is that I have to request all the movies every time the application starts, and if a user's got many movies that will make the app unusable. I'll see what I can do about this.