Login, session restore, and logout against a Solid identity provider using
Solid-OIDC, which builds on
OpenID Connect
and DPoP (the specifications
and their status are in standards.md). All authentication code lives in
packages/solid/src/solidSessionGateway.ts,
behind the SessionGateway port.
The user enters only their WebID
(its profile in Solid; collected by the
onboarding flow, validated as an https: URL). The app
dereferences it (unauthenticated) and reads the solid:oidcIssuer triple to
find their identity provider, then starts the OIDC redirect flow. The issuer
is never inferred from the WebID's origin — the two often differ (a WebID on
alice.datapod.igrant.io is served by the issuer datapod.igrant.io) — and
must itself be an https: URL.
Alternatively the user picks a suggested provider (onboarding);
loginWithIssuer then skips the profile lookup and starts the same flow at
that issuer.
The app never handles credentials: no password field, no client secret, no
hand-rolled OIDC. The authn library registers the client dynamically,
under the site's name (VITE_APP_NAME, configuration.md),
and runs the authorization-code + PKCE flow.
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" }
restore() runs on every page load (App's boot effect), through the
restoreSession use case, which falls back to a guest's session when a
guest studied in this browser (guest-mode.md). It completes a
pending OIDC redirect if one is in flight, otherwise silently restores a
previous session (restorePreviousSession: true). It returns a domain
EstablishedSession ({ session, origin }) or null. origin is
"login" when the library emitted its LOGIN event (a login redirect just
completed) and "restored" otherwise; the UI shows the "Pod connected"
onboarding step only for the former.
Solid Memo and the Studio are one page, so they share this one session
(studio.md): a login in either is a login in both. A
redirect URL may carry no hash, so the identity provider sends the user
back to the site's root, without the route they left from. The gateway
puts that route back before restore() returns. On a login, startLogin
keeps the hash in sessionStorage (solid-memo:login-hash) and
restore() takes it from there. On a silent restore, the library
reports the page it left (its SESSION_RESTORED event) and restore()
takes the hash from that. Either way it replaces the page's hash and
fires hashchange, so the routers follow, as after a link. So a reload
comes back to the screen it left, and a login set off at the Studio's
landing page (#/studio) comes back to the Studio, not to Solid Memo's
decks.
Repositories receive fetch by injection. The composition root injects
authFetch, a lazy wrapper that
delegates to the current default session's fetch at call time — never a
reference captured before login — behind a router that sends the
guest's URLs to the pod kept in the browser instead
(guest-mode.md). Every pod read goes through it, the
WebID document's too: the storage gateway hands @inrupt/solid-client the
profile it read, since left to itself the library reads it with the
browser's own fetch.
logout() clears the session via the authn library; the UI additionally
clears the react-query cache so no pod data outlives the session that
fetched it.