Authenticate (Node.js Web Server)#
Inrupt provides the @inrupt/solid-client-authn-node
library to
authenticate in Node.js.
npm install @inrupt/solid-client-authn-node
For applications implementing Authorization Code Flow:
The application starts the login process by sending the user to the user’s Solid Identity Provider.
The user logs in to the Solid Identity Provider.
The Solid Identity Provider sends the user back to your application, where the application handles the returned authentication information to complete the login process.

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 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() function to start the login process. Pass in the following login options:
Set to the user’s Solid Identity Provider (where
handleRedirect
will send the user).Set to the location that the Solid Identity Provider will send the user back once logged in.
Set to a callback function that sends users to their Solid Identity Provider.
(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
clientName
is the name displayed during the approval step. IfclientName
is not provided, a random identifier is generated and used for the name.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.
- Completing the authentication flow:
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(), 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.
6. After Session.handleIncomingRedirect() returns, your session is logged in.
- Making an authenticated requests:
7. 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.- Logging a session out:
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
.Warning
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.
Call the session’s logout() method. Logging a session out removes it from the in-memory storage as well as disabling its access to private resources.
- Getting a list of all the sessions currently in 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.
Warning
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.
Call getSessionIdFromStorageAll(). This function return the session identifiers in the legacy in-memory storage. These can then be used calling getSessionFromStorage().
Managing the Session tokens#
Warning
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.
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 theevents
emitter of theSession
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 aSessionTokenSet
object containing information about the new tokens, including the access token, ID token, refresh token, and expiration information.See Storing tokens for more information about storing tokens.
- Injecting the tokens into a
Session
Session.fromTokens
is a static function that builds aSession
instance from aSessionTokenSet
object. If the tokens are not expired, the obtainedSession
instance is able to make authenticated requests:session.info.isLoggedIn
istrue
. If the tokens have expired, first use therefreshTokens
function to refresh the tokens, and then callSession.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:
Token Type |
Description |
---|---|
Short-lived tokens |
|
Long-lived token |
|
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 server example uses
the @inrupt/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({
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=<resource URL>`.</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);
const session = await Session.fromTokens(
sessionTokenSet,
req.session.sessionId,
);
session.logout();
res.send(`<p>Logged out.</p>`);
});
app.listen(port, () => {
console.log(
`Server running on port [${port}]. ` +
`Visit [http://localhost:${port}/login] to log in to [login.inrupt.com].`
);
});