solid-client-authn-js icon indicating copy to clipboard operation
solid-client-authn-js copied to clipboard

Cannot make authenticated operations with fetch (solid-client-authn-node)

Open vincenzo-dip8 opened this issue 3 years ago • 2 comments

Hi everyone,

Premise

i have read all the documentations available at solid and inrupt site. i've tried also to post on forum, but still nothing.

Scenario

i have registered an account to Inrupt to have a Pod that i will use to interact with. Then, i d like to login both as admin and as an external user, but without using GUI. So, I have created an account on Inrupt also for all the other users i want to use in the scenario. About the login-with-no-GUI, I've learned from the section " Node.js Script: Single-User Script " in Inrupt Documentation, that to execute this kind of login i need to use a tool called @inrupt/generate-oidc-token that will return a token with some parameters within, that will be used to login .

I m posting the code used as login function, and all seems to work as well.

function logging(sessionID, provider, myClientId, myClientSecret, myRefreshToken){

    sessionID += createSessionID();

    const session = new Session(

      {  },

      sessionID

    );

    try {

      session.login({

        // Set oidcIssuer to the Solid Identity Provider associated with the credentials.

        oidcIssuer: provider,

 

        //An already-registered clientId, which identifies your application to the Solid Identity Provider.

        clientId: myClientId,

 

        //An already-registered clientSecret, associated to the Client ID. Like a password

        clientSecret: myClientSecret,

 

        //An already-registered refreshToken, which your application can use to get an Access Token.

        //Access Tokens allows you to access Resources for which you have been authorized.

        refreshToken: myRefreshToken,

 

        // If the refresh token is updated by the Identity Provider, this callback gets invoked.

        onNewRefreshToken: (newToken) => {

          console.log("New refresh token: ", newToken);   //to be stored in some way

          //myRefreshToken = newToken;

        }

      }).then(() => {

        if (session.info.isLoggedIn) {


          console.log(session.info.clientAppId);


          console.log(session.info.webId);



          console.log(session.info.expirationDate);          


          session


            .fetch(session.info.webId)

            .then((response) => {

              return response.text();

            })

            .then(console.log);

        }

      });

 

      console.log(session.info.sessionId);

     

      return session;      

    } catch (error) {

      console.log(error);

    }  

}

After days of focus, i think the problem was in the usage/explanation of this tool. It prompts me these questions:

  • What Solid Identity Provider do you want to log in to? Anser was broker.inrupt.com

  • Has Your App Been Pre-Registered by The Administrator Of The Pod Server You Are Signing In To? Answer could be Yes or No

  • What Is the Name of the Application You Are Registering? Answer will be a name like “Something”

  • Then i obtain the login link to use to login on inrupt.

considering those informations, even if i m either the admin or an external user that want to access to a Pod, I should still use that tool to login without GUI. Right?

Since i need to login for both admin and external user, i suppose that this tool need to be used still both as admin and external user.

Questions

I need to understand:

  • how to ask these questions as a pod administrator and external users to allow me to get all the tokens for my app / user I want to log in to my pod without using GUI login?

  • The only things i have noticed is that even trying all possible combination of responses to the prompted questions (by the tool) then it redirect me to the login page. So, if i need a login-token for admin/external user, when redirect to the login page, i need to use the relative credentials to the provider (admin-adminCredentials, extUser-extUserCredentials) or always use admin credentials?

i hope someone could help me to understand how to interact with the tool both as admin or external user

vincenzo-dip8 avatar Oct 23 '21 04:10 vincenzo-dip8

There is no real difference in the login process between the Pod owner (what I think you refer to as an admin) and any other third-party user: at some point, the person logging in must provide their credentials (typically username and password, even though it could be something else) to the identity provider through a GUI. This is the way OpenID-Connect (OIDC) is designed.This is why @inrupt/generate-oidc-token redirects you to the login page.

The problem with what you describe is that either the user has to entrust you with their private credentials, which is exactly what OIDC wants to avoid, or you would log any user in with your own identity if you used your credentials, which you probably don't want to do either. In any case, the user must interact with the identity provider through its GUI at some point in the process in order to log in and have its Solid identity recognized by the Pod.

What was the use case you had in mind ? Also, I see you've closed the issue, so if you've figured out something that works for you don't feel obligated to follow up here :).

NSeydoux avatar Oct 25 '21 08:10 NSeydoux

Hi, thanks for your reply. I ve found the solution to login using the generate-oidc-token tool. SO it is ok. But now i found other questions to explain:

  • I now find myself having to manage requests with necessary authorization, or the fetch. I found the Fetch () function in @inrupt/solid-client-authn-node. In the official documentation, the examples that show me as the word "fetch" is used "as an authorization parameter". We can import fetch (but this seems to be the case of browser context). import { login, fetch } from "@inrupt/solid-client-authn-browser"; While with Node library this seems to not be "importable", in the sense that "importing" or "requiring" this function within node script, this is not available (same for other function e.g. setAccessFor). When i use {fetch : fetch} i obtain this error config.json is not a function related to the index.js in solid at this code:
async function getResourceInfo(url, options = Object.assign(Object.assign({}, internal_defaultFetchOptions), { ignoreAuthenticationErrors: false })) {
    var _a;
    const config = Object.assign(Object.assign({}, internal_defaultFetchOptions), options);
    const response = await config.fetch(url, { method: "HEAD" });
    return responseToResourceInfo(response, {
        ignoreAuthenticationErrors: (_a = options.ignoreAuthenticationErrors) !== null && _a !== void 0 ? _a : false,
    });
}

and going deeper i found this (always in index.js within @inrupt/solid-client-authn-node)

const fetch = async (resource, init) => {
    /* istanbul ignore if: `require` is always defined in the unit test environment */
    if (typeof window === "object" && typeof require !== "function") {
        return await window.fetch(resource, init);
    }
    /* istanbul ignore if: `require` is always defined in the unit test environment */
    if (typeof require !== "function") {
        // When using Node.js with ES Modules, require is not defined:
        const crossFetchModule = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('cross-fetch')); });
        const fetch = crossFetchModule.default;
        return fetch(resource, init);
    }
    // Implementation note: it's up to the client application to resolve these module names to the
    // respective npm packages. At least one commonly used tool (Webpack) is only able to do that if
    // the module names are literal strings.
    // Additionally, Webpack throws a warning in a way that halts compilation for at least Next.js
    // when using native Javascript dynamic imports (`import()`), whereas `require()` just logs a
    // warning. Since the use of package names instead of file names requires a bundles anyway, this
    // should not have any practical consequences. For more background, see:
    // https://github.com/webpack/webpack/issues/7713
    let fetch;
    // Unfortunately solid-client-authn-browser does not support a default session yet.
    // Once it does, we can auto-detect if it is available and use it as follows:
    // try {
    //   fetch = require("solid-client-authn-browser").fetch;
    // } catch (e) {
    // When enabling the above, make sure to add a similar try {...} catch block using `import`
    // statements in the elseif above.
    // eslint-disable-next-line prefer-const
    fetch = require("cross-fetch");
    // }
    return await fetch(resource, init);
};

This is what i saw as error image

  • I can't understand the mechanism of generating a new token after logging. What is specified as "Handling refresh token rotation". In particular, known that, once you have obtained the token through the tool, this "expires" immediately after its (first and only) use. I believed that "Expiration Date" refer to the general duration of the token, and not at the expiry of the same. Where am I wrong to reason? In any case, the explanation of the mechanism cites:

"...each time the client application uses a Refresh Token to get a new Access Token, a new Refresh Token is also returned. The previous Refresh Token is invalidated, and can no longer be used to get Access Tokens.

@inrupt/solid-client-authn-node has an internal mechanism to manage refresh tokens in the Session’s storage. For the Session’s storage, you can pass in a storage as an option to the Session constructor or use the default storage, which is an in-memory storage. If you pass in a persistent storage, the refresh token management is transparent. However, for NodeJS scripts that use the Session’s default in-memory storage, the storage (and hence the refresh token) is lost when the program stops.

As an alternative to providing a persistent storage to the Session constructor, you can pass in the onNewRefreshToken callback to the constructor instead. Then, each time a new refresh token is issued, the onNewRefreshToken callback is invoked with the new refresh token as a parameter. The onNewRefreshToken option allows you to run custom code to handle the refresh token as appropriate.

I tried the use of onNewRefreshToken, but I couldn't get anything and I'm still forced to use the tool for every time I have to test my code to log in. Do you have usage suggestions ??

  • I'd better understand how the storage mechanism works. I saw from the Default documentation it is InMemory (which then loses everything after closing), while I saw setting examples on .json

  • When I receive an unauthorized error 401 while using functions to upload files what is telling me? What the authentication parameters didn't work? I am referring to passing "fetch"

vincenzo-dip8 avatar Oct 26 '21 05:10 vincenzo-dip8