How to authenticate a restApi to write on a user pod

Hi, I am currently developing a software project for my degree along with 3 colleagues and one of the constrainst for the project is to use Solid as much as possible. The application is based on a “social network” for users to post their favourite locations and to share among friends. The application is being developed in Node.js with Express for the RestAPI and a React based WebApp. The main problem we are facing right now is how can we grant the RestApi the ability to have write priviledges on the user’s pod. As for now we are able to log in the webApp and if we do the requests from there everything works fine. But when trying to move this logic to the restApi (where it should be) authentication problems arrise.

For now we have tried to embed the Session of ther user in the webapp into the headers of the HTTP request but authentication issues still persist.

Does anyone know how can we solve this issue?
Thanks in advance.
Lomap_en2a team :smile:
Our repo (currently merging in pod_basic_operations branch)

Hi, here’s a tutorial on how to authenticate in the backend: Authenticate (Node.js Web Server) — Inrupt JavaScript Client Libraries. It also uses NodeJS and Express in the example, so it is hopefully rather straight forward to follow. Does this help your use case?

Tutorial seems to fit perfectly (thanks :smile: ) the only issue I am currently having is a CORS problem on the redirection to the pod provider’s site. Any recomendation on how to hand the webapp the control to redirect the user and still have the results back in the restapi?

I’m not sure I can help here. But if you share the error maybe I have a guess. If you did it as in the example (res.redirect(url);) and it doesn’t work, you can also raise an issue in their github issues.

Hi! I am developing the same application with a different group for the same degree. We are having a similar problem, let me show what we have and maybe it will help you solve your problem or at least clarify it. First, we have the original request from the WebAPP:

export const login = async (providerURL : String) => {
    const loginURL = await fetch('http://127.0.0.1:8082/auth/login',{
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        },
        credentials: "include",
        method: 'POST',
        mode: 'cors',
        body: JSON.stringify({provider: providerURL })
    });
    window.open(await loginURL.json()); //Redirect to provider auth
}

When the login process starts, we call the following function in our RestAPI, that links the WebAPP session with the provider session.

    initLogin : async function (req:Request, res:Response){
        // create a new Session
        const session = new Session();
        req.session.solidSessionId = session.info.sessionId;

        //Redirect user to POD provider login
        const redirectToSolidIdentityProvider = (providerURL : string) => {
            res.status(200).json(providerURL);
        };
        // redirect handler will handle sending the user to their POD Provider.
        await session.login({
            // If login successfully, redirect here
            redirectUrl: 'http://localhost:3000/auth/loginconfirm',
            // Set user SOLID identity provider
            oidcIssuer: req.body.provider,
            // Application name to show when requesting data
            clientName: "LoMap",
            //handler to redirect to the provider login
            handleRedirect: redirectToSolidIdentityProvider
        });
    },

If you take a look at the redirect handler, we redirect again to the WebAPP, since we need to close the original request we made. That fires the following

export const confirmLogin =  (params : String) => {
    return fetch("http://127.0.0.1:8082/auth/loginconfirm"+params, {
        credentials: "include",
        method: "POST"
    })
}

Which calls the function in the RestAPI that is responsible for finishing the login process.

    confirmLogin : async function (req:Request, res:Response){
        // If we get here, the user has logged in successfully
        // Recover session information
        const session = await getSessionFromStorage(req.session.solidSessionId!);
        // Complete login process using the data appended by the Solid Identity Provider
        await session!.handleIncomingRedirect(`http://localhost:8082/auth${req.url}`);
        // Session now contains an authenticated Session instance.
        if (session!.info.isLoggedIn) {
            return res.sendStatus(200);
        }
        return res.sendStatus(401)
    },

And here is where we are having the problem. After redirecting to the provider, we are loosing the cookie, so when we try to recover the session from the storage in the RestAPI there is no matching solid session. Is there a way to keep the session of the WebAPP after redirecting to the provider?

Hi

I am confused what you want to achieve. Do you want to make authenticated requests from the client side? If yes, then why do you use the server for it (instead of doing it from the client: Authenticate (Browser) — Inrupt JavaScript Client Libraries)?

Or do you want to make authenticated requests from the server side? If yes, is the problem that session!.info.isLoggedIn returns false?

And in general, if you’re really stuck try to start from the Inrupt example. My guess it that it should work. And if it works you could adapt it step by step afterwards to suit your needs, always checking if it still works

Hi, thanks for your quick response :slight_smile:

I want to make authenticated requests from the server, but the client needs to access the log in functionality that I offer in my RestAPI.

The problem is that I cannot redirect from the server, since the user is not going to access it directly and its responsability is not to render views. In order to render the provider login, in need to redirect it from the client side. I did follow the example of the Inrupt Server authentication library, but since I cannot use the res.redirect(url) what I do is return to the client the url so that it can redirect and then send the confirm information back to the RestAPI. The problem is that when I redirect to the provider in the client, I loose the cookie session and I don’t know how to persist it.

Do you mean that req.session.solidSessionId is undefined? If yes, that’s not Solid related so you’ll need to look at the documentation of express-session. I think it stores the session id in the browser as a cookie and then on the backend uses this cookie to load the session data. If it doesn’t load the data, check if the cookie is set in the browser

Nevermind, after a couple days of getting the same error I just realized that my browser was blocking the cookies. I get an Invalid Grant now, I’ll keep working on it and comment if I make any advancement.

Hi! Made new advancements.

I think my problem with the Invalid grant was because the login process was going through two different systems, and then the session was being invalidated (maybe some measures against man in the middle attakcs?). Maybe that is related to the problem that @monkeyPablo was having of not being able to pass the session to his server.

My solution was to follow @A_A advice and just stick to the INRUPT example, so now my server works now as a restAPI for resources, but as a redirection service for authentication.

Hi! Sorry I’m late to respond, and thanks to you @A_A for providing helpful advice.

Just to clarify, I think one important item to outline here is what @A_A was drawing attention to: the client and server sessions are distinct. Our (Inrupt) authentication library makes the access token unavailable (on purpose, for security reasons), which means the client-side session can’t be serialized to be sent to the server, which explains the problems you’ve been experiencing. Therefore, there are two approaches possible: either your client session is authenticated directly to the Pod server, or it is authenticated to your API, and the server-side component is authenticated to the Pod. The latter is what appears to fit your requirements, and it is quite a traditional setup for Web applications. This means that your React app won’t have anything to do with Solid, it will be specific to your backend API, and the backend will get data from Solid. The server can track the client authentication through a cookie, and the Access Tokens used to access the Solid data will be held by the server, and somehow linked to that cookie so that the server can track the authenticated user.

This is pretty much the architecture seen in the Inrupt server-side authentication example, except that the Express server returns server-rendered HTML for simplicity :).

The invalid_grant issues may come from a couple of causes, but usually it means something went wrong with your registered client. Are you using a Client Identifier Document, or simply using Dynamic Client Registration (the default) ?