Skip to content

Commit 62ee41b

Browse files
m5x5claude
andcommitted
feat: swap @inrupt/solid-client-authn-browser for @uvdsl/solid-oidc-client-browser
Wraps uvdsl's default WebWorkerSession in a thin Inrupt-shape adapter inside authSession.ts so downstream call sites (panes, solid-ui) keep working unchanged. The SharedWorker provides background token refresh and cross-tab token sharing — the intended uvdsl usage for web apps. Refs #274 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c4fa813 commit 62ee41b

6 files changed

Lines changed: 66 additions & 10 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
"webpack-cli": "^7.0.2"
7373
},
7474
"dependencies": {
75-
"@inrupt/solid-client-authn-browser": "^4.0.0",
75+
"@uvdsl/solid-oidc-client-browser": "^0.2.2",
7676
"solid-namespace": "^0.5.4"
7777
},
7878
"peerDependencies": {

src/authSession/authSession.ts

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,57 @@
1-
import {
2-
Session,
3-
} from '@inrupt/solid-client-authn-browser'
1+
import { Session as UvdslSession, SessionIDB } from '@uvdsl/solid-oidc-client-browser'
42

5-
export const authSession = new Session()
3+
/** Inrupt-shape `Session` API consumed by solid-logic + downstream panes,
4+
* backed by uvdsl's default `Session` (WebWorkerSession). */
5+
6+
export const EVENTS = { LOGIN: 'login', LOGOUT: 'logout', SESSION_RESTORED: 'sessionRestore' } as const
7+
type Cb = (...args: any[]) => void
8+
9+
export class Session {
10+
private inner: UvdslSession | null = null
11+
private subs: Record<string, Set<Cb>> = {}
12+
readonly events = {
13+
on: (n: string, cb: Cb) => { (this.subs[n] ||= new Set()).add(cb) },
14+
off: (n: string, cb: Cb) => this.subs[n]?.delete(cb),
15+
removeListener: (n: string, cb: Cb) => this.subs[n]?.delete(cb),
16+
}
17+
private emit(n: string, ...a: any[]) {
18+
this.subs[n]?.forEach(cb => { try { cb(...a) } catch (_) {} })
19+
}
20+
private ensure(redirect?: string) {
21+
return this.inner ||= new UvdslSession(
22+
{ client_name: 'SolidOS', redirect_uris: [redirect || location.origin + location.pathname] } as any,
23+
{ database: new SessionIDB() } as any,
24+
)
25+
}
26+
27+
get info() {
28+
const s = this.inner
29+
return { webId: s?.isActive ? s.webId : undefined, isLoggedIn: !!s?.isActive }
30+
}
31+
32+
fetch = (input: RequestInfo | URL, init?: RequestInit) =>
33+
this.ensure().authFetch(input as any, init)
634

7-
35+
async login(opts: { oidcIssuer: string; redirectUrl?: string }) {
36+
const r = opts.redirectUrl || location.href
37+
await this.ensure(r).login(opts.oidcIssuer, r)
38+
}
39+
40+
async logout() { await this.ensure().logout(); this.emit('logout') }
41+
42+
async handleIncomingRedirect(opts?: { restorePreviousSession?: boolean; url?: string }) {
43+
const url = new URL(opts?.url || location.href)
44+
const s = this.ensure()
45+
if (url.searchParams.has('code') && url.searchParams.has('state')) {
46+
await s.handleRedirectFromLogin()
47+
for (const k of ['code', 'state', 'iss']) url.searchParams.delete(k)
48+
try { history.replaceState(null, '', url.toString()) } catch (_) {}
49+
if (s.isActive) this.emit('login')
50+
} else if (opts?.restorePreviousSession !== false) {
51+
await s.restore()
52+
if (s.isActive) this.emit('sessionRestore', url.toString())
53+
}
54+
}
55+
}
56+
57+
export const authSession = new Session()

src/authn/SolidAuthnLogic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { namedNode, NamedNode, sym } from 'rdflib'
22
import { appContext, offlineTestID } from './authUtil'
33
import * as debug from '../util/debug'
4-
import { EVENTS, Session } from '@inrupt/solid-client-authn-browser'
4+
import { EVENTS, Session } from '../authSession/authSession'
55
import { AuthenticationContext, AuthnLogic } from '../types'
66

77
export class SolidAuthnLogic implements AuthnLogic {

src/logic/solidLogic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Session } from '@inrupt/solid-client-authn-browser'
1+
import { Session } from '../authSession/authSession'
22
import * as rdf from 'rdflib'
33
import { LiveStore, NamedNode, Statement } from 'rdflib'
44
import { createAclLogic } from '../acl/aclLogic'

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Session } from '@inrupt/solid-client-authn-browser'
1+
import { Session } from './authSession/authSession'
22
import { LiveStore, NamedNode, Statement } from 'rdflib'
33

44
export type AppDetails = {

webpack.config.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ const externalsBase = {
1010
'@trust/webcrypto': 'crypto',
1111
'@xmldom/xmldom': 'window',
1212
'whatwg-url': 'URL',
13-
'rdflib': '$rdf'
13+
'rdflib': '$rdf',
14+
// Must externalize: uvdsl's SharedWorker URL is constructed via
15+
// `new URL('./RefreshWorker.js', import.meta.url)`. If bundled inline here,
16+
// webpack bakes a `file://…/solid-logic/dist/…` path that browsers refuse
17+
// to load over http:// origin. Leaving it external lets the consumer bundle
18+
// (mashlib) resolve the worker URL relative to its own publicPath.
19+
'@uvdsl/solid-oidc-client-browser': '@uvdsl/solid-oidc-client-browser'
1420
}
1521

1622
const externalsESM = {

0 commit comments

Comments
 (0)