Examination of the Code#

The following provides a brief explanation of the Inrupt’s JavaScript client libraries usage in the Getting Started application code.

Login Code#

The example uses Inrupt’s solid-client-authn-browser library to log in. solid-client-authn-browser is for client-side code only.

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

  1. The application starts the login process by sending 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, where the application handles the returned authentication information to complete the login process.

Login Flow: 1) Start Login by redirecting user to Solid Identity Provider. 2) User logs in.  3) Solid Identity Provider redirects the user back to the application to handle the returned authentication information.

Import#

The application uses various objects from @inrupt/solid-client-authn-browser to log in.

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

Start Login#

The application starts the login process by calling login() with the following options:

Important

PodSpaces currently uses version 1.1 of ESS. This documentation provide a preview of PodSpaces that has been upgraded to ESS 2.0 and may not be applicable for use with current PodSpaces.

oidcIssuer

The URL of the user’s Solid Identity Provider. The function sends the user to this URL to log in.

In this example, it is set to the value selected by the user.

Note

If you are not using PodSpaces, modify the select-idp options in the example’s index.html.

redirectUrl

The URL where the user, after logging in, will be redirected in order to finish the login process.

In this example, it is set to window.location.href (i.e., the current page of the application).

clientName

A user-friendly application name to be passed to the Solid Identity Provider. The value is displayed in the Identity Provider’s access request approval window.

function loginToSelectedIdP() {
  const SELECTED_IDP = document.getElementById("select-idp").value;

  return login({
    oidcIssuer: SELECTED_IDP,
    redirectUrl: window.location.href,
    clientName: "Getting started app"
  });
}

The login() function sends the user to the Solid Identity Provider specified in oidcIssuer. Once logged in at the identity provider, the user is redirected back to the specified redirectURL to finish the login process.

Finish Login#

Once logged in at the Solid Identity Provider, the user is redirected back to the redirectURL. The redirectURL is specified in the login() function call at the start of the login process. The page at this redirectURL (in this example, the current page of the application) calls handleRedirectAfterLogin() which uses handleIncomingRedirect() to complete the login process:

// When redirected after login, finish the process by retrieving session information.
async function handleRedirectAfterLogin() {
  await handleIncomingRedirect();

  const session = getDefaultSession();
  if (session.info.isLoggedIn) {
    // Update the page with the status.
    document.getElementById("myWebID").value = session.info.webId;

    // Enable Read button to read Pod URL
    buttonRead.removeAttribute("disabled");
  }
}

// The example has the login redirect back to the index.html.
// This calls the function to process login information.
// If the function is called when not part of the login redirect, the function is a no-op.
handleRedirectAfterLogin();

The handleIncomingRedirect() function collects the information provided by the Identity Provider.

For more information on using the library to authenticate, see Authentication.

Get Pod(s) Code#

The example uses Inrupt’s solid-client library to return the Pod(s) associated with a WebID.

Import#

The application uses various objects from @inrupt/solid-client. Additional import objects are displayed for the other read and write operations used in the application.

import {
  addUrl,
  addStringNoLocale,
  createSolidDataset,
  createThing,
  getPodUrlAll,
  getSolidDataset,
  getThingAll,
  getStringNoLocale,
  removeThing,
  saveSolidDatasetAt,
  setThing
} from "@inrupt/solid-client";

import { SCHEMA_INRUPT, RDF, AS } from "@inrupt/vocab-common-rdf";

Get Pods#

The application uses getPodUrlAll to retrieve the Pod URLs (i.e., the value stored under http://www.w3.org/ns/pim/space#storage) in the user’s profile.

const webID = document.getElementById("myWebID").value;
const mypods = await getPodUrlAll(webID, { fetch: fetch });

Write Reading List#

Import#

The application uses various objects from solid-client and vocab-common-rdf libraries to write data to your Pod. Additional import objects are shown for read profile operations.

import {
  addUrl,
  addStringNoLocale,
  createSolidDataset,
  createThing,
  getPodUrlAll,
  getSolidDataset,
  getThingAll,
  getStringNoLocale,
  removeThing,
  saveSolidDatasetAt,
  setThing
} from "@inrupt/solid-client";

import { SCHEMA_INRUPT, RDF, AS } from "@inrupt/vocab-common-rdf";

Create Reading List SolidDataset#

The application uses getSolidDataset to retrieve an existing reading list from the URL.

  • If found, the application uses removeThing to clear the reading list by removing all titles from the list.

  • If not found (i.e., errors with 404 ), the application uses createSolidDataset to create a new SolidDataset (i.e., the reading list).

  let myReadingList;

  try {
    // Attempt to retrieve the reading list in case it already exists.
    myReadingList = await getSolidDataset(readingListUrl, { fetch: fetch });
    // Clear the list to override the whole list
    let items = getThingAll(myReadingList);
    items.forEach((item) => {
      myReadingList = removeThing(myReadingList, item);
    });
  } catch (error) {
    if (typeof error.statusCode === "number" && error.statusCode === 404) {
      // if not found, create a new SolidDataset (i.e., the reading list)
      myReadingList = createSolidDataset();
    } else {
      console.error(error.message);
    }
  }

Tip

As an alternative to fetching an existing reading list and removing all titles from the list, you can instead attempt to deleteSolidDataset first, and then use createSolidDataset.

Add Items (Things) to Reading List#

For each title entered by the user:

  • The application uses createThing to create a new reading item (i.e., Thing). 1

  • To the item, the application uses the following functions to add specific data:

    • addUrl to add the http://www.w3.org/1999/02/22-rdf-syntax-ns#type property with the URL value https://www.w3.org/ns/activitystreams#Article

      The example uses the RDF.type and AS.Article convenience objects to specify the aforementioned property and value.

    • addStringNoLocale to add the https://schema.org/name property with the string value set to one of the entered titles.

      The example uses the SCHEMA_INRUPT.name convenience object for the aforementioned property.

  • Then, the application uses setThing to add the item to the SolidDataset (i.e., the reading list).

  let i = 0;
  titles.forEach((title) => {
    if (title.trim() !== "") {
      let item = createThing({ name: "title" + i });
      item = addUrl(item, RDF.type, AS.Article);
      item = addStringNoLocale(item, SCHEMA_INRUPT.name, title);
      myReadingList = setThing(myReadingList, item);
      i++;
    }
  });

The solid-client library’s functions (such as the various add/set functions) do not modify the objects that are passed in as arguments. Instead, the library’s functions return a new object with the requested changes.

1

The application specifies the Thing’s name (optional) during its instantiation. Typically, a Thing’s URL is its SolidDataset URL (which ends with a /) appended with # and the Thing’s name; in this case:

  • ${podURL}getting-started/readingList/myList#title1,

  • ${podURL}getting-started/readingList/myList#title2, etc.

Save Reading List (SolidDataset)#

Tip

For the sake of simplicity and brevity, this getting started guide hardcodes the SolidDataset URL. In practice, you should add a link to this resource in your profile that applications can follow.

Use saveSolidDatasetAt to save the reading list to <PodURL>getting-started/readingList/myList. saveSolidDatasetAt creates any intermediate folders/containers as needed. 2

let savedReadingList = await saveSolidDatasetAt(
  readingListUrl,
  myReadingList,
  { fetch: fetch }
);

Upon successful save, saveSolidDatasetAt returns a SolidDataset whose state reflects the data that was sent to be saved.

See also Create vs. Update Operations.

2

The solid-client library also provides the saveSolidDatasetInContainer. However, unlike saveSolidDatasetAt which creates any intermediate folders/containers as needed, saveSolidDatasetInContainer requires that the specified destination container already exists.

Verify the Save Operation#

The save operation returns the SolidDataset (the reading list) whose state reflect the data that was sent to be saved. The savedReadingList may not accurately reflect the saved state of the data if concurrent operations have modified additional fields.

To ensure you have the latest data, the tutorial uses getSolidDataset again after saving the data.

savedReadingList = await getSolidDataset(readingListUrl, { fetch: fetch });

let items = getThingAll(savedReadingList);

let listcontent = "";
for (let i = 0; i < items.length; i++) {
  let item = getStringNoLocale(items[i], SCHEMA_INRUPT.name);
  if (item !== null) {
    listcontent += item + "\n";
  }
}

document.getElementById("savedtitles").value = listcontent;

The application uses SCHEMA_INRUPT.name convenience object from the vocab-common-rdf library to specify the property to retrieve.