Authorizing requests using an indentity from a different provider

Hello,
I am working on an web application, that allows users to collaborate on documents. To implement the authorization and authentication side of things, I am using the @inrupt/solid-client-authn-browser library.

After authenticating the user I am able to make authorized requests with rdflib by passing the authorized fetch function from @inrupt/solid-client-authn-browser into the rdflib's fetcher.

Recently, I have come across the following issue:

  1. The user logs in using their identity on solidcommunity.net (their WebID is something like https://[user].solidcommunity.net/profile/card#me)
  2. The user tries to access a file on a different Solid Pod, for example on the inrupt.net Solid serves (something like https://[anotherUser].inrupt.net/public/file.ttl). According to the file’s access list, this user should have read, append, write and control access to this file.

In this case, according to the headers sent, no authorization headers are being sent, which results in an unauthenticated request.

This is only an issue, if the user’s identity is from a different solid pod, than the requested file. If they are stored on the same solid pod, a nssidp.sid=XXXX cookie is sent and everything works as expected.

I will be very thankful for any responses, that will help me make this thing work. (And I apologize about my English.)

I’m not too familiar with rdflib, but I just tried this using solid-client-authn-browser and a different library (@inrupt/solid-client), and there sending requests to different servers sent the Authorization header just fine. So it looks like this might be a bug in rdflib. I’m not sure if someone is processing reported issues there, but just in case you might want to log it there?

@Vinnl - if @jenda passes in @inrupt/solid-client-authn;s fetch to rdlib, that means that all of the fetching done is done using solid-client-authn. So I doubt the problem is in rdflib (but of course I could be wrong). @jenda, can we see your test case and the errors you get?

Hi, thank you both for your responses.

I am testing it on the following file:

https://jenda.inrupt.net/private/test6.ttl

The https://jenda.inrupt.net//profile/card#me user can access it normally, but the https://jenda2.solidcommunity.net/profile/card#me is unable to do so. The ACL file looks like this:

@prefix : <#>.
@prefix n0: <http://www.w3.org/ns/auth/acl#>.
@prefix c: </profile/card#>.
@prefix n1: <http://xmlns.com/foaf/0.1/>.
@prefix c0: <https://jenda2.solidcommunity.net/profile/card#>.

:162358414343406647702462499261
    a n0:Authorization;
    n0:agent c:me;
    n0:default <test6.ttl>;
    n0:mode n0:Control, n0:Read, n0:Write.
:16235841434359916942851373513
    a n0:Authorization;
    n0:accessTo <test6.ttl>;
    n0:agent c:me;
    n0:mode n0:Control, n0:Read, n0:Write.
:1623584144770047905421864635866
    a n0:Authorization;
    n0:agent c0:me;
    n0:default <test6.ttl>;
    n0:mode n0:Control, n0:Read, n0:Write.
:1623584144770057401164870901744
    a n0:Authorization;
    n0:accessTo <test6.ttl>;
    n0:agent c0:me;
    n0:mode n0:Control, n0:Read, n0:Write.
:public
    a n0:Authorization;
    n0:accessTo <test6.ttl>;
    n0:agentClass n1:Agent;
    n0:mode n0:Read.

Here are the headers, when the user is not logged in. (In this case a default rdflib fetch is used, not the fetch form the auth library):

Request:

GET /private/test6.ttl HTTP/1.1
Host: jenda.inrupt.net
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: */*
Accept-Language: cs,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Referer: http://localhost:3000/
Pragma: no-cache
Cache-Control: no-cache

Response:

HTTP/1.1 200 OK
X-Powered-By: solid-server/5.6.6
Access-Control-Allow-Origin: http://localhost:3000
Vary: Accept, Authorization, Origin
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By
Allow: OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE
Link: <test6.ttl.acl>; rel="acl", <test6.ttl.meta>; rel="describedBy", <http://www.w3.org/ns/ldp#Resource>; rel="type"
WAC-Allow: user="read",public="read"
MS-Author-Via: SPARQL
Updates-Via: wss://jenda.inrupt.net
Content-Type: text/turtle
Date: Tue, 15 Jun 2021 20:41:42 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Here is the same request, when https://jenda.inrupt.net//profile/card#me is logged in (I censored the contents of the cookie what I guess is probably a session token):

Request:

GET /private/test6.ttl HTTP/1.1
Host: jenda.inrupt.net
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: */*
Accept-Language: cs,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Referer: http://localhost:3000/
Cookie: nssidp.sid=THIS_TOKEN_IS_PROBABLY_PRIVATE_SO_I_CENSORED_IT_HERE
Pragma: no-cache
Cache-Control: no-cache

Response:

HTTP/1.1 200 OK
X-Powered-By: solid-server/5.6.6
Access-Control-Allow-Origin: http://localhost:3000
Vary: Accept, Authorization, Origin
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By
Allow: OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE
Link: <test6.ttl.acl>; rel="acl", <test6.ttl.meta>; rel="describedBy", <http://www.w3.org/ns/ldp#Resource>; rel="type"
WAC-Allow: user="read write append control",public="read"
MS-Author-Via: SPARQL
Updates-Via: wss://jenda.inrupt.net
Content-Type: text/turtle
Set-Cookie: nssidp.sid=THIS_TOKEN_IS_PROBABLY_PRIVATE_SO_I_CENSORED_IT_HERE; Domain=.inrupt.net; Path=/; Expires=Wed, 16 Jun 2021 20:43:49 GMT; HttpOnly; Secure
Date: Tue, 15 Jun 2021 20:43:49 GMT
Connection: keep-alive
Transfer-Encoding: chunked

…and finally, here is the same request, when https://jenda2.solidcommunity.net/profile/card#me is logged in instead:

Request (looks unauthenticated to me - no session token or anything similar):

GET /private/test6.ttl HTTP/1.1
Host: jenda.inrupt.net
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: */*
Accept-Language: cs,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Referer: http://localhost:3000/
Pragma: no-cache
Cache-Control: no-cache

Response:

HTTP/1.1 200 OK
X-Powered-By: solid-server/5.6.6
Access-Control-Allow-Origin: http://localhost:3000
Vary: Accept, Authorization, Origin
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By
Allow: OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE
Link: <test6.ttl.acl>; rel="acl", <test6.ttl.meta>; rel="describedBy", <http://www.w3.org/ns/ldp#Resource>; rel="type"
WAC-Allow: user="read",public="read"
MS-Author-Via: SPARQL
Updates-Via: wss://jenda.inrupt.net
Content-Type: text/turtle
Date: Tue, 15 Jun 2021 20:51:56 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Both users logged in the same way using the oidcIssuer option of the login method from the @inrupt/solid-client-authn-browser library.

I will try to create a minimal example to ensure it is not caused by some weirdness in unrelated code.

Hmm, but that second request (when https://jenda.inrupt.net//profile/card#me is logged in) doesn’t appear to have an Authorization header either? Which as far as I know would be added by solid-client-authn-browser.

Could you maybe wrap that fetch into another function that logs when it gets called, so you can be sure that it’s actually used?

const loggingFetch = async (request, init) => {
  console.log("Sending a request to:", request);
  // The following `fetch` should be the one from solid-client-authn-browser:
  return await fetch(request, init);
};

Thank you @Vincent, this helped me to figure out the issue. The authenticated fetch was not used at all.
For future reference and for anyone, that happens to struggle with a similar issue, here is what I did wrong.

For some reason I thought that it is possible to pass the fetch function in a fetcher call:

// somewhere we initialize the rdflib.js fetcher
const fetcher = new Fetcher({
   // ...options
});

// and somewhere else we use this code:
fetcher.load(requestedUri, {
  // originally, I passed the authenticated fetch here.
  // THIS IS WRONG, PLEASE DO NOT COPY-PASTE THIS EXAMPLE
  fetch: myFetchFunction
});

The working approach was to pass the fetch function to the fetcher constructor:

const fetcher = new Fetcher({
  fetch: myFetchFunction
  // ...other options
});

// all requests are now using the custom fetch function:
fetcher.load(requestedUri, {
  // you do not need to pass a fetch function here
});
1 Like

@jenda, perhaps you omitted this for brevity, but you should probably pass {fetch:myFetchLibrary.myFetchFunction.bind(myFetchLibrary)}

1 Like

Also note : with rdflib, you can declare global.SolidFetcher or window.SolidFetcher before importing rdflib, and that is the same as passing it in the constructor for fetcher each time you construct a new one.

1 Like