Login, session restore and logout against a Solid identity provider use
Solid-OIDC, which builds on
OpenID Connect
and DPoP;
standards.md lists their status. The authentication
code is in
packages/solid/src/solidSessionGateway.ts,
behind the SessionGateway port. It signs in through the sign-in client
the composition root passes it, the one in
client/session.ts, the only
file that calls the authentication library.
The learner enters only a WebID
(its profile in Solid); the
onboarding flow validates it as an https: URL. The
app reads the WebID document unauthenticated, takes the issuer
(https: too) from its solid:oidcIssuer triple, and starts the OIDC
redirect flow. The issuer is never inferred from the WebID's
origin: a WebID on alice.datapod.igrant.io has the issuer
datapod.igrant.io.
For a suggested provider, loginWithIssuer skips the profile lookup
and starts at that issuer.
The app never handles credentials: no password field, no client secret,
no hand-rolled OIDC. The authn library runs the authorization-code +
PKCE flow. On its own domain the app names itself by its
Client ID Document; elsewhere, or with a
provider whose scopes_supported lacks webid, the library registers
dynamically under the site's name (VITE_APP_NAME,
configuration.md). The provider returns the learner to
the page's address without index.html or a hash.
sequenceDiagram
accTitle: Logging in with Solid-OIDC
accDescr: The user enters a WebID, the gateway finds its identity provider, the provider redirects back with a code, and the gateway exchanges the code for a session.
actor User
participant UI
participant Gateway as SolidSessionGateway
participant Pod as WebID document
participant IdP as Identity provider
User->>UI: enter WebID, submit
UI->>Gateway: login(webId)
Gateway->>Pod: GET WebID document
Pod-->>Gateway: profile (solid:oidcIssuer)
Gateway->>IdP: redirect (authorization request)
IdP->>User: login + consent
IdP->>UI: redirect back with code
Note over UI,Gateway: page reloads
UI->>Gateway: restore()
Gateway->>IdP: handleIncomingRedirect (token exchange)
IdP-->>Gateway: session (WebID)
Gateway-->>UI: { session: { webId }, origin: "login" }
Solid-OIDC lets an app name itself by the address of a JSON-LD document describing it and its redirect URLs (Solid-OIDC, section 5). Solid Memo's is at https://solid-memo.com/clientid.jsonld. clientId.ts writes it at build time for the domain in CNAME, like the sitemap (deployment.md):
| Field | Value |
|---|---|
client_id |
Its own address, https://<domain>/clientid.jsonld. |
client_name |
The site's name (VITE_APP_NAME). |
redirect_uris |
The site's root, https://<domain>/, alone: the app is one page. |
client_uri, logo_uri |
The site's root, and its picture, solid-memo-avatar.png. |
scope, grant_types, response_types |
What the library asks for: openid offline_access webid, the authorization code and refresh grants, code. |
token_endpoint_auth_method |
none: the app has no secret. |
It has no tos_uri (no terms of service) and no
post_logout_redirect_uris (Solid-OIDC's context doesn't define it,
and logout, local to this browser, doesn't use it). Without a CNAME
the build writes no document; a copy in apps/web/public/ fails the
build.
The build gives the page the document's address (__CLIENT_ID__,
configuration.md). The gateway
passes it to the library as clientId only when the page is served
from the document's folder. Elsewhere the document lists no redirect
URL, the provider would refuse the login, and the app registers
dynamically: on npm run dev and npm start, in the user journeys,
on a fork's address or in a subfolder.
Of the suggested providers (onboarding.md),
solidcommunity.net and solidweb.me (Community Solid Server) and Inrupt
PodSpaces (login.inrupt.com) list webid in their
/.well-known/openid-configuration. iGrant.io's Data Pod (Node Solid
Server) doesn't, so logins there register dynamically.
login.inrupt.com lacks none in its
token_endpoint_auth_methods_supported, though Inrupt's guide
describes Client ID Documents for apps with no secret; a login there
with the document on the deployed site is untried. A session from
before the document restores with the client it was registered as. The
next login moves it to the document.
- The address is part of the public API (api/self-hosting.txt): providers record consent per client ID, so a new address asks every learner again.
- The provider reads it, not the app. A provider that lists
webidbut can't fetch the document refuses the login on its own page and doesn't send the learner back, so the app can't say why; such a provider goes in an interop report. Community Solid Server fetches the document at each login; Inrupt's caches it for an unstated time, so a new redirect URL goes into the document a release before the app uses it. - The tests check it, but no journey logs in with it. Journeys
serve the site at
http://127.0.0.1:4173and register dynamically. clientId.test.ts checks the document, solidSessionGateway.test.ts when the gateway names it, and the build test that the build writes it with the address its page logs in with.
restore() runs on every page load (App's boot effect) through the
restoreSession use case, which falls back to a guest's session if a
guest studied in this browser (guest-mode.md).
It completes a pending OIDC redirect, or else silently restores a
previous session (restorePreviousSession: true), and returns a domain
EstablishedSession ({ session, origin }) or null. origin is
"login" when the library's LOGIN event fired (a login redirect
completed on this load), and "restored" otherwise. The UI shows the
"Pod connected" onboarding step only for "login", unless every instance's preferences say to skip it
(onboarding.md).
Solid Memo and the Studio are one page (studio.md): a
login in either is a login in both. A redirect URL can't carry a hash,
so the provider returns the learner to the site's root; the gateway
restores the route before restore() returns:
- On a login,
startLoginkeeps the hash insessionStorage(solid-memo:login-hash) andrestore()reads it back. - On a silent restore, the hash comes from the page the library's
SESSION_RESTOREDevent reports.
restore() then sets the page's hash and fires hashchange for the
routers. A reload returns to the screen it left, and a login started
at the Studio's landing page (#/studio) to the Studio, not to Solid
Memo's decks.
The composition root injects repositories' fetch:
authFetch, a wrapper around the
sign-in client's fetch, which calls the current session's, never one
captured before login.
solidSession.ts makes it and
the session gateway on one client. A router in front sends the guest's URLs to the browser's pod
(guest-mode.md). Every pod read goes through
authFetch, the WebID document's too. A release outside the user's own
pod is read without it (studio.md). The storage
gateway hands the profile lookup (getProfileStorages, in
client/resources.ts) the profile it read, so the lookup doesn't fetch
it with the browser's fetch.
logout() clears the session through the authn library, and the UI
clears the react-query cache: no pod data outlives its session.