sharing an experiment.
created with Noel’s inspiration and Gemini 2.5 Pro.
deployed to Solid Login App (Working Pattern)
does nothing else than displaying foaf:name
hardcoded to https://teamid.live
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Solid Login App (Working Pattern)</title>
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>
<body>
<main>
<h1>Solid Login</h1>
<!-- Shown while the session is being checked -->
<div id="loading">
<p>Initializing session...</p>
</div>
<!-- Shown only to logged-out users -->
<div id="auth-guest" hidden>
<p>You are not logged in. Please log in to continue.</p>
<button id="login-button">Login with teamid.live</button>
</div>
<!-- Shown only to logged-in users -->
<div id="auth-user" hidden>
<p>Hello, <strong id="username"></strong>!</p>
<button id="logout-button">Log out</button>
</div>
</main>
<!--
The necessary libraries, using the current versions from august 2025.
The order is critical: Libraries first, then our application logic.
-->
<script src="https://cdn.jsdelivr.net/npm/@inrupt/solid-client-authn-browser@3.1.0/dist/solid-client-authn.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/n3@1.26.0/browser/n3.min.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
// --- SECTION 1: CONFIGURATION ---
const SOLID_OIDC_ISSUER = "https://teamid.live";
const FOAF_NAME_PREDICATE = "http://xmlns.com/foaf/0.1/name";
// --- SECTION 2: UI ELEMENT REFERENCES ---
const loadingDiv = document.getElementById('loading');
const guestDiv = document.getElementById('auth-guest');
const userDiv = document.getElementById('auth-user');
const loginButton = document.getElementById('login-button');
const logoutButton = document.getElementById('logout-button');
const usernameSpan = document.getElementById('username');
// --- SECTION 3: CORE SOLID LOGIC ---
/**
* Restores a previous session or handles the redirect from the login provider.
* This is the main entry point of the application.
*/
async function restoreSession() {
try {
// Handle the redirect from the Solid Identity Provider.
await solidClientAuthentication.handleIncomingRedirect({ restorePreviousSession: true });
// Get the current session information.
const session = solidClientAuthentication.getDefaultSession();
// If not logged in, show the guest view and stop.
if (!session.info.isLoggedIn) {
updateUI(false);
return;
}
// If logged in, fetch the user's name and update the UI.
const user = await fetchUserProfile(session.info.webId);
updateUI(true, user.name);
} catch (error) {
alert(error.message);
updateUI(false); // If an error occurs, show the guest view.
}
}
/**
* Fetches the user's name from their Solid profile.
* @param {string} webId - The WebID of the user.
* @returns {Promise<object>} An object containing the user's name.
*/
async function fetchUserProfile(webId) {
// This function uses the `readSolidDocument` helper below.
const profileQuads = await readSolidDocument(webId);
// Find the quad containing the foaf:name.
const nameQuad = profileQuads.find(quad => quad.predicate.value === FOAF_NAME_PREDICATE);
return {
name: nameQuad?.object.value || 'Anonymous'
};
}
/**
* A low-level helper to fetch and parse a Solid document.
* @param {string} url - The URL of the document to read.
* @returns {Promise<Array>} An array of quads from the parsed document.
*/
async function readSolidDocument(url) {
// Use the authenticated fetch from the library.
const response = await solidClientAuthentication.fetch(url, { headers: { Accept: 'text/turtle' } });
if (Math.floor(response.status / 100) !== 2) return []; // Return empty on error.
const data = await response.text();
// The n3.min.js bundle exposes the Parser directly on the global N3 object.
const parser = new N3.Parser({ baseIRI: url });
return parser.parse(data);
}
// --- SECTION 4: UI AND EVENT HANDLING ---
/**
* Updates the user interface based on the login state.
* @param {boolean} isLoggedIn - Whether the user is logged in.
* @param {string} [name] - The user's name, if logged in.
*/
function updateUI(isLoggedIn, name) {
loadingDiv.setAttribute('hidden', ''); // Hide loading message
if (isLoggedIn) {
guestDiv.setAttribute('hidden', '');
userDiv.removeAttribute('hidden');
usernameSpan.textContent = name;
} else {
userDiv.setAttribute('hidden', '');
guestDiv.removeAttribute('hidden');
}
}
// Attach event listeners to buttons.
loginButton.onclick = () => {
solidClientAuthentication.login({
oidcIssuer: SOLID_OIDC_ISSUER,
redirectUrl: window.location.href,
clientName: 'Hello World Solid App'
});
};
logoutButton.onclick = async () => {
logoutButton.setAttribute('disabled', '');
await solidClientAuthentication.logout();
logoutButton.removeAttribute('disabled');
updateUI(false); // Reset to guest view.
};
// --- SECTION 5: START THE APPLICATION ---
restoreSession();