-
Notifications
You must be signed in to change notification settings - Fork 4
Usage Examples
You can use this library along the lines of these examples:
I use Vue for my apps. If you want to see how this library is used in a Vue app, look at my Solid App Template (Vue Edition). It should (TM) work the same with the other frameworks. Here is a quick usage example:
Defining a useSolidSession composable e.g. located in ./composables/useSolidSession
import { DynamicRegistrationClientDetails, Session, SessionEvents, SessionExpirationWarningDetail, SessionOptions, SessionStateChangeDetail } from "@uvdsl/solid-oidc-client-browser";
import { Reactive, reactive, readonly } from "vue";
interface SessionState {
isActive: boolean;
webId?: string;
isLoading: boolean;
}
// This reactive object holds the global session state. Components can
// subscribe to this state and will automatically update when it changes.
const sessionState: Reactive<SessionState> = reactive({
isActive: false,
webId: undefined,
isLoading: true,
});
// This handler syncs the external session's state with our reactive Vue state.
const handleStateChange = (event: Event) => {
if (event instanceof CustomEvent) {
const { isActive, webId } = event.detail as SessionStateChangeDetail;
// Any event from the session means the loading phase is complete.
sessionState.isActive = isActive;
sessionState.webId = webId;
sessionState.isLoading = false;
}
};
// This handler warns about session expiration
const handleExpirationWarning = (event: Event) => {
if (event instanceof CustomEvent) {
const { expires_in } = event.detail as SessionExpirationWarningDetail;
console.warn(`[WARN] Session will expire in ${expires_in}s.`);
}
};
// This handler ends the session on expiration
const handleExpiration = (event: Event) => {
if (event instanceof CustomEvent) {
sessionState.isActive = false;
sessionState.webId = undefined;
sessionState.isLoading = false;
}
};
// example client details
const clientDetails: DynamicRegistrationClientDetails = {
redirect_uris: [window.location.href],
client_name: "My Solid App"
};
// Option 1: pass event listeners as session options
const sessionOptions = {
workerUrl: refreshWorkerUrl,
onSessionStateChange: handleStateChange,
onSessionExpirationWarning: handleExpirationWarning,
onSessionExpiration: handleExpiration
} as SessionOptions
// It's recommended to create a single session instance
// that the entire application can share.
const session = new Session(clientDetails, sessionOptions);
// Option 2: pass (additional) event listeners dynamically after session creation (uncomment if wanted)
// session.addEventListener(SessionEvents.STATE_CHANGE, handleStateChange);
// session.addEventListener(SessionEvents.EXPIRATION_WARNING, handleExpirationWarning);
// session.addEventListener(SessionEvents.EXPIRATION, handleExpiration);
// This function handles the initial, asynchronous session restoration.
// It is called only once when this module is first imported.
const initializeSession = async () => {
try {
await session.restore();
} catch (e) {
// This is an expected outcome if no previous session exists.
} finally {
// This block is crucial. It ensures `isLoading` is set to false
// and the state is synchronized after the restore attempt, even if
// `restore()` fails silently and doesn't fire a `stateChange` event.
sessionState.isActive = session.isActive;
sessionState.webId = session.webId;
sessionState.isLoading = false;
}
};
// Start the session restoration process immediately.
initializeSession();
/**
* A Vue Composable to provide access to the global session state and instance.
* @returns An object with the reactive state (readonly) and the session instance.
*/
export function useSolidSession() {
return {
// It's a best practice to return a readonly version of the state
// to prevent components from directly modifying it.
state: readonly(sessionState),
// The session instance is provided for calling methods like login() or logout().
session,
};
}Usage in a component, e.g. with a login button, logout button, ...
import { useSolidSession } from './composables/useSolidSession';
const { session, state } = useSolidSession();
// call on a button click
const redirect_uri = window.location.href;
const idp = "your IDP";
session.login(idp, redirect_uri);
// in code that is being executed
// to handle the redirect after login
session.handleRedirectFromLogin();
// let's have a look if we have a session
watch(() => state.isActive, () => console.log("Logged in:", state.webId), { immediate: true });
// call on a button click
session.logout();import { useState, useEffect } from 'react';
import { Session, SessionEvents, SessionStateChangeDetail } from '@uvdsl/solid-oidc-client-browser';
// It's recommended to create a single, global session instance that the
// entire application can share.
const session = new Session(clientDetails);
/**
* A custom React Hook to subscribe to the session state.
* It handles the initial session restoration and provides reactive
* session data and a loading status.
* @returns An object with { isActive, webId, isLoading }.
*/
function useSession() {
const [sessionData, setSessionData] = useState({
isActive: false,
webId: undefined,
// The isLoading flag prevents UI flicker while the initial,
// asynchronous session restoration is in progress.
isLoading: true,
});
useEffect(() => {
// This handler syncs the external session's state with our internal React state.
const handleStateChange = (event: Event) => {
if (event instanceof CustomEvent) {
const { isActive, webId } = event.detail as SessionStateChangeDetail;
// Any event from the session means the loading phase is complete.
setSessionData({ isActive, webId, isLoading: false });
}
};
session.addEventListener(SessionEvents.STATE_CHANGE, handleStateChange);
// This function handles the initial, asynchronous session restoration.
const initializeSession = async () => {
try {
await session.restore();
} catch (e) {
// This is an expected outcome if no previous session exists.
} finally {
// This block is crucial. It ensures `isLoading` is set to false
// and the state is synchronized after the restore attempt, even if
// `restore()` fails silently and doesn't fire a `stateChange` event.
setSessionData({
isActive: session.isActive,
webId: session.webId,
isLoading: false,
});
}
};
initializeSession();
// The cleanup function removes the event listener when the component unmounts
// to prevent memory leaks.
return () => {
session.removeEventListener(SessionEvents.STATE_CHANGE, handleStateChange);
};
}, []); // The empty dependency array ensures this effect runs only once.
return sessionData;
}Once authenticated, you can use session.authFetch to fetch data from the Web using authenticated requests.
If the session is not yet authenticated, session.authFetch behaves like window.fetch.
There is a small library that provides Solid Requests for get, post, put, delete on resources, and even to create resources with the correct LDP link header, and to create containers with the correct link header - for your convenience.
If you don't want to dabble with parsing the retrieved RDF data manually, check out the Solid RDF Store.
You can use the session object in that store to let the store fetch (authenticated) RDF data from the Web and have reactive query results, i.e. results that can update reactively when query underlying data changes.
Please note the corresponding security considerations.
Currently, your best option for playing around is version 0.1.3.
See also this worker-related issue.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solid Login Page</title>
<script type="module" src="https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js"></script>
</head>
<body>
<div class="container">
<h1>Solid Login Demo</h1>
<p>Click the button below to log in with your Solid identity provider (solidcommunity.net)</p>
<p id="welcome-message">Welcome to the application</p>
<div id="user-info" class="user-info">
<p>WebID: <span id="webid">not logged in.</span></p>
</div>
<button id="loginButton">Login with Solid</button>
<button id="logoutButton">Logout</button>
</div>
<script>
// Initialize the session
let session;
document.addEventListener('DOMContentLoaded', async () => {
const module = await import('https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js');
const Session = module.Session;
const sessionOptions = {
onSessionStateChange: () => {
console.warn("Session state changed!");
document.getElementById('webid').textContent = session.webId;
document.getElementById('welcome-message').textContent =
`State was updated.`;
},
onSessionExpirationWarning: () => {
console.warn("Session is about to expire!");
document.getElementById('welcome-message').textContent = "Warning: Session is expiring soon.";
},
onSessionExpiration: () => {
console.warn("Session is expired!");
document.getElementById('webid').textContent = "not logged in.";
document.getElementById('welcome-message').textContent =
"You're not logged in.";
}
};
// Create a new session
const clientDetails = {
redirect_uris: [window.location.href],
client_name: "uvdsl's Solid App Template"
};
session = new Session(clientDetails, sessionOptions);
// Set up the login button
document.getElementById('loginButton').addEventListener('click', () => {
const idp = "https://solidcommunity.net/";
const redirect_uri = window.location.href; // The URL of this page
session.login(idp, redirect_uri);
});
// Set up the logout button
document.getElementById('logoutButton').addEventListener('click', () => {
session.logout();
document.getElementById('webid').textContent = "not logged in.";
document.getElementById('welcome-message').textContent =
"You're not logged in.";
});
// Handle page revisit
try {
// either: handle redirect after login
await session.handleRedirectFromLogin();
// or: try to restore the session
await session.restore().catch(e => console.log(e.message));
if (session.webId) {
document.getElementById('webid').textContent = session.webId;
document.getElementById('welcome-message').textContent =
`Welcome! You are logged in.`;
} else {
document.getElementById('welcome-message').textContent =
"You're not logged in.";
}
} catch (error) {
console.error("Error restoring session:", error);
document.getElementById('welcome-message').textContent =
"Error restoring session. Please try logging in again.";
}
});
</script>
</body>
</html>Just serve the two files on a web server.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solid Login Page</title>
<script type="module" src="https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px;
cursor: pointer;
border-radius: 4px;
}
.container {
margin-top: 100px;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="container">
<h1>Solid Login Demo</h1>
<p>Click the button below to log in with your Solid identity provider</p>
<button id="loginButton">Login with Solid</button>
<button id="navigateButton">Go to Welcome Page</button>
<button id="logoutButton">Logout</button>
</div>
<script>
// Initialize the session
let session;
document.addEventListener('DOMContentLoaded', async () => {
// Import the Session class from the library
const module = await import('https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js');
const Session = module.Session;
// Create a new session
session = new Session();
// Set up the login button
document.getElementById('loginButton').addEventListener('click', () => {
// Use a default IDP or let user specify one
const idp = "https://solidcommunity.net"; // Default IDP - you can change this
const redirect_uri = window.location.href;
// Redirect to login
session.login(idp, redirect_uri);
});
// Set up the logout button
document.getElementById('logoutButton').addEventListener('click', () => {
session.logout();
alert("Logout! \nWebID: " + session.webId);
});
// Set up the navigation button
document.getElementById('navigateButton').addEventListener('click', () => {
// Navigate to the welcome page
window.location.href = 'welcome.html';
});
// Handle redirect after login
try {
// either: handle redirect after login
await session.handleRedirectFromLogin();
// or: try to restore the session
await session.restore();
if (session.webId) {
console.log("Logged in successfully:", session.webId);
alert("Login successful! \nWebID: " + session.webId);
}
} catch (error) {
console.error("Error handling login redirect:", error);
}
});
</script>
</body>
</html>welcome.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome Page</title>
<script type="module" src="https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.container {
margin-top: 100px;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.user-info {
margin-top: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 4px;
text-align: left;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px;
cursor: pointer;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>Hello!</h1>
<div id="welcome-message">Welcome to the application</div>
<div id="user-info" class="user-info">
<p>WebID: <span id="webid">Not logged in</span></p>
</div>
<button id="back-button">Back to Login Page</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', async () => {
// Import the Session class from the library
const module = await import('https://unpkg.com/@uvdsl/solid-oidc-client-browser@0.1.3/dist/esm/index.min.js');
const Session = module.Session;
// Create a new session
const session = new Session();
// Try to restore the session
try {
// either: handle redirect after login
await session.handleRedirectFromLogin();
// or: try to restore the session
await session.restore();
// Update the UI
if (session.webId) {
document.getElementById('webid').textContent = session.webId;
document.getElementById('welcome-message').textContent =
`Welcome! You are logged in.`;
} else {
document.getElementById('welcome-message').textContent =
"You're not logged in. Please return to the login page.";
}
} catch (error) {
console.error("Error restoring session:", error);
document.getElementById('welcome-message').textContent =
"Error restoring session. Please try logging in again.";
}
// Set up the back button
document.getElementById('back-button').addEventListener('click', () => {
window.location.href = 'index.html';
});
});
</script>
</body>
</html>