68 migrate to reactive auth - #71
Conversation
Drops @inrupt/solid-client-authn-browser along with the @ldo/solid-react and solid-react-component wrappers in favour of @solid/reactive-authentication, which drives sign-in from custom elements rather than a React context we do not control. The authorization code flow runs in a popup, so it needs a static callback page to hand the code back to the opener. With solid-react-component gone there is nothing left for next.config to transpile, and the local Community Solid Server port becomes configurable so it can move off 3000 when that port is already taken.
The auth manager owns the token provider and the authenticated fetch, and keeps the current session outside React so plain modules read the same value the UI renders. It attaches credentials only to origins the app has opted in, because the DPoP provider answers any 401 from any host and this app fetches URLs it does not control. Sign-in accepts either a WebID or a login server, so identify() asks the profile and OIDC discovery both rather than guessing from the shape of the URL.
SolidAuthProvider mounts the authorization code element, restores a stored session on load and exposes login and logout to the tree, taking LdoProvider's place in the root layout. Components reach it through useSolidAuth, which throws when called outside the provider so a missing wrapper fails loudly instead of quietly rendering as signed out.
One field now takes either a WebID or a login server and works out which it is, so what we remember between visits is the entry the user typed rather than a choice from a list of identity providers. A profile may name several issuers, so IssuerPickerDialog asks which to use only when the choice is genuinely ambiguous. Signing out here cannot end the session at the login server, so SignedOutNotice offers that door rather than leaving the next person signed straight back in.
Every helper that talked to a pod went through getAuthenticatedSession(), which existed only to reach into the inrupt session. They now take the authenticated fetch from the auth manager, so that facade goes away entirely. Agent gains oidcIssuers because the login page has to read the issuers a profile advertises before there is any session at all.
AuthWrapper replaces solid-react-component's AuthGuard on the protected routes and fails closed: children mount only once a session is confirmed, so nothing below can fetch before there is anything to authenticate with. The components underneath read the WebID and the authenticated fetch directly instead of from the removed provider.
9316350 to
7f85544
Compare
|
Preview deployment: https://solid-file-manager-hcarqd2xt-solid-odis-projects.vercel.app |
| "@inrupt/solid-client-authn-browser": "^3.1.1", | ||
| "@ldo/solid-react": "^1.0.0-alpha.33", | ||
| "@rdfjs/wrapper": "^0.33.0", | ||
| "@solid/reactive-authentication": "github:PreciousOritsedere/reactive-authentication", |
There was a problem hiding this comment.
This needs to point to a release version of reactive authn before merging
| function writeStoredCredentials(credentials: SolidCredentials): void { | ||
| try { | ||
| localStorage.setItem(STORAGE_KEY, JSON.stringify(credentials)); | ||
| } catch (error) { | ||
| // The session still works; it just won't survive a reload. | ||
| console.warn("Could not persist the session", error); | ||
| } | ||
| } | ||
|
|
||
| function clearStoredCredentials(): void { | ||
| try { | ||
| localStorage.removeItem(STORAGE_KEY); | ||
| } catch (error) { | ||
| // The identity stays on disk until something else clears it. | ||
| console.warn("Could not clear the stored session", error); | ||
| } | ||
| } |
There was a problem hiding this comment.
This should be handled upstream in the reactive authentication library with a LocalStorage store; rather than handling that logic here.
| function readStoredCredentials(): SolidCredentials | null { | ||
| try { | ||
| const parsed: unknown = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "null"); | ||
| return isCredentials(parsed) ? parsed : null; | ||
| } catch (error) { | ||
| console.warn("Discarding an unreadable stored session", error); | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
This should be upstreamed - same as https://github.com/solid-contrib/solid-file-manager/pull/71/changes#r3727518359
| function isCredentials(value: unknown): value is SolidCredentials { | ||
| return ( | ||
| typeof value === "object" && | ||
| value !== null && | ||
| typeof (value as SolidCredentials).webId === "string" && | ||
| typeof (value as SolidCredentials).issuer === "string" | ||
| ); | ||
| } |
There was a problem hiding this comment.
This should be upstreamed - same as https://github.com/solid-contrib/solid-file-manager/pull/71/changes#r3727518359
| const logout = useCallback(async () => { | ||
| const generation = ++generationRef.current; | ||
| const previousIssuer = getCurrentSession()?.issuer ?? null; | ||
|
|
||
| clearStoredCredentials(); | ||
| setStatus("restoring"); | ||
|
|
||
| resetAuthManager(); | ||
| await buildManager(); | ||
| if (generationRef.current !== generation) return; | ||
| setSignedOutFrom(previousIssuer); | ||
| setStatus("anonymous"); | ||
| }, [buildManager]); |
There was a problem hiding this comment.
This logic should be handled up-stream in the reactive authentication library as we move token storage over there.
| const login = useCallback((next: { webId: string; issuer: string }) => { | ||
| generationRef.current++; | ||
| setCurrentSession(next); | ||
| writeStoredCredentials(next); | ||
| setSignedOutFrom(null); | ||
| setStatus("authenticated"); | ||
| }, []); |
There was a problem hiding this comment.
We should not need to proactively trigger login since this will be triggered reactively by the reactive-authentication package.
Addresse #68