Authenticate

To access private data on Solid Pods, you must authenticate as a user/agent who has been granted appropriate access to that data. For example, to write to a resource, the user must have Write Access on the Resource.

Authentication in Solid is based on OpenID Connect (OIDC), which means the authentication has the following flow:

  1. The application sends the user to the user’s Solid Identity Provider.

  2. The user logs in to the Solid Identity Provider.

  3. The Solid Identity Provider sends the user back to your application.

    Important

    The login is only complete after the user is redirected back to the app; i.e., your application must be reloaded between the start of the login and its completion.

Inrupt provides the following libraries for authentication:

  • solid-client-authn-browser library to authenticate in a browser.

  • solid-client-authn-node library to authenticate in Node.js.

In a Browser Environment

To authenticate in a browser environment, you can use the solid-client-authn-browser library.

  1. Call the login() function to start the process. Pass in the following login options:

    oidcIssuer

    Set to the user’s Solid Identity Provider (where the login function will send the user).

    redirectUrl

    Set to the location that the Solid Identity Provider will send the user back once logged in.

    For other options available to the function, see ILoginInputOptions.

    This process redirects the user from your application to the Solid Identity Provider. Once redirected to the Solid Identity Provider, the user logs in. Upon successful login, the Solid Identity Provider sends the user back to your application.

  2. To complete the login process, call handleIncomingRedirect() to collect the information provided by the Solid Identity Provider when it redirects the user back to your application. The session is logged in only after it handles the incoming redirect from the Solid Identity Provider.

  3. Once logged in, the fetch() function can retrieve data using the available login information.

    You can pass this fetch() function as an option to the solid-client functions (e.g., getSolidDataset, saveSolidDatasetAt) to include the user’s credentials with a request.

Example

The following example uses the solid-client-authn-browser library to login to a Solid server. It passes the fetch() function as an option to the solid-client functions getSolidDataset and saveSolidDatasetAt to read and write data to a Pod where the logged-in user has the appropriate access.

import { handleIncomingRedirect, login, fetch, getDefaultSession } from '@inrupt/solid-client-authn-browser'
import { getSolidDataset, saveSolidDatasetAt } from "@inrupt/solid-client";

async function loginAndFetch() {
  // 1. Call the handleIncomingRedirect() function to complete the authentication process.
  //   If the page is being loaded after the redirect from the Solid Identity Provider
  //      (i.e., part of the authentication flow), the user's credentials are stored in-memory, and
  //      the login process is complete. That is, a session is logged in 
  //      only after it handles the incoming redirect from the Solid Identity Provider.
  //   If the page is not being loaded after a redirect from the Solid Identity Provider, 
  //      nothing happens.
  await handleIncomingRedirect();

  // 2. Start the Login Process if not already logged in.
  if (!getDefaultSession().info.isLoggedIn) {
    // The `login()` redirects the user to their identity provider;
    // i.e., moves the user away from the current page.
    await login({
      // Specify the URL of the user's Solid Identity Provider; e.g., "https://inrupt.net"
      oidcIssuer: "https://inrupt.net",
      // Specify the URL the Solid Identity Provider should redirect to after the user logs in,
      // e.g., the current page for a single-page app.
      redirectUrl: window.location.href,
    });
  }

  // 3. Make authenticated requests by passing `fetch` to the solid-client functions.
  // The user must have logged in as someone with the appropriate access to the specified URL.
  
  // For example, the user must be someone with Read access to the specified URL.
  const myDataset = await getSolidDataset(
    "https://docs-example.inrupt.net/profile/card", {
    fetch: fetch
  });

  // ...
  
  // For example, the user must be someone with Write access to the specified URL.
  const savedSolidDataset = await saveSolidDatasetAt(
    "https://docs-example.inrupt.net/profile/card",
    myChangedDataset, {
    fetch: fetch
  });
}

loginAndFetch();

For working examples, you can look at the sample apps distributed with the code:

In a Node.js Environment

To authenticate in a Node.js environment, you can use the solid-client-authn-node library.

Node.js Web Server: Multi-session Management

A Node.js web server can use the solid-client-authn-node library to handle the user authentication flow and manage multiple sessions.

Session Management

To manage sessions, solid-client-authn-node automatically stores a mapping of the Session ID to Session when starting the login process. To retrieve the correct session for a user, solid-client-authn-node provides the getSessionFromStorage() function that takes a Session ID as its argument.

Logging out a session removes the session from the storage as well as disables its access to private resources.

  1. Create a new Session for the user at the login endpoint.

  2. Call the Session.login() function to start the login process. Pass in the following login options:

    oidcIssuer

    Set to the user’s Solid Identity Provider (where handleRedirect will send the user).

    redirectUrl

    Set to the location that the Solid Identity Provider will send the user back once logged in.

    handleRedirect

    Set to a callback function that sends users to their Solid Identity Provider.

    For other options available to the function, see ILoginInputOptions.

    This process redirects the user from your application to the Solid Identity Provider. Once redirected to the Solid Identity Provider, the user logs in. Upon successful login, the Solid Identity Provider sends the user back to your application.

  3. Use getSessionFromStorage() to retrieve the correct session for the user. Pass in the Session ID.

  4. To complete the login process, call Session.handleIncomingRedirect(), passing in the URL of the page handling the redirect. Session.handleIncomingRedirect() collects the session information provided by the Solid Identity Provider. Because this information is appended to the URL as query parameters, pass the function the full URL.

  5. After Session.handleIncomingRedirect() returns, your session is logged in.

  6. Once logged in, the Session object provides a fetch() function that retrieves data using available login information.

    You can pass this fetch() function as an option to the solid-client functions (e.g., getSolidDataset, saveSolidDatasetAt) to include the user’s credentials with a request.

  7. To log out a session, call the session’s logout() method.

    Logging out a session removes the session from the storage as well as disables its access to private resources.

  8. To get a list of all the sessions currently in storage, you can call getSessionIdFromStorageAll().

Example

The following Express server example uses the solid-client-authn-node library to log in to a Solid server.

Note

In the example, the cookie-session Express middleware is used to associate the session ID to the user’s browser through a cookie.

const express = require("express");
const cookieSession = require("cookie-session");

const { 
  getSessionFromStorage,
  getSessionIdFromStorageAll,
  Session
} = require("@inrupt/solid-client-authn-node");

const app = express();
const port = 3000;

// The following snippet ensures that the server identifies each user's session
// with a cookie using an express-specific mechanism
app.use(
  cookieSession({
    name: "session",
    // These keys are required by cookie-session to sign the cookies.
    keys: [
      "Required, but value not relevant for this demo - key1",
      "Required, but value not relevant for this demo - key2",
    ],
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  })
);


app.get("/login", async (req, res, next) => {
  // 1. Create a new Session
  const session = new Session();
  req.session.sessionId = session.info.sessionId;
  const redirectHandler = (url) => {
    // Since we use Express in this example, we can call `res.redirect` to send the user to the
    // given URL, but the specific method of redirection depend on your app's particular setup.
    // For example, if you are writing a command line app, this might simply display a prompt for
    // the user to visit the given URL in their browser.
    res.redirect(url);
  };
  // 2. Start the login process; redirect handler will handle sending the user to their
  //    Solid Identity Provider.
  await session.login({
    // After login, the Solid Identity Provider will send the user back to the following
    // URL, with the data necessary to complete the authentication process
    // appended as query parameters:
    redirectUrl: `http://localhost:${port}/handle-redirect`,
    // Set to the user's Solid Identity Provider; e.g., "https://broker.pod.inrupt.com" 
    oidcIssuer: "https://broker.pod.inrupt.com",
    clientName: "Demo app",
    handleRedirect: redirectHandler,
  });
});

app.get("/handle-redirect", async (req, res) => {
  // 3. If the user is sent back to the `redirectUrl` provided in step 2,
  //    it means that the login has been initiated an can be completed. In
  //    particular, initiating the login stores the session in storage, 
  //    which means it can be retrieved as follows.
  const session = await getSessionFromStorage(req.session.sessionId);

  // 4. With your session back from storage, you are now able to 
  //    complete the login process using the data appended to it as query
  //    parameters in req.url by the Solid Identity Provider:
  await session.handleIncomingRedirect(`http://localhost:${port}${req.url}`);

  // 5. `session` now contains an authenticated Session instance.
  if (session.info.isLoggedIn) {
    return res.send(`<p>Logged in with the WebID ${session.info.webId}.</p>`)
  }
});

// 6. Once you are logged in, you can retrieve the session from storage, 
//    and perform authenticated fetches.
app.get("/fetch", async (req, res, next) => {
  const session = await getSessionFromStorage(req.session.sessionId);
  console.log(await (await session.fetch(req.query["resource"])).text());
  res.send("<p>Performed authenticated fetch.</p>");
});

// 7. To log out a session, just retrieve the session from storage, and 
//    call the .logout method.
app.get("/logout", async (req, res, next) => {
  const session = await getSessionFromStorage(req.session.sessionId);
  session.logout();
  res.send(`<p>Logged out.</p>`);
});

// 8. On the server side, you can also list all registered sessions using the
//    getSessionIdFromStorageAll function.
app.get("/", async (req, res, next) => {
  const sessionIds = await getSessionIdFromStorageAll();
  for(const sessionId in sessionIds) {
    // Do something with the session ID...
  }
  res.send(
    `<p>There are currently [${sessionIds.length}] visitors.</p>`
  );
});

app.listen(port, () => {
  console.log(
    `Server running on port [${port}]. ` +
    `Visit [http://localhost:${port}/login] to log in to [broker.pod.inrupt.com].`
  );
});

Node.js Script: Single-User Script

A Node.js script can use the solid-client-authn-node library to handle the user authentication flow.

  1. Create a new Session for the user.

  2. Call the Session.login() function, passing in ILoginInputOptions.

    Although you can pass in options to start the authentication flow, you can also pass in the following options to have an authenticated session without the manual, in-browser redirect interactions:

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

    • An already-registered clientSecret, associated to the Client ID.

    • 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.

    • oidcIssuer, the Solid Identity Provider where your Client ID, Client Secret, and Refresh Token have been registered.

    When login() returns, your session should be logged in and able to make authenticated requests.

Warning

Safeguard your clientId, clientSecret, and refreshToken values. Do not share these with any third parties as anyone with your clientId, clientSecret, and refreshToken values can impersonate you and act fully on your behalf.

Example

The following single-user script calls Session.login() with:

  • clientId, clientSecret, refreshToken, and

  • oidcIssuer.

Warning

Safeguard your clientId, clientSecret, and refreshToken values. Do not share these with any third parties as anyone with your clientId, clientSecret, and refreshToken values can impersonate you and act fully on your behalf.

const { Session } = require("@inrupt/solid-client-authn-node");

// 1. Get the authenticated credentials: myClientId, myClientSecret, myRefreshToken 
// ...
// ...
// Important: Safeguard these credentials.

const session = new Session();
session.login({
  // 2. Use the authenticated credentials to log in the session.
  clientId: myClientId,
  clientSecret: myClientSecret,
  refreshToken: myRefreshToken,
  // Set oidcIssuer to the Solid Identity Provider associated with the credentials.
  oidcIssuer: "https://broker.pod.inrupt.com",
}).then(() => {
  if (session.info.isLoggedIn) {
    // 3. Your session should now be logged in, and able to make authenticated requests.
    session
      // You can change the fetched URL to a private resource, such as your Pod root.
      .fetch(session.info.webId)
      .then((response) => {
        return response.text();
      })
      .then(console.log);
  }
});

Additional Reference

@inrupt/generate-oidc-token Utility

As mentioned in the introduction, Solid authentication involves redirecting the user in a browser to the Solid Identity Provider, and after the user logs in, redirecting the user back to the specified URL.

To help during the development of a single-user Node.js script, Inrupt provides a utility @inrupt/generate-oidc-token. The standalone utility takes a user through the authentication flow and upon successful authentication, outputs the Client ID, Client Secret , and Refresh Token.

Note

  • The utility only supports logging in to Solid-OIDC compliant Identity Provider, such as https://broker.pod.inrupt.com and https://broker.pod-compat.inrupt.com.

  • The utility outputs credentials that expire after 3 days.

  1. In a terminal, run generate-oidc-token utility to log in to a

    npx @inrupt/generate-oidc-token
    

    The utility prompts the user for some information.

  2. Enter the appropriate values to the prompts.

    When finished, the utility asks the user to visit a site in a browser.

  3. Open the browser and visit the site. Log in to the Solid Identity Provider.

  4. Upon login, return to the terminal and you should see the following information:

    • Client ID

    • Client Secret

    • Refresh Token

Warning

Safeguard your Client ID, Client Secret, and Refresh Token values. Do not share these with any third parties as anyone with your Client ID, Client Secret, and Refresh Token values can impersonate you and act fully on your behalf.