# Authentication Server Side

Inrupt provides the `solid-client-authn-node` library to authenticate in Node.js.

```
npm install @inrupt/solid-client-authn-node
```

For applications implementing [Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowSteps):

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.

<figure><img src="https://2584838151-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLMLxFYifBOpjrf8rQMX1%2Fuploads%2Fgit-blob-207f8c5cf3aef0f5dea5f8a1e9e63cc211e63e67%2Flogin-flow.png?alt=media" alt=""><figcaption></figcaption></figure>

### Node.js Web Server: Multi-session Management

A Node.js web server can use the `@inrupt/solid-client-authn-node` library to handle the user authentication flow and manage multiple sessions. In a multi-session context, the server maps requests to sessions. Typically, this is done attaching a cookie to the user’s browser.

From a session lifecycle perspective, there are two main types of requests:

* those changing the session status (logging in or out),
* and those performing an authenticated request from the session, without modifying its status.

By default, within the library code, all the session state is stored in memory, and lost on server restart. To persist the session state in external storage, you will need to register listeners for the `authorizationRequest` and `newTokens` events (see the dedicated section). These events allow you to capture the state needed to complete the login process and retrieve sessions in a clustered deployment where a sequence of requests may be directed to different nodes.

#### **Starting the authentication flow:**

1. Create a new [Session](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#constructor) for the user at the login endpoint. By default, the Session is *periodically* refreshed in the background using the refresh token. You should override this legacy behavior by specifying `keepAlive: false` as a Session option to the Session constructor.\
   \
   At this point, you should associate the user’s browser to the `Session` identifier via a cookie, as the identifier is required in subsequent steps.\
   \
   You should also ensure that you capture the `authorizationRequest` event and persist the object it returns in external storage. This event is emitted as the user is redirected to their OpenID Provider. The payload contains the state of the session at this stage of the login process so that the login can be completed upon redirect from the OpenID Provider back to the application.
2. Call the [Session.login()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#login) function to start the login process. Pass in the following login options:\
   \\

   <table data-header-hidden><thead><tr><th width="163.3671875"></th><th></th></tr></thead><tbody><tr><td><a href="https://inrupt.github.io/solid-client-authn-js/node/interfaces/ILoginInputOptions.html#oidcissuer">oidcIssuer</a></td><td>Set to the user’s Solid Identity Provider (where <code>handleRedirect</code> will send the user).</td></tr><tr><td><a href="https://inrupt.github.io/solid-client-authn-js/node/interfaces/ILoginInputOptions.html#redirecturl">redirectUrl</a></td><td>Set to the location that the Solid Identity Provider will send the user back once logged in.</td></tr><tr><td><a href="https://inrupt.github.io/solid-client-authn-js/node/interfaces/ILoginInputOptions.html#handleredirect">handleRedirect</a></td><td>Set to a callback function that sends users to their Solid Identity Provider.</td></tr><tr><td><a href="https://inrupt.github.io/solid-client-authn-js/node/interfaces/ILoginInputOptions.html#clientname">clientName</a></td><td>(Optional) Set to the display name for the client during the login process. When logging in, the user has to approve the client’s access to the requested data. The <code>clientName</code> is the name displayed during the approval step. If <code>clientName</code> is not provided, a random identifier is generated and used for the name.</td></tr><tr><td><code>customScopes</code></td><td>(Optional) Set of custom scopes requested by the client in addition to the default ones. This allows for application-specific claims to be added to the ID Token by the OpenID Provider.</td></tr></tbody></table>

   \
   For other options available to the function, see [ILoginInputOptions](https://inrupt.github.io/solid-client-authn-js/node/interfaces/ILoginInputOptions.html).\
   \
   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.

***

**Building a `Session` object**

Once the login process has been initiated, the `Session` needed to handle the redirect from the Solid Identity Provider has to be rebuilt in the context of the operations managing the login process. There are multiple approaches to building the `Session` object at that stage:

{% tabs %}
{% tab title="Using an external storage" %}
{% hint style="danger" %}
This requires using a [Solid-OIDC Client ID](https://solid.github.io/solid-oidc/#clientids-document). Make sure your Client Identifier is a URL pointing to a Client Identifier Document.
{% endhint %}

The `Session` class has a static method `Session.fromAuthorizationRequestState` returning a `Session` instance. You should call it providing the `authorizationRequestState` from your server’s persistent storage.
{% endtab %}

{% tab title="Using legacy in-memory storage" %}
{% hint style="danger" %}
The legacy in-memory storage relies on the process memory being consistent. This means it is not a suitable approach in a cluster deployment where the same session could have requests handled by different nodes.
{% endhint %}

To retrieve a user’s session, `@inrupt/solid-client-authn-node` provides the [getSessionFromStorage()](https://inrupt.github.io/solid-client-authn-js/node/functions.html#getsessionfromstorage) function. It takes a Session ID as its argument, and returns a `Session` instance.
{% endtab %}
{% endtabs %}

***

#### **Completing the authentication flow:**

3. Retrieve the session using one of the methods above.
4. Set up a listener to capture the tokens created during the login process and persist them in external storage if you are using this approach (preferred).
5. To complete the login process, call [Session.handleIncomingRedirect()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#handleincomingredirect), passing in the URL of the page handling the redirect. [Session.handleIncomingRedirect()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#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.
6. After [Session.handleIncomingRedirect()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#handleincomingredirect) returns, your session is logged in.

***

**Building a `Session` object**

Once the `Session` has been logged in, it has to be rebuilt in the context of the operations making use of the user’s credentials to perform authenticated requests. There are multiple approaches to building the `Session` object at that stage:

{% tabs %}
{% tab title="Using an external storage" %}
{% hint style="danger" %}
This requires using a [Solid-OIDC Client ID](https://solid.github.io/solid-oidc/#clientids-document). Make sure your Client Identifier is a URL pointing to a Client Identifier Document.
{% endhint %}

The `Session` class has a static method `Session.fromTokens` returning a `Session` instance. You should call it providing tokens from your server’s persistent storage.
{% endtab %}

{% tab title="Using legacy in-memory storage" %}
{% hint style="danger" %}
The legacy in-memory storage relies on the process memory being consistent. This means it is not a suitable approach in a cluster deployment where the same session could have requests handled by different nodes.
{% endhint %}

To retrieve a user’s session, `@inrupt/solid-client-authn-node` provides the [getSessionFromStorage()](https://inrupt.github.io/solid-client-authn-js/node/functions.html#getsessionfromstorage) function. It takes a Session ID as its argument, and returns a `Session` instance. By default, `getSessionFromStorage` will refresh the session. You can disable this behavior to manually control the refreshing of the session by setting `refreshSession: false` in the second argument of `getSessionFromStorage`.
{% endtab %}
{% endtabs %}

***

#### **Making an authenticated requests:**

7. Once logged in, the `Session` object provides a [fetch()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#fetch) function that retrieves data using available login information.\
   \
   You can pass this [fetch()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#fetch) function as an option to the `solid-client` functions (e.g., [getSolidDataset](https://inrupt.github.io/solid-client-js/modules/resource_solidDataset.html#getsoliddataset), [saveSolidDatasetAt](https://inrupt.github.io/solid-client-js/modules/resource_solidDataset.html#savesoliddatasetat)) to include the user’s credentials with a request.

#### **Logging a session out:**

{% tabs %}
{% tab title="Using an external storage" %}
By default, the application may log the user out by clearing the resources associated to the user session. This is an ad-hoc process, specific to the application session management mechanism.

8\. In addition, the application may log the user out of their OpenID Provider (see the Session Lifecycle section) using the `logout` function exposed by `@inrupt/solid-client-authn-node`.
{% endtab %}

{% tab title="Using legacy in-memory storage" %}
{% hint style="danger" %}
The legacy in-memory storage relies on the process memory being consistent. This means it is not a suitable approach in a cluster deployment where the same session could have requests handled by different nodes.
{% endhint %}

8. Call the session’s [logout()](https://inrupt.github.io/solid-client-authn-js/node/classes/Session.html#logout) method. Logging a session out removes it from the in-memory storage as well as disabling its access to private resources.
   {% endtab %}
   {% endtabs %}

#### **Getting a list of all the sessions currently in storage:**

{% tabs %}
{% tab title="Using an external storage" %}
How session identifiers and tokens are managed by the external persistent storage is out of scope of the library. Listing these sessions is dependent of the specifics of the chosen storage.
{% endtab %}

{% tab title="Using legacy in-memory storage" %}
{% hint style="danger" %}
The legacy in-memory storage relies on the process memory being consistent. This means it is not a suitable approach in a cluster deployment where the same session could have requests handled by different nodes.
{% endhint %}

Call [getSessionIdFromStorageAll()](https://inrupt.github.io/solid-client-authn-js/node/functions.html#getsessionidfromstorageall). This function return the session identifiers in the legacy in-memory storage. These can then be used calling [getSessionFromStorage()](https://inrupt.github.io/solid-client-authn-js/node/functions.html#getsessionfromstorage).
{% endtab %}
{% endtabs %}

### Managing the Session tokens

{% hint style="danger" %}
Tokens are very sensitive pieces of information because they allow access to user data. They must be stored securely: no third-party should have access to the tokens in storage.
{% endhint %}

#### Exchanging tokens with a `Session`

**Getting the tokens from the `Session`**

When new tokens are issued (on login or on refresh), the `newTokens` event is emitted by the `events` emitter of the `Session` instance. This event is specific to the Node.js environment, it is available in addition to the common events described in the Session Events section of the Authentication documentation.

When listening for the `newTokens` event, your callback will receive a `SessionTokenSet` object containing information about the new tokens, including the access token, ID token, refresh token, and expiration information.

**Injecting the tokens into a `Session`**

`Session.fromTokens` is a static function that builds a `Session` instance from a `SessionTokenSet` object. If the tokens are not expired, the obtained `Session` instance is able to make authenticated requests: `session.info.isLoggedIn` is `true`. If the tokens have expired, first use the `refreshTokens` function to refresh the tokens, and then call `Session.fromTokens` with the new tokens. Do not forget to update persistent storage with the new tokens as well.

#### Storing tokens

As part of its authentication lifecycle, the session makes use of two types of tokens:

<table><thead><tr><th width="184.7109375">Token Type</th><th>Description</th></tr></thead><tbody><tr><td><strong>Short-lived tokens</strong></td><td><ul><li><strong>Access Token</strong>: Used to authenticate API requests to protected resources. Typically valid for a short period (minutes to hours).</li><li><strong>ID Token</strong>: Contains user identity information, used for authentication purposes. Has a similar short lifespan to the Access Token.</li></ul></td></tr><tr><td><strong>Long-lived token</strong></td><td><ul><li><strong>Refresh Token</strong>: Used to obtain new Access and ID tokens when they expire, without requiring the user to log in again. Usually valid for a longer period (days to weeks).</li></ul></td></tr></tbody></table>

The Access Token is used directly by the `Session` to perform authenticated requests. For performance reasons, your application may cache the short-lived tokens to reuse them across multiple requests from an authenticated user.

The Refresh Token is used by the `Session` to refresh an expired Access Token. Refreshing a token requires a network round-trip with the Identity Provider. The Refresh Token is typically rotated when used: the Identity Provider issues a new Refresh Token when refreshing an Access Token, and the previous Refresh Token can no longer be used. In order to be able to perform authenticated operations without the user being present, the Refresh Token should be stored in a persistent storage.

### Example

The following [Express](https://expressjs.com/) server example uses the `@inrupt/solid-client-authn-node` library to log in to a Solid server.

{% hint style="info" %}
In the example, the `cookie-session` Express middleware is used to associate the session ID to the user’s browser through a cookie.
{% endhint %}

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

const {
  Session,
  logout
} = 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({
    keys: ["some secret used to sign cookies"],
  })
);

// For simplicity, all tokens and session state are stored in-memory. In a real case,
// persistent storage would be used for long-lived tokens.
const sessionCache = new Map();

app.get("/login", async (req, res) => {
  // 1. Create a new Session and ensure the request state is captured.
  const session = new Session({ keepAlive: false }); // Turn off periodic refresh of the Session in background
  req.session.sessionId = session.info.sessionId;
  session.events.on("authorizationRequest", (authorizationRequestState) => {
    sessionCache.set(req.session.sessionId, authorizationRequestState);
  });
  const redirectToSolidIdentityProvider = (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 depends 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; the 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}/login/callback`,
    // Set to the user's Solid Identity Provider; e.g., "https://login.inrupt.com"
    oidcIssuer: "https://login.inrupt.com",
    // Set to you application's Client Identifier
    clientId: "https://example.org/client-id",
    handleRedirect: redirectToSolidIdentityProvider,
  });
});

app.get("/login/callback", 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 and can be completed. In
  //    particular, initiating the login stores the session state in storage,
  //    which means it can be retrieved as follows.
  const authorizationRequestState = sessionCache[req.session.sessionId];
  const session = await Session.fromAuthorizationRequestState(
      authorizationRequestState,
      req.session.sessionId
  );

  // 4. Ensure the tokens are cached.
  session.events.on("newTokens", (tokenSet) => {
    sessionCache.set(req.session.sessionId, tokenSet);
  });

  // 5. 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}`);

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

// 7. Once you are logged in, you can retrieve the tokens from the cache,
//    and perform authenticated fetches.
app.get("/fetch", async (req, res) => {
  if(typeof req.query["resource"] === "undefined") {
    res.send(
      "<p>Please pass the (encoded) URL of the Resource you want to fetch using `?resource=&lt;resource URL&gt;`.</p>"
    );
  }
  const sessionTokenSet = sessionCache.get(req.session.sessionId);
  const session = await Session.fromTokens(
    sessionTokenSet,
    req.session.sessionId,
  );
  console.log(
    await session.fetch(req.query["resource"])
      .then((response) => response.text())
  );
  res.send("<p>Performed authenticated fetch.</p>");
});

// 8. To log out a session, just retrieve the session, and
//    call the .logout method.
app.get("/logout", async (req, res) => {
  const sessionTokenSet = sessionCache.get(req.session.sessionId);
  sessionCache.delete(req.session.sessionId);
  await logout(
    sessionTokenSet,
    (url) => {
      res.redirect(url);
    }
  );
});

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