First published 2025.10.23
Change Log Part 2: Breaking Up the Platform
Welcome back to Change Log, an ongoing series where we write a web application week by week, showing step-by-step how we can create a new, post-platform ecosystem for the web.
Last week in Part 1 we set up our project, putting our software development loop in place with our production environment.
This week we’ll talk about what makes Stucco apps “post-platform”, and demonstrate how to integrate an authentication and storage provider to our app.
What Does it Mean to be Post-Platform?
In a traditional web platform application, we create an account on the application itself. Usually this means creating a new username and password for the app – sometimes it means logging in with something like our Gmail account. Either way, our account lives on the application’s web servers, inside the application’s database.
As we use the application we create, read, update, and delete data, this data also lives inside the applications database. Now everything you create with the application belongs to the application.
What If We Made a New Deal?
We envision a different kind of web application. In our new model, we control our own identity and data, either through cooperatively run providers or local institutions.
Any provider can log in to any application. These applications then rely on the provider for creating, reading, updating, and deleting data. The data lives with the provider, not the application.
The web application doesn’t need to manage any user accounts, doesn’t need to secure or protect any user data, doesn’t even need to have a server any more. These applications don’t need to know anything about their users, and never need to touch any sensitive user data.
The Stucco Software Data Coop
Stucco Software operates a data coop where anyone can join and participate in owning their own data & identity. We call it SolidID.
We’ll demonstrate how to integrate a provider like this into the Pocket Change application
Logging In
Set up the client details to ensure we don’t get authenticated users hijacked out of our application. We only allow authentication on the defined URLs.
// static/client.json
{
"@context": [
"https://www.w3.org/ns/solid/oidc-context.jsonld"
],
"client_id": "…",
"client_name": "…",
"redirect_uris": [
"http://localhost:5173/auth/confirm",
"…"
],
"post_logout_redirect_uris": [
"http://localhost:5173",
"…"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"scope": "openid webid offline_access",
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"application_type": "web",
"require_auth_time": false
} Deploy this, since it needs to be accessible on the public web before we go any further.
Now we add our environment variables – where is the client information, this application, and who is the default provider.
// .env
PUBLIC_CLIENT_ID=…
PUBLIC_HOST=http://localhost:5173/
PUBLIC_PROVIDER=https://login.stucco.software/ Now we need to set up some internal state management for SvelteKit, which we’ll use to conditionally show or hide parts of the application as appropriate.
// lib/session.svelte.js
export let clientSession = $state({
data: null
})
export const setSession = (user) => {
clientSession.data = user
return clientSession
} Using this state object, we add the UI for the log in flows.
// +layout.svelte
<script>
import { clientSession } from "$lib/session.svelte.js"
let { children } = $props();
</script>
<nav>
<a href="/">Home</a>
{#if clientSession.data?.info?.isLoggedIn}
<a href="/">Log Out</a>
{:else}
<a href="/auth">Log In</a>
{/if}
</nav>
{@render children?.()}
<style>
nav {
display: flex;
justify-content: space-between;
}
</style> This gives us a header nav on the app that shows a log in or log out button, depending on if anyone is authenticated.
Next, that log in button has to have somewhere to go. We’ll make an /auth page, and set yup a log in button that calls the function we need to actually do the authentication.
// /routes/auth/+page.svelte
<script type="text/javascript">
import {
PUBLIC_CLIENT_ID,
PUBLIC_HOST,
PUBLIC_PROVIDER
} from '$env/static/public'
import {
login
} from '@inrupt/solid-client-authn-browser'
let { data } = $props()
const startLogin = async () => {
await login({
clientId: PUBLIC_CLIENT_ID,
redirectUrl: `${PUBLIC_HOST}auth/confirm`,
oidcIssuer: PUBLIC_PROVIDER,
clientName: "Pocket Change"
})
}
</script>
<h1>
Sign In
</h1>
<h2>With Stucco Software SolidID</h2>
<button onclick={startLogin}>Sign In With SolidID</button> Note that we’re using the @inrupt/solid-client-authn-browser library’s login function, so we’ll need to install that as a dependency:
npm install --save-dev @inrupt/solid-client-authn-browser Our login button will no redirect us to our authentication provider, which will prompt us to allow this application access. Once we do, we’ll be redirected back to the /auth/confirm page so we better make that next.
// routes/auth/confirm/+page.svelte
<script>
import { goto } from "$app/navigation"
import { onMount } from 'svelte'
import {
EVENTS,
getDefaultSession,
handleIncomingRedirect
} from '@inrupt/solid-client-authn-browser'
import { setSession } from "$lib/session.svelte.js"
let session
onMount(async () => {
session = getDefaultSession()
session.events.on(EVENTS.SESSION_RESTORED, (url) => {
setSession(session)
goto(url)
})
setSession(session)
goto( '/')
})
</script>
<h1>
Logging in…
</h1> This will validate the authentication, and redirect the user to the application.
We need to do one more thing: when the SvelteKit application starts from a fresh page load – like, for instance, the user is sent to our callback page from the auth provider, we need to run the handleIncomingRedirect function.
We put this on any page load, rather than just the callback page with the restorePreviousSession: true option so we can keep our user logged in between hard page reloads.
// hooks.client.js
import {
handleIncomingRedirect
} from '@inrupt/solid-client-authn-browser'
export const init = async () => {
// On Page refresh, check if we can restore a session.
await handleIncomingRedirect({
restorePreviousSession: true
})
}; And thats it! We can now log in to our Pocket Change application with a username and password provided by our Data Coop!
Next Week On Change Log
Now that we’re logged in, we need to do stuff. Next week we’ll look at those basic create, read, update, and delete operations.