Read/write Access to Inrupt Pod from Application


#1

I have an application trying to read/write from an inrupt Solid pod, but having issues. I am able to successfully authenticate using the webid of the pod owner, but when I try to read the root storage container to get the folder structure, I get an authentication error.

The live app is running here: https://graphmetrix.net (click login to authenticate)

fetcher.js:42 GET https://gibsonf1.inrupt.net/ 401 (Unauthorized)

I’m using this code to do the fetch the storage container post authentication:

if (session) {
    state.webId = session.webId;
				
  lobe.login = async function(){
	await lobe.rdf.fetcher.load(session.webId);
	state.userName = lobe.get.label(state.webId);
				    
	var storage = lobe.rdf.store.match($rdf.sym(session.webId),$rdf.sym(ns.SPACE('storage')));
	var profile = lobe.rdf.store.statementsMatching(null,ns.RDF('type'),ns.FOAF('PersonalProfileDocument'));
	if (storage.length > 0){
		state.storage = storage[0].object.value;
	}
	await lobe.rdf.fetcher.nowOrWhenFetched($rdf.sym(state.storage),undefined,function(ok, body, xhr){
		if (!ok) {
			console.log("Error",xhr);
		} else {
			console.log(ok,body,xhr);
		}
	});
   };
   lobe.login();

Any help greatly appreciated!


#2

Hi @gibsonf1 what I do to fetch is :
async getProfile () {

 /*if (!this.session) {
 await this.getSession();
}*/
this.VCARD = $rdf.Namespace('http://www.w3.org/2006/vcard/ns#');
try {
   await this.fetcher.load(this.session.webId);
 return {
  fn : this.getValueFromVcard('fn'),
  company : this.getValueFromVcard('organization-name'),
  phone: this.getPhone(),
  role: this.getValueFromVcard('role'),
  image: this.getValueFromVcard('hasPhoto'),
  address: this.getAddress(),
  email: this.getEmail(),
};
} catch (error) {
console.log(`Error fetching data: ${error}`);
}
}; 

and to update :

async updateProfile (profile){
console.log(profile)
//  updateProfile = async (form: NgForm) => {
const me = $rdf.sym(this.session.webId);
const doc = $rdf.NamedNode.fromValue(this.session.webId.split('#')[0]);
console.log(me)
console.log(doc)
const data = this.transformDataForm(profile, me, doc);

console.log(data)



//Update existing values
if(data.insertions.length > 0 || data.deletions.length > 0) {
  console.log(this.updateManager)
  console.log(this.session)
  this.updateManager.update(data.deletions, data.insertions, (response, success, message) => {
    if(success) {
      console.log("Your Solid profile has been successfully updated");
      //this.toastr.success('Your Solid profile has been successfully updated', 'Success!');
      //form.form.markAsPristine();
      //  form.form.markAsTouched();
    } else {
      //  this.toastr.error('Message: '+ message, 'An error has occurred');
      console.log('Message: '+ message, 'An error has occurred');
      console.log(response)
    }
   });
   }
}

complete code can be found here : https://github.com/scenaristeur/spoggy-graph/blob/master/spoggy-solid.js


#3

The interesting part is that the profile loads no problem (that’s what you see on the screen shot). As soon as I try to fetch something beyond the basic profile, that’s when it fails, even though I’m the owner of and authenticated to the pod.

await lobe.rdf.fetcher.load(session.webId);

Fetches all the public profile information very well. It’s when I then try to use the fetched storage location to fetch the folder structure that it fails on authentication.


#4

Your profile is public, so that may be why it works - it would work for me too without loggin in.

The “401 Unauthorized” response means no credentials was sent. So somehow the fetcher is unaware of the fact that your are logged in … which I assume you must be if session.webId has a value.

Could it be that you are using incompatible RDFLIB components from different sources (if such thing is possible)?

Does the bare bone “Lunch break” app work for you? If so then try to figure out what is different.


#5

Jorn,

That’s a good idea, though the lunch break example is only reading public information. I’ll see if I can figure out how to authenticate a fetch with one of the other writing apps. (I’m not using Node)


#6

On further code reading, I was able to gain access to a private resource, however, the api for getting information from it is unclear (this is a 200 response with no content):

window.solid.auth.fetch("https://gibsonf1.inrupt.net/",{headers: {"accept": "image/*;q=0.9, */*;q=0.1, application/rdf+xml;q=0.9, application/xhtml+xml, text/xml;q=0.5, application/xml;q=0.5, text/html;q=0.9, text/plain;q=0.5, text/n3;q=1.0, text/turtle;q=1"}}).then(console.log)

=> Response {type: "cors", url: "https://gibsonf1.inrupt.net/", redirected: false, status: 200, ok: true, …}

So the solid-auth-client library is working just fine, but the standard fetcher is somehow not using it apparently.


#7

Finally success:

window.solid.auth.fetch("https://gibsonf1.inrupt.net/",{headers: {accept: "image/*;q=0.9, */*;q=0.1, application/rdf+xml;q=0.9, application/xhtml+xml, text/xml;q=0.5, application/xml;q=0.5, text/html;q=0.9, text/plain;q=0.5, text/n3;q=1.0, text/turtle;q=1"}}).
then(function(response){return response.text();}).
then(function(data){ console.log(data);})

#8

It works, great :+1:

I have been using rdflib.js and then used $rdf to access the fetcher and store etc. Then I get the fetcher like this:

this.store = $rdf.graph();
this.fetcher = new $rdf.Fetcher(this.store);
this.updater = new $rdf.UpdateManager(this.store);

and this to include a login button:

const popupUri = '/lib/solid-auth-client/dist-popup/popup.html';

$('#login-button').click(() => solid.auth.popupLogin({ popupUri }));

solid.auth.trackSession(session => { ... });

The rdflib.js has this piece of (minified) code:

$rdf=n(t.window,t.solid.auth)

So that is apparently how rdflib interacts with solid.auth to get the credentials.


#9

Wish that minified code worked in my case. I found it and un-minified it for context:

'undefined' == typeof window || window.solid || (window.solid = {}),
    function e(t, n) {
        'object' == typeof exports && 'object' == typeof module ? module.exports = n(require('window'), require('solid-auth-client')) : 'function' == typeof define && define.amd ? define(['window'], n) : 'object' == typeof exports ? exports.$rdf = n(require('window'), require('solid-auth-client')) : t.$rdf = n(t.window, t.solid.auth)
    }(window, function(e, t) {
            var n = Math.pow,
                a = Math.floor,
                r = String.fromCharCode,
                o = Math.abs;
            return function(e) {
                    function t(a) {
                        if (n[a]) return n[a].exports;
                        var r = n[a] = {
                            i: a,
                            l: !1,
                            exports: {}
                        };
                        return e[a].call(r.exports, r, r.exports, t), r.l = !0, r.exports
                    }...

#10

I got it to work by adding this line:

fetch = solid.auth ? solid.auth.fetch : (a, b) => window.fetch(a, b);