Announcing Solid-Node-Client

Solid-Node-Client provides nodejs access to Solid pods and to local file systems. The local file system, when addressed with file:/// URLs in this library, is treated as if it were a pod and behaves, in most respects just like a pod. Solid-node-client is an act-alike replacement for solid-auth-cli that uses the new access tokens and is therefore more future facing.

The library may be used stand-alone, or may be combined with other libraries such as rdflib or solid-file-client. See the README for details.

Please give it a try and report feedback, preferably via a github issue or just message me here.

10 Likes

Hey @jeffz,
I am trying to use the solid-node-client in my project. I was wondering if the solid-node-client supports persistent login as solid-auth-cli did? If so I am getting an unauthenticated error while trying to use it with rdflib. Any thoughts pertaining to what the issue might be would be really helpful.

1 Like

Okay. I changed the interface yesterday and put some info in the README that relates to usage with rdflib. Main changes are that you need to get a client object and use its methods (e.g. client.fetch) instead of the functional approach (e.g. fetch). And for rdflib it is important that you import rdflib and bind the fetch method when you create a fetcher.

This no longer works :

    const fetcher = $rdf.fetcher(store),{fetch:solid.auth.cli.fetch});

Instead do this bind the fetch like this:

  import {SolidNodeClient} from 'solid-node-client';
  import * as $rdf from 'rdflib';
  const client = new SolidNodeClient();
  await client.login();
  const store   = $rdf.graph();
  const fetcher = $rdf.fetcher(store,{fetch:client.fetch.bind(client)}); 

Please message me if you continue to have problems, I am glad to help and want to know what the problems are.

@jeffz
Thank you for the reply,
I have successfully logged in and used fetcher to obtain data using the above format. The issue I am having is that when I try to access the solid session in a different route after saving the session returned in req.session as I used to do with solid-auth-cli does not seem to work with the session produced by solid-node-client.
On trying to check the session in a different route using currentSession method, it turns out to be undefined and hence my fetcher says unauthenticated. It works when it is the same route though.
Is there a different way with which I am supposed to save the session to be used persistently across different routes?
And also it would be helpful if you could let me know how to use solid-node-client with solid-file-client.

I’m not absolutely sure what you mean by “different routes”. Once you’ve instantiated a client with new SolidNodeClient() and called client.login(), that client should maintain the authentication until you log out. The new version lets you have multiple clients, each with a different identity. If you want to look directly at the underlying solid-auth-fetcher session object for each client, that should be available as client.session, so you should be able to do, for example if( client.session.loggedIn() ) …

I may have to make some changes to solid-file-client to make it compatible with solid-node-client. I’ll work on this and message you the details.

You should definitely be using v1.2.0 or greater of solid-node-client. And yes, sorry, I should have included a currentSession method for backwards compatibility. I’ll add it. But for now, the session is available as described above in the client.session object.

i have instantiated a client and is avilable in the particular file where it was instantiated and is working as expected. I need to use the same client globally like in a different file for example without having to reinstantiate a new client.
Lets say i have two routes, /login and /profile each in a different file. how do get the client or the session received from /login to work with /profile route without having to reinstantiate the client and the user having to login in again.
thank you for taking time to help me out

If you need the same client in two files, you’ll either need to manually pass the client into the second file or else declare global.client in the file where you login. This works :

// file1.js
import {test} from './file2.js';                                                   
import {SolidNodeClient} from 'solid-node-client';                              
const client = global.client = new SolidNodeClient();                           
client.login().then( ()=> {test()});                                            

// file2.js
export function test(){                                                         
  console.log( global.client.session.loggedIn )  // should output "true"                                
}                                                                               

I think I’ve understood your issue, if not, write back.

This is my rough work flow…

routes/auth.js

router.post('/login', async(req, res) => {
    const { unameoremail, password } = req.body;
    uname = unameoremail
    pass = password
    let response = await authService.login(unameoremail, password).then(result => {
        if(result) {
            console.log(result, "result")
            return result;
        }
    });
    req.session.user = response.solidSession;
    return res.json({response})
});

router.get('/myprofile', async(req, res) => {
    if(!req.session.user) {
        return res.status(401).json({ success: false, message: 'Please Login to Continue' });
    } else {
        let response = await userService.getMyProfile(req.session.user, req.session.userDetails.session.username).then(result => {
            if(result) {
                return result;
            }
        });
        return res.json({ response });
    }
});

service/authService

const auth = require('solid-node-client')
const client = global.solid = new auth.SolidNodeClient();

module.exports = {
login: async(username, member_id) => {
let creds = {
        idp      : keys.solidServer.idp,
        username : uname,
        password : psswd,
        debug: true
    }
	let solidSession = await client.login(cred).then(solidSession => {
                        if(solidSession){     
                                let session = {
                                    success: true,
                                    message: 'User Successfully authenticated to POD',
                                    solidSession
                            }
			return session
}
                        }
                    )
return solidSession
} 
}

service/userService

    const auth = require('solid-node-client')
 // const client = new auth.SolidNodeClient();
    const $rdf = require('rdflib');
    global.$rdf = $rdf;
    const store = $rdf.graph();
    const fetcher = $rdf.fetcher( store, {fetch:client.fetch.bind(client)} );


    module.exports.getMyProfile = async(solidSession, profilePath, me) => {
         let profile = store.sym(profilePath)
         let user = store.sym(me)
    	fetcher.load(profile).then(response =>{
                console.log(solidSession, response)
                let name = store.any(user, vcard('hasName'))
                let mobile = store.any(user, vcard('hasTelephone'))
        });
        return name, mobile;

    }

When I used solid-auth-cli I saved the solid session in req.session.user and was able to use that session to make authenticated requests to the NSS. I want to implement the same with solid-node-client.
I need to authenticate the fetcher in userService/getMyProfile using the session or client from authService/login
I realize I might be confusing something very basic please bear with.

No problem, if I don’t answer your question, just keep posting.

I think it should work if you declare the global.client in the topmost file (I think that would be auth.js) and then use global.client instead of client in imported files (authService.js and userService.js). So you’d declare global.client in auth.js and then do a global.client.login() in authService.js and a $rdf.fetcher(store, {fetch:global.client.fetch.bind(global.client)}) in userService.js.

It worked! Thanks you

1 Like

Hey,
Is there a way I could fetch the URL of the ACL file of a particular file or folder, and write acl files like the low-level methods of solid-file-client using solid-node-client as the auth client.
Also how do i add trusted application during login through nodejs instead of the data browser. Adding https://solid-node-client as a trusted origins in config/defaults in NSS dosent seem to work.

We need to look over solid-file-client for getting it to work with solid-node-client so I’m not certain that everything works yet. But this works. Keep in mind that you will only be able to use this on items for which you (and your app) have Control privileges.

const SolidNodeClient = require('../solid-node-client/').SolidNodeClient;       
const SolidFileClient = require('../solid-file-client/');                       
const auth = new SolidNodeClient();                                             
const fileClient = new SolidFileClient(auth);                                   
                                                                                
const item = "https://jeff-zucker.solidcommunity.net/public/";                  
                                                                                
async function main(){                                                          
  await auth.login();                                                           
  let links = await fileClient.getItemLinks(item);                              
  let aclUrl = links.acl;                                                       
  let aclContent = await fileClient.readFile( aclUrl );                         
  console.log(aclContent);                                                      
}                                                                               
main()                                                                          

You can add trusted apps by editing your profile or editing the acl of the resource you want it trusted for. You do not need to make a console-based app trusted, they are trusted by default. When you edit your profile or an acl, be very careful - minor typos can cause you to lose control over a resource or even your whole pod. You can practice by editing the acl with a file: URL locally.

@bourgeoa, the co-author of solid-file-client may have additional comments.

i am working on a console based app. But when i try to login using solid-node-client without adding “https://solid-node-client” to the trusted applications the login fails and i get this error.
(node:4181) UnhandledPromiseRejectionWarning: Error: Please make sure the cookie is valid, and add "https://solid-node-client" as a trusted app!
It works if i add it as a trusted application.

Sorry, I was unclear. You do need to add “https://solid-node-client” as a trusted app in order to login, but if you create foo.js as a console app that uses solid-node-client, you do not need to add foo.js as a trusted app.

How would I add https://solid-node-client as a trusted app by default so I do not have to rely on the data browser to add it as a trusted app for every new user. My final aim is for the application to be independent of the data browser.

As far as I know, there is no way to accomplish that. The console app can’t modify resources until it is logged in and it can’t login until solid-node-client is trusted. So the trusting part needs to be done with a browser app. I’ll check with the server authors to see if there’s a way around that, but AFAIK it would require changes in the server.

Cool, Thank you.