Sync/main from staging - #333
Merged
Merged
Conversation
- Convert `SessionWithLegacyEvents` imports to type-only imports to avoid runtime side effects when importing auth-related modules - Prevent eager initialization of `authSession` via type-only usage in: - types.ts - SolidAuthnLogic.ts - solidLogic.ts - Add focused tests for fetch bridge behavior in `solidLogicSingleton`: - use `window.fetch` when `credentials: omit` - fall back to `authFetch` when `session.fetch` is unavailable - Keep migration compatibility behavior intact while improving import safety and regression coverage
Skip WebSession worker initialization for localhost/127.0.0.1 over http and use SessionCore with IndexedDB directly. This prevents browser SecurityError noise from file:// worker resolution in local dev while keeping normal session behavior in other environments.
- session.ts: OIDC session factory (WebSession/SessionCore, database backends) - events.ts: SessionEvents class (pure EventEmitter shim, zero side-effects) - issuer.ts: issuer discovery via /.well-known/openid-configuration - authSession.ts: login compatibility shim, legacy event wiring, authSession assembly; re-exports authSession and SessionWithLegacyEvents type All existing imports continue working unchanged — authSession.ts is a drop-in replacement with the same exports.
replace Inrupt by Uvdsl OIDC client
Auth session type
Update ci.yml
Prompt: add resource service regression tests and harden delete recursion Co-authored-by: GPT-5.4 Mini <gpt-5.4-mini@openai.com>
Add functionality to support 3 dots menu
add a editable refresh to resource
This reverts commit 3c06085.
Revert "add a edittable refresh to resource" — superseded by the session-transition metadata flag
The identity used to be assembled per call site from session fields, a cookie fallback and a cleared mark, each with its own predicate; every new async path (a slow restore, a late probe, a refocus) could reintroduce the same class of bug in a different place. The state now lives once per session in identityState.ts: - sessionIsActive() is the single activity rule (an explicit false wins over a cached WebID); - every observation (session event, token update, refocus resync, cookie probe result) is applied to one record, so a change is detected and reported once, whichever path noticed it; - the record carries a version and the newest resync attempt, so an answer that belongs to an identity the session has since left — a restore started under Alice answering after Bob logged in — is dropped, and a caller that joins an attempt in flight is judged against the attempt's own baseline; - transitions are derived from the previous/next snapshots (classifyTransition/identityReplaced), not from flags, so an unchanged identity costs no event. The cookie fallback and the setTokenDetails watch are TEMPORARY: they exist because uvdsl announces only isActive changes — not a WebID change that keeps the session active, and not a cookie identity change at all. Both are isolated here so they become a small deletion when the library reports identity changes itself. Nothing consumes the state yet: the next commit moves authSession, SolidAuthnLogic and the fetch bridge onto it. Tests: test/identityState.test.ts (15) — activity/predicate table, login and logout (one replacement, no second one after the WebID is cleared), A->B while active, an A->B that uvdsl never announces around setTokenDetails, cross-tab clear reported once, joined resync, dropped stale answer, cookie identity adopt/replace/clear, release semantics, refocus fan-out.
authSession, SolidAuthnLogic and the fetch bridge now use the per-session identity state (identityState.ts) instead of assembling the identity per call site: - authSession subscribes once (events + the refocus resync) and publishes the derived `info` shape. 'logout' / 'sessionChange' / 'identityReplaced' come from the state; 'login' / 'sessionRestore' stay with checkUser, the only place that knows which path activated the session. - SolidAuthnLogic keeps only what is its own: the redirect handling, the NSS cookie probe and saveUser. currentUser() asks the state for the effective identity, and the probe result goes through its subscription — a probe that answers after dispose(), or while the session took ownership, can no longer resurrect an identity. fallbackWebId, cookieBackedFallback, cookieProbeGeneration, the refocus watcher, probeCookieIdentity() and reportFallbackIdentityChange() are gone: one record replaces them. - solidLogicSingleton decides the fetch credentials from the state, so a session that reports itself inactive (or was reported cleared) cannot send the previous identity's credentials. - AuthnLogic gains the optional dispose() the state's lifetime needs; the legacy event vocabulary is exported and includes the identity events. Tests: the fetch-bridge test fakes the session identity instead of the derived `info` shape, whose assignment is ignored on purpose. Full suite green.
Replaces the smoke tests with the behaviours the identity state has to keep for SolidAuthnLogic: - currentUser(): the active session's WebID; logged out for an explicit isActive:false or info.isLoggedIn:false with a cached WebID; the legacy WebID-only shape accepted. - checkUser(): sessionRestore/login announced once, from the path that activated the session; "No session to restore." is treated as logged out while a failure that nevertheless left the session active is rethrown; the NSS cookie probe recovers the WebID on *.localhost and reports it as a session change, is skipped when the session already has one, and a probe that answers after the session took the identity is ignored. - refocus/dispose: the cookie identity is re-probed and its loss reported; a disposed instance stops probing (an in-flight probe result is dropped), while a second instance using the same session keeps the watcher. - authSession.info: derived, explicit false reported, assignment ignored. The tests subscribe a forwarder like authSession does in the app, so the emitted list means the same thing as the legacy events consumers see.
A session transition changes whose credentials a request would carry, but editable() reads responses that are not keyed by identity: a document fetched anonymously (before a restore completed) or under a previous WebID keeps answering for the old identity. flagAuthorizationOnSessionTransitions() subscribes to the identity state — one call per applied transition, whichever path noticed it — and marks every recorded response out-of-date, so editable() answers "unknown" instead of the previous identity's access. A failed invalidation is remembered as refreshRequired, so the decision points repair instead of trusting a store that could not be invalidated. refreshDocumentAuthorization() / ensureDocumentAuthorization() / loadAuthorizedDocument() force-refresh (force: true, clearPreviousData: true) with the store's transition generation stamped around each attempt: an answer overtaken by a transition is retried under the new identity and stays "unknown" when it cannot be established — never the previous identity's answer. loadAuthorizedDocument also generation-checks the load, because a response begun before a transition can be recorded after it. utilityLogic's followOrCreate* repair before reading (and fail closed with NotEditableError), and solidLogic wires the invalidation where the session is created. identityState reports an applied transition through onTransition (once), while onEvent still carries the individual legacy events: a transition that carries two events must not make a consumer that only invalidates do its work twice. On rdflib 2.4.0 a plain load cannot repair a flagged document (the literal / NamedNode mismatch, linkeddata/rdflib.js#427); from 2.4.1 load() matches the literal, so checkEditable() heals too — the force path remains the deterministic repair on both. Tests: 12 for the store side (flag on change only, every transition path, release, failed invalidation, definitive answers, repair, overtaken load, failure modes).
rdflib 2.4.1 carries linkeddata/rdflib.js#871: load() matches the recorded request by its URI literal and refetches a document whose recorded answers are all flagged, so a plain load()/checkEditable() heals a flagged document again. Our repair path (refreshDocumentAuthorization/ensureDocumentAuthorization/ loadAuthorizedDocument) is unchanged and stays the deterministic repair — it also covers consumers still on rdflib 2.4.0. Contract tests against the real UpdateManager/Fetcher: - definitive -> unknown when flagged -> definitive after a fresh response; - load() heals a flagged, already-loaded document on 2.4.1; - refreshDocumentAuthorization() repairs on any version. package.json / package-lock.json: rdflib ^2.4.0 -> ^2.4.1, lock edited to the single dependency entry instead of regenerating it.
deliver() interleaved a subscriber's own events with its transition callback, so which ran first depended on subscription order: in the app authSession subscribes before the store, and a legacy 'logout' listener could therefore read the store before it had been invalidated (measured in the e2e: a synchronous read inside the listener still saw the previous identity's write capability). Every subscriber's onTransition now runs before any subscriber's onEvent, so "the store is already invalidated when you hear the event" is a guarantee rather than an accident. Test: subscribers in the app's order observe ['transition', 'event', 'event'].
…efresh rdflib 2.4.1 refetches a document whose recorded answers are all flagged (linkeddata/rdflib.js#871) — exactly the state an identity transition leaves behind — so the store side no longer forces a fetch by hand: - forceRefresh() and refreshDocumentAuthorization() are gone; repairDocument() marks the responses out-of-date (including one recorded after the transition, which the transition's own flag could not have marked) and loads, retrying under a new identity and staying unknown if it keeps moving; - a store that cannot invalidate is repaired through the same path, and ensureDocumentAuthorization() fails closed when the repair cannot be established — a store that cannot invalidate must not have its answers trusted; - the module and its tests state the requirement plainly: rdflib >= 2.4.1. Tests: the store-side suite models the flag/load contract instead of a refresh callback; the contract test asserts that a decision point repairs a flagged document through a load.
loadAuthorizedDocument() answers whether a document can be consumed; on a store without fetcher.load it fell through to ensureDocumentAuthorization() and could answer "yes" for a document it never loaded. It now fails like a plain load() would. createSolidLogic() keeps the unsubscribe that flagAuthorizationOnSessionTransitions() returns and calls it from authn.dispose(), so replacing a SolidLogic instance no longer leaves the old store subscribed to the session (and retained by it).
watchTokenUpdates() only compared the identity when setTokenDetails() resolved, so an update that applied the new identity and then rejected (a failed persistence, say) left the transition unseen — the one thing the wrapper exists to catch. The comparison now runs on success, on rejection and on a synchronous throw; the failure itself is rethrown untouched. resyncActionOf() promised the newest resync action but returned the first subscriber that had one, i.e. the oldest. It keeps the last match instead, so a second provider cannot make refocus behaviour depend on subscription order.
… tests invalidate() interpolated the caught value directly, which reads "[object Object]" for a non-Error throw; String(error) matches the reload warning in the same module. The identityState tests now subscribe through a helper that keeps every handle and releases it in afterEach: a first subscription attaches a visibilitychange listener to the document, and leaving those behind lets a later test's refocus run resyncs (and cookie probes) from a finished one.
Session staleness 1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.