Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider switching auth client #9

Closed
megoth opened this issue Dec 3, 2020 · 7 comments
Closed

Consider switching auth client #9

megoth opened this issue Dec 3, 2020 · 7 comments

Comments

@megoth
Copy link

megoth commented Dec 3, 2020

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
Copy link
Owner

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
Copy link
Owner

NoelDeMartin commented Dec 28, 2020

@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
Copy link
Author

megoth commented Jan 4, 2021

@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
Copy link
Author

megoth commented Jan 4, 2021

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
Copy link

NSeydoux commented Jan 4, 2021

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:

  • Dereference the URI. If you get RDF back (the Content-Type should tell you that), that's a WebID. You can look for a triple using the predicate http://www.w3.org/ns/solid/terms#oidcIssuer: that should give you the user's Solid Identity Provider (as mandated by the spec). Then you can use the provider's IRI in the auth library.
  • If you don't get RDF back, you can try to dereference <IRI>/.well-known/openid-configuration. This path should always exist for an OIDC-compliant identity provider. It that works, you can use the provider's IRI with the auth library.

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 added a commit to NoelDeMartin/ramen that referenced this issue Jan 5, 2021
@NoelDeMartin
Copy link
Owner

@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:

  • inrupt/solid-client-authn-js#423: Until this is not fixed, I won't update Media Kraken because it has an important impact in the UX. I guess if months go by and that issue is still open I'll finally do it anyways, but I'd rather not.

  • DPoP vs Legacy authentication: As mentioned above, the heuristics I'm using right now are not foolproof. If there's really no better way, I'll have to do something else in Media Kraken because I don't think the current approach is acceptable for a real application. And dropping support for legacy servers is not something I'm contemplating at the moment. So maybe I'll end up adding some UI affordance to force legacy authentication. I'm open to suggestions!

@NoelDeMartin
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants