[Solved] How to attest possession of a WebID (from arbitrary source)?

Normally, the interaction of Apps in Solid is that users directly operate on an App, and log-in to their account there directly, through OIDC.

However, in my case, there is an interaction with an external component, called the orchestrator. The key here is that the orchestrator is a service but does not directly interact with the user with a Web UI. The user uses a Solid App (with Web UI) to configure the orchestrator (through API). In particular, they need to “register” with the orchestrator to use it.

A side note: The “registration” is not the same as creating an account. It shows that the user intends to use the orchestrator for its functionality. In our scenario, after registration, the user’s WebID (and some other information) will be added to the orchestrator’s own database, and the orchestrator will start to function accordingly for that registered user. Users can also de-register through the API.

My question is: in this scenario, is there a way for the orchestrator to verify that the user is who they claim they are? In other words: can an arbitrary application/source attest the possession of a WebID (without requiring to go through the log-in process?)?

Ultimately, this aims to prevent impersonation in the registration process and other API calls with the orchestrator. Happy to consider other methods.

1 Like

One way to prove control over a WebID is to write something in your WebID profile.
So the registration process could provide the user with some token, that they would then add in their profile (<#me> orch:registrationId “abcd1234”). The orchestrator would then verify that it is there. (this could be automated by the registration app, of course).

Of course, you might not want to make this public, but you could put this in an extended profile document that is only readable by the orchestrator. This means that the orchestrator itself should have a WebID.

Another option would be

  • I put my “registration request” as a resource somewhere in my pod (potentially restricting access to the orchestrator only)
  • I send a link to this resource to the orchestrator
  • the “registration request” contains a reference to my WebID, which, in turn, has a pim:storage link to the pod containing the “registration request”
  • the orchestrator can therefore infer that the WebID and the “registration request” come from the same person
1 Like

I think you’d use OpenID Connect’s auth code flow for that, as supported by solid-client-authn-node?

Thanks. The first method is also what I thought would be an option. But automating that would be somewhat cumbersome, because:

  1. The App needs to modify a piece of data (WebID + private profile) which it did not expect to modify;
  2. It needs to do this every time it performs an API call to the orchestrator.

I thought there would be some standards for that…?

I had a quick look, but did not see how it is different from the usual auth flow (apart from starting from cmdline)?

If so, this is not what I need. I do not want the orchestrator to act on behalf of the user, by any chance. In addition, this would require the user to go through this process every time an API call is made, which is really annoying.

But indeed I’m seeking some potentials from the OIDC flow, as it is the standard and should already have a solution for this… I assume…?

I only have only a superficial knowledge of OIDC, so take what I write with a grain of salt.

I do not want the orchestrator to act on behalf of the user

I think (hope) that there is no such risk here: whenever I authenticate with OIDC in a Solid app, I can access multiple PODs (mine, but also others’) who all can check that it is me in order to grant me the correct authorization. If that enabled them to impersonate me, something would be rotten in the state of OIDC…

So it should be possible to use the same kind of mechanism on your orchestrator’s server to ensure the identity of the user.

Thanks for the comment. I may not complete understand that, so please bear with my potentially stupid question.

I believe this refers to the normal process, i.e. you “log in” to that Solid App with your own WebID through OIDC, right?

Therefore, that App is acting on behalf of you, during that period of time while you are using the App (or more accurately, I believe, before you log out from the App).

I presume “them” means the Solid App(s)?
Well, to my understanding, that is sort of true, but expected. After log in, the App is using your identity (WebID) to perform requests to PODs, and the App has a way to prove that this is authorized by you. Of course, I’m not entirely sure if the App will still send its own identity (e.g. the Origin?) in this case, but presume it should?

So actually, from this line of thinking, if an App could prove that it using a WebID is authorized by the owner of the WebID, to some other parties (i.e. Pods), then it should also be able to prove to the orchestrator.
I’ll try to do some search to find out how this is done, and does the solid-client-authn library support this. But if anyone is more familiar with OIDC/OpenID and/or the solid-client-authn library, please do shout out.

My understanding is that when a Solid Resource server gets a request from a client app, it gets a WebID and a token from the client, goes to the profile associated with the WebID, finds a solid:oidcIssuer URI in the profile, goes to the URI and verifies with the issuer if the token is validly assigned to that WebID. In other words, it doesn’t trust the client without checking with the issuer. So I doubt that solid-authn-browser on its own is the full answer. @acoburn or @elf-pavlik might know the answer to your question and would definitely have a better explanation than mine.

[EDIT] So, as @acoburn shows, I got it backwards. You get the issuer from the token, verify that the issuer issued the token, then use the WebID to visit the profile and find if the issuer is recognized by the WebID owner as able to issue tokens for their WebID.

1 Like

The orchestrator would verify a user by performing two operations on the ID token provided by the app.

First, the orchestrator would check the signature of the ID Token. This step is defined by OpenID Connect: use the issuer [iss] URL, append .well-known/openid-configuration, use that to locate the public key used to sign the ID Token (JWT)

Second, if the signature is valid, then the orchestrator would dereference the WebID URL, looking for a particular triple in that RDF document: solid:oidcIssuer. If the issuer [iss] claim in the ID token is listed in the WebID profile document, then an app can assume that the agent who controls that WebID profile document trusts the issuer to generate ID tokens on their behalf.

That is all the orchestrator needs for WebID validation. There may be additional levels of validation, including the validation of the audience [aud] and/or authorized party [azp] claims, and also any DPoP-based confirmation [cnf] claims, but those are not necessary for simply validating an assertion of identity (webid)

5 Likes

I think what you describe can easily be done with the access-token-verifier library.

And to create the token in your web UI, I guess it is enough to use any authenticated fetch method (e.g. from solid-client-js) and make a request to your.orchestrator.com/some/path. Then in the backend, take this token, the dpop header, the http method and url and pass it to the library. If it’s invalid, it should throw an error, if it succeeds it returns the webId.

3 Likes

Thanks very much @jeffz @acoburn and @A_A for the replies! That indeed helps me in understanding the underlying mechanism, and also points to a potential library I could use (and examples!).

I’ll look into the library and see if it would work in our case. Will report back after confirmation, though probably not immediately.

Ok, I have tried the solution A_A suggested, and it worked without issues.

I’m now wondering will there be any security concerns for this method?

In case useful to others: the headers are directly contained in the HTTP request header, and named accordingly. I spent some time trying to find the library to obtain them, but then when inspecting packets in browser Console-Network, I found them out there directly.

I think there should be no surprising security concerns. From what I could think of:

  • the user needs to trust the app used to make the authenticated request (the app could also make authenticated requests to other backends / pods)
  • requests could be replayed (ie repeated) within a 60s time frame, though the access-token-verifier has a small protection against it (see their readme)
  • the tokens are valid only for a specific url and http method, so the backend server can NOT reuse/abuse these tokens to make authenticated requests to other servers
  • authenticating as a webid tells nothing about ownership of pod storages etc (ie you cannot infer that foo.pod.com/profil/card#me has full control over foo.pod.com)
1 Like

Usually the server framework should easily be able to access them. For instance with expressjs you can get any header with req.get(‘the-header-name’).

1 Like

Thanks, this is indeed my main concern previously. Glad to know this is not an issue.

Does that mean the access token is refreshed/obtained every time a fetch request is to be made, and this refreshing is done by talking with the OpenID Provider / Token Endpoint?
Seems like a tradeoff between server / network performance vs security. Where can I find the details in (OAuth’s?) document?

No, iirc it stores a private key on the client side and the corresponding public key on the IDP. And then it uses the private key to sign the request metadata and then anyone can verify the signature by requesting the public key from the IDP. That’s the simplified version at least. I think you need some request(s) to setup the keys on the client, but then you don’t need additional requests per each request you want to sign (ie client side only a minor computation overhead for signing the requests, no network overhead).

You can find more at the solid oidc primer document: Solid-OIDC Primer (or the specification itself)

1 Like