@undefineds.co/xpod 0.3.39 → 0.3.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/xpod.base.json +4 -0
- package/dist/api/chatkit/pod-store.d.ts +9 -0
- package/dist/api/chatkit/pod-store.js +229 -10
- package/dist/api/chatkit/pod-store.js.map +1 -1
- package/dist/components/context.jsonld +6 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +2 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +65 -5
- package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
- package/package.json +1 -1
|
@@ -2267,6 +2267,9 @@
|
|
|
2267
2267
|
"options_providerFactory": {
|
|
2268
2268
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_providerFactory"
|
|
2269
2269
|
},
|
|
2270
|
+
"options_storageBaseUrl": {
|
|
2271
|
+
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_storageBaseUrl"
|
|
2272
|
+
},
|
|
2270
2273
|
"options_identityDbUrl": {
|
|
2271
2274
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_identityDbUrl"
|
|
2272
2275
|
},
|
|
@@ -2285,6 +2288,9 @@
|
|
|
2285
2288
|
"providerFactory": {
|
|
2286
2289
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_providerFactory"
|
|
2287
2290
|
},
|
|
2291
|
+
"storageBaseUrl": {
|
|
2292
|
+
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_storageBaseUrl"
|
|
2293
|
+
},
|
|
2288
2294
|
"identityDbUrl": {
|
|
2289
2295
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_identityDbUrl"
|
|
2290
2296
|
},
|
|
@@ -5,6 +5,7 @@ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Respo
|
|
|
5
5
|
export interface ScopedPickWebIdHandlerOptions {
|
|
6
6
|
webIdStore: WebIdStore;
|
|
7
7
|
providerFactory: ProviderFactory;
|
|
8
|
+
storageBaseUrl?: string;
|
|
8
9
|
identityDbUrl?: string;
|
|
9
10
|
provisionBaseUrl?: string;
|
|
10
11
|
podLookupRepository?: PodWebIdLookupRepository;
|
|
@@ -27,6 +28,7 @@ export declare class ScopedPickWebIdHandler extends JsonInteractionHandler imple
|
|
|
27
28
|
private readonly logger;
|
|
28
29
|
private readonly webIdStore;
|
|
29
30
|
private readonly providerFactory;
|
|
31
|
+
private readonly storageBaseUrl?;
|
|
30
32
|
private readonly provisionBaseUrl?;
|
|
31
33
|
private readonly podLookupRepository?;
|
|
32
34
|
private readonly fetch;
|
|
@@ -24,6 +24,7 @@ class ScopedPickWebIdHandler extends community_server_1.JsonInteractionHandler {
|
|
|
24
24
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
25
25
|
this.webIdStore = options.webIdStore;
|
|
26
26
|
this.providerFactory = options.providerFactory;
|
|
27
|
+
this.storageBaseUrl = normalizeOptionalUrl(options.storageBaseUrl);
|
|
27
28
|
this.provisionBaseUrl = normalizeOptionalUrl(options.provisionBaseUrl);
|
|
28
29
|
this.podLookupRepository = options.podLookupRepository ??
|
|
29
30
|
(options.identityDbUrl ? new PodLookupRepository_1.PodLookupRepository((0, db_1.getIdentityDatabase)(options.identityDbUrl)) : undefined);
|
|
@@ -150,7 +151,7 @@ class ScopedPickWebIdHandler extends community_server_1.JsonInteractionHandler {
|
|
|
150
151
|
async resolveTargetStorage(provider, oidcInteraction) {
|
|
151
152
|
const provisionCode = extractProvisionCode(oidcInteraction);
|
|
152
153
|
if (!provisionCode) {
|
|
153
|
-
return { storageUrl: ensureTrailingSlash(provider.issuer) };
|
|
154
|
+
return { storageUrl: ensureTrailingSlash(this.storageBaseUrl ?? provider.issuer) };
|
|
154
155
|
}
|
|
155
156
|
const payload = new ProvisionCodeCodec_1.ProvisionCodeCodec(this.provisionBaseUrl ?? provider.issuer).decode(provisionCode);
|
|
156
157
|
if (!payload) {
|
|
@@ -209,7 +210,7 @@ function deriveStorageMode(webId, storageUrl) {
|
|
|
209
210
|
if (!webIdRoot || !storageRoot) {
|
|
210
211
|
return 'custom';
|
|
211
212
|
}
|
|
212
|
-
return webIdRoot
|
|
213
|
+
return sameStorageRoot(webIdRoot, storageRoot) ? 'cloud' : 'local';
|
|
213
214
|
}
|
|
214
215
|
function deriveStorageRoot(url) {
|
|
215
216
|
try {
|
|
@@ -249,16 +250,75 @@ function matchesTargetStorage(pod, targetStorageUrl) {
|
|
|
249
250
|
}
|
|
250
251
|
for (const candidate of candidateUrls) {
|
|
251
252
|
const candidateRoot = deriveStorageRoot(candidate);
|
|
252
|
-
if (candidateRoot && candidateRoot
|
|
253
|
+
if (candidateRoot && sameStorageRoot(candidateRoot, targetRoot)) {
|
|
253
254
|
return true;
|
|
254
255
|
}
|
|
255
|
-
|
|
256
|
-
if (candidateUrl.startsWith(targetRoot) || targetRoot.startsWith(candidateUrl)) {
|
|
256
|
+
if (sameStorageScope(candidate, targetRoot)) {
|
|
257
257
|
return true;
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
return false;
|
|
261
261
|
}
|
|
262
|
+
function sameStorageRoot(left, right) {
|
|
263
|
+
if (ensureTrailingSlash(left) === ensureTrailingSlash(right)) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
const leftUrl = parseUrl(left);
|
|
267
|
+
const rightUrl = parseUrl(right);
|
|
268
|
+
if (!leftUrl || !rightUrl) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return sameUrlAuthority(leftUrl, rightUrl)
|
|
272
|
+
&& normalizeUrlPath(leftUrl.pathname) === normalizeUrlPath(rightUrl.pathname);
|
|
273
|
+
}
|
|
274
|
+
function sameStorageScope(candidate, targetRoot) {
|
|
275
|
+
const candidateUrl = parseUrl(candidate);
|
|
276
|
+
const targetUrl = parseUrl(targetRoot);
|
|
277
|
+
if (!candidateUrl || !targetUrl || !sameUrlAuthority(candidateUrl, targetUrl)) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
const candidatePath = normalizeUrlPath(candidateUrl.pathname);
|
|
281
|
+
const targetPath = normalizeUrlPath(targetUrl.pathname);
|
|
282
|
+
return candidatePath.startsWith(targetPath) || targetPath.startsWith(candidatePath);
|
|
283
|
+
}
|
|
284
|
+
function sameUrlAuthority(left, right) {
|
|
285
|
+
if (left.protocol !== right.protocol) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
if (normalizePort(left) !== normalizePort(right)) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (left.hostname === right.hostname) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
return isLoopbackHostname(left.hostname) && isLoopbackHostname(right.hostname);
|
|
295
|
+
}
|
|
296
|
+
function parseUrl(url) {
|
|
297
|
+
try {
|
|
298
|
+
return new URL(url);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function normalizePort(url) {
|
|
305
|
+
if (url.port) {
|
|
306
|
+
return url.port;
|
|
307
|
+
}
|
|
308
|
+
if (url.protocol === 'http:') {
|
|
309
|
+
return '80';
|
|
310
|
+
}
|
|
311
|
+
if (url.protocol === 'https:') {
|
|
312
|
+
return '443';
|
|
313
|
+
}
|
|
314
|
+
return '';
|
|
315
|
+
}
|
|
316
|
+
function normalizeUrlPath(pathname) {
|
|
317
|
+
return ensureTrailingSlash(pathname || '/');
|
|
318
|
+
}
|
|
319
|
+
function isLoopbackHostname(hostname) {
|
|
320
|
+
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]';
|
|
321
|
+
}
|
|
262
322
|
function extractProvisionCode(oidcInteraction) {
|
|
263
323
|
const params = oidcInteraction?.params;
|
|
264
324
|
const value = params?.provisionCode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScopedPickWebIdHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/ScopedPickWebIdHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { boolean, object, string } from 'yup';\nimport { getLoggerFor } from 'global-logger-factory';\nimport {\n BadRequestHttpError,\n FoundHttpError,\n JsonInteractionHandler,\n assertAccountId,\n assertOidcInteraction,\n finishInteraction,\n forgetWebId,\n parseSchema,\n validateWithError,\n} from '@solid/community-server';\nimport type {\n Json,\n JsonInteractionHandlerInput,\n JsonRepresentation,\n JsonView,\n ProviderFactory,\n WebIdStore,\n} from '@solid/community-server';\nimport { getIdentityDatabase } from '../drizzle/db';\nimport { PodLookupRepository, type PodLookupResult } from '../drizzle/PodLookupRepository';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\ntype FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nconst inSchema = object({\n webId: string().trim().required(),\n remember: boolean().default(false),\n});\n\nexport interface ScopedPickWebIdHandlerOptions {\n webIdStore: WebIdStore;\n providerFactory: ProviderFactory;\n identityDbUrl?: string;\n provisionBaseUrl?: string;\n podLookupRepository?: PodWebIdLookupRepository;\n fetch?: FetchLike;\n}\n\nexport interface PodWebIdLookupRepository {\n findByWebId: (webId: string) => Promise<PodLookupResult | undefined>;\n findAllByWebId?: (webId: string) => Promise<PodLookupResult[]>;\n findByWebIds?: (webIds: string[]) => Promise<PodLookupResult[]>;\n listByAccountId?: (accountId: string) => Promise<PodLookupResult[]>;\n}\n\ninterface WebIdEntry extends Record<string, Json | undefined> {\n webId: string;\n storageUrl?: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n}\n\n/**\n * CSS-compatible WebID picker scoped to the current storage provider.\n *\n * The upstream handler lists every WebID linked to the IdP account. In an\n * IDP/SP split flow that lets a Local SP login pick a Cloud Pod again, so this\n * replacement keeps consent choices constrained by the selected SP's Pod facts.\n */\nexport class ScopedPickWebIdHandler extends JsonInteractionHandler implements JsonView {\n private readonly logger = getLoggerFor(this);\n private readonly webIdStore: WebIdStore;\n private readonly providerFactory: ProviderFactory;\n private readonly provisionBaseUrl?: string;\n private readonly podLookupRepository?: PodWebIdLookupRepository;\n private readonly fetch: FetchLike;\n\n public constructor(options: ScopedPickWebIdHandlerOptions) {\n super();\n this.webIdStore = options.webIdStore;\n this.providerFactory = options.providerFactory;\n this.provisionBaseUrl = normalizeOptionalUrl(options.provisionBaseUrl);\n this.podLookupRepository = options.podLookupRepository ??\n (options.identityDbUrl ? new PodLookupRepository(getIdentityDatabase(options.identityDbUrl)) : undefined);\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getView({ accountId, oidcInteraction }: JsonInteractionHandlerInput): Promise<JsonRepresentation> {\n assertAccountId(accountId);\n const provider = await this.providerFactory.getProvider();\n const description = parseSchema(inSchema);\n const target = await this.resolveTargetStorage(provider, oidcInteraction);\n const entries = await this.resolveScopedEntries(accountId, target);\n\n return {\n json: {\n ...description,\n webIds: entries.map((entry) => entry.webId),\n entries,\n },\n };\n }\n\n public async handle({ oidcInteraction, accountId, json }: JsonInteractionHandlerInput): Promise<never> {\n assertOidcInteraction(oidcInteraction);\n assertAccountId(accountId);\n const { webId, remember } = await validateWithError(inSchema, json);\n const provider = await this.providerFactory.getProvider();\n const target = await this.resolveTargetStorage(provider, oidcInteraction);\n\n if (!await this.isLinkedToAccount(webId, accountId)) {\n this.logger.warn(`Trying to pick WebID ${webId} which does not belong to account ${accountId}`);\n throw new BadRequestHttpError('WebID does not belong to this account.');\n }\n\n if (!await this.isResolvableByCurrentSp(webId, target)) {\n this.logger.warn(`Trying to pick WebID ${webId} which does not belong to this storage provider`);\n throw new BadRequestHttpError('WebID does not belong to this storage provider.');\n }\n\n await forgetWebId(provider, oidcInteraction);\n const location = await finishInteraction(oidcInteraction, {\n login: {\n accountId: webId,\n remember,\n },\n }, true);\n throw new FoundHttpError(location);\n }\n\n private async resolveScopedEntries(accountId: string, target: TargetStorage): Promise<WebIdEntry[]> {\n const webIds = await this.resolveCandidateWebIds(accountId);\n if (target.serviceToken) {\n return this.resolveRemoteSpEntries(webIds, target);\n }\n\n const entries: WebIdEntry[] = [];\n for (const webId of webIds) {\n const pod = await this.findSpPod(webId, target.storageUrl);\n if (!pod) {\n continue;\n }\n const storageUrl = ensureTrailingSlash(pod.storageUrl ?? pod.baseUrl);\n entries.push({\n webId,\n storageUrl,\n storageMode: deriveStorageMode(webId, storageUrl),\n });\n }\n return entries;\n }\n\n private async resolveCandidateWebIds(accountId: string): Promise<string[]> {\n const linkedWebIds = (await this.webIdStore.findLinks(accountId)).map((link) => link.webId);\n if (linkedWebIds.length > 0) {\n return dedupeStrings(linkedWebIds);\n }\n\n if (!this.podLookupRepository?.listByAccountId) {\n return [];\n }\n\n try {\n const pods = await this.podLookupRepository.listByAccountId(accountId);\n return dedupeStrings(pods.flatMap(getPodCandidateWebIds));\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for account ${accountId}: ${error}`);\n return [];\n }\n }\n\n private async isLinkedToAccount(webId: string, accountId: string): Promise<boolean> {\n if (await this.webIdStore.isLinked(webId, accountId)) {\n return true;\n }\n\n if (!this.podLookupRepository?.listByAccountId) {\n return false;\n }\n\n try {\n const pods = await this.podLookupRepository.listByAccountId(accountId);\n return pods.some((pod) => getPodCandidateWebIds(pod).includes(webId));\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for account ${accountId}: ${error}`);\n return false;\n }\n }\n\n private async isResolvableByCurrentSp(webId: string, target: TargetStorage): Promise<boolean> {\n if (target.serviceToken) {\n return (await this.resolveRemoteSpEntries([webId], target)).some((entry) => entry.webId === webId);\n }\n return Boolean(await this.findSpPod(webId, target.storageUrl));\n }\n\n private async findSpPod(webId: string, targetStorageUrl: string): Promise<PodLookupResult | undefined> {\n if (!this.podLookupRepository) {\n this.logger.warn('No PodLookupRepository configured; refusing to expose unscoped WebID choices');\n return undefined;\n }\n\n try {\n if (this.podLookupRepository.findAllByWebId) {\n const pods = await this.podLookupRepository.findAllByWebId(webId);\n return pods.find((pod) => matchesTargetStorage(pod, targetStorageUrl));\n }\n\n if (this.podLookupRepository.findByWebIds) {\n const pods = await this.podLookupRepository.findByWebIds([webId]);\n return pods.find((pod) => matchesTargetStorage(pod, targetStorageUrl));\n }\n\n const pod = await this.podLookupRepository.findByWebId(webId);\n return pod && matchesTargetStorage(pod, targetStorageUrl) ? pod : undefined;\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for WebID ${webId}: ${error}`);\n return undefined;\n }\n }\n\n private async resolveTargetStorage(\n provider: { issuer: string },\n oidcInteraction?: JsonInteractionHandlerInput['oidcInteraction'],\n ): Promise<TargetStorage> {\n const provisionCode = extractProvisionCode(oidcInteraction);\n if (!provisionCode) {\n return { storageUrl: ensureTrailingSlash(provider.issuer) };\n }\n\n const payload = new ProvisionCodeCodec(this.provisionBaseUrl ?? provider.issuer).decode(provisionCode);\n if (!payload) {\n throw new BadRequestHttpError('Invalid or expired provisionCode.');\n }\n\n const targetUrl = payload.spDomain\n ? `https://${payload.spDomain}`\n : payload.spUrl;\n return {\n storageUrl: ensureTrailingSlash(targetUrl),\n lookupUrl: ensureTrailingSlash(payload.spUrl),\n serviceToken: payload.serviceToken,\n };\n }\n\n private async resolveRemoteSpEntries(webIds: string[], target: TargetStorage): Promise<WebIdEntry[]> {\n if (!target.lookupUrl || !target.serviceToken || webIds.length === 0) {\n return [];\n }\n\n const response = await this.fetch(new URL('/provision/webids', target.lookupUrl).toString(), {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${target.serviceToken}`,\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n },\n body: JSON.stringify({ webIds }),\n });\n\n if (!response.ok) {\n this.logger.warn(`Remote SP WebID lookup failed: HTTP ${response.status}`);\n return [];\n }\n\n const body = await response.json().catch(() => null) as { entries?: RemoteSpWebIdEntry[] } | null;\n if (!Array.isArray(body?.entries)) {\n return [];\n }\n\n const allowedWebIds = new Set(webIds);\n return body.entries\n .filter((entry) => typeof entry.webId === 'string' && allowedWebIds.has(entry.webId))\n .filter((entry) => typeof entry.storageUrl === 'string' && matchesTargetStorage(\n {\n podId: '',\n accountId: '',\n baseUrl: entry.podUrl ?? entry.storageUrl,\n storageUrl: entry.storageUrl,\n },\n target.storageUrl,\n ))\n .map((entry) => ({\n webId: entry.webId,\n storageUrl: ensureTrailingSlash(entry.storageUrl),\n storageMode: deriveStorageMode(entry.webId, entry.storageUrl),\n }));\n }\n}\n\ninterface TargetStorage {\n storageUrl: string;\n lookupUrl?: string;\n serviceToken?: string;\n}\n\ninterface RemoteSpWebIdEntry {\n webId: string;\n podUrl?: string;\n storageUrl: string;\n}\n\nfunction deriveStorageMode(webId: string, storageUrl: string): 'cloud' | 'local' | 'custom' {\n const webIdRoot = deriveStorageRoot(webId);\n const storageRoot = deriveStorageRoot(storageUrl);\n if (!webIdRoot || !storageRoot) {\n return 'custom';\n }\n return webIdRoot === storageRoot ? 'cloud' : 'local';\n}\n\nfunction deriveStorageRoot(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n const segments = parsed.pathname.split('/').filter(Boolean);\n if (segments.length === 0) {\n return ensureTrailingSlash(parsed.origin);\n }\n\n return ensureTrailingSlash(new URL(`/${segments[0]}/`, parsed.origin).toString());\n } catch {\n return undefined;\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/u, '') + '/';\n}\n\nfunction normalizeOptionalUrl(url: string | undefined): string | undefined {\n const trimmed = url?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction dedupeStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction getPodCandidateWebIds(pod: PodLookupResult): string[] {\n return dedupeStrings([\n pod.webId,\n ...(pod.webIds ?? []),\n ].filter((value): value is string => typeof value === 'string' && value.length > 0));\n}\n\nfunction matchesTargetStorage(pod: PodLookupResult, targetStorageUrl: string): boolean {\n const candidateUrls = (pod.storageUrl ? [pod.storageUrl] : [pod.baseUrl])\n .filter((value): value is string => typeof value === 'string' && value.length > 0);\n const targetRoot = deriveStorageRoot(targetStorageUrl);\n if (!targetRoot) {\n return false;\n }\n\n for (const candidate of candidateUrls) {\n const candidateRoot = deriveStorageRoot(candidate);\n if (candidateRoot && candidateRoot === targetRoot) {\n return true;\n }\n\n const candidateUrl = ensureTrailingSlash(candidate);\n if (candidateUrl.startsWith(targetRoot) || targetRoot.startsWith(candidateUrl)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction extractProvisionCode(oidcInteraction: JsonInteractionHandlerInput['oidcInteraction']): string | undefined {\n const params = oidcInteraction?.params as Record<string, unknown> | undefined;\n const value = params?.provisionCode;\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ScopedPickWebIdHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/ScopedPickWebIdHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { boolean, object, string } from 'yup';\nimport { getLoggerFor } from 'global-logger-factory';\nimport {\n BadRequestHttpError,\n FoundHttpError,\n JsonInteractionHandler,\n assertAccountId,\n assertOidcInteraction,\n finishInteraction,\n forgetWebId,\n parseSchema,\n validateWithError,\n} from '@solid/community-server';\nimport type {\n Json,\n JsonInteractionHandlerInput,\n JsonRepresentation,\n JsonView,\n ProviderFactory,\n WebIdStore,\n} from '@solid/community-server';\nimport { getIdentityDatabase } from '../drizzle/db';\nimport { PodLookupRepository, type PodLookupResult } from '../drizzle/PodLookupRepository';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\ntype FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nconst inSchema = object({\n webId: string().trim().required(),\n remember: boolean().default(false),\n});\n\nexport interface ScopedPickWebIdHandlerOptions {\n webIdStore: WebIdStore;\n providerFactory: ProviderFactory;\n storageBaseUrl?: string;\n identityDbUrl?: string;\n provisionBaseUrl?: string;\n podLookupRepository?: PodWebIdLookupRepository;\n fetch?: FetchLike;\n}\n\nexport interface PodWebIdLookupRepository {\n findByWebId: (webId: string) => Promise<PodLookupResult | undefined>;\n findAllByWebId?: (webId: string) => Promise<PodLookupResult[]>;\n findByWebIds?: (webIds: string[]) => Promise<PodLookupResult[]>;\n listByAccountId?: (accountId: string) => Promise<PodLookupResult[]>;\n}\n\ninterface WebIdEntry extends Record<string, Json | undefined> {\n webId: string;\n storageUrl?: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n}\n\n/**\n * CSS-compatible WebID picker scoped to the current storage provider.\n *\n * The upstream handler lists every WebID linked to the IdP account. In an\n * IDP/SP split flow that lets a Local SP login pick a Cloud Pod again, so this\n * replacement keeps consent choices constrained by the selected SP's Pod facts.\n */\nexport class ScopedPickWebIdHandler extends JsonInteractionHandler implements JsonView {\n private readonly logger = getLoggerFor(this);\n private readonly webIdStore: WebIdStore;\n private readonly providerFactory: ProviderFactory;\n private readonly storageBaseUrl?: string;\n private readonly provisionBaseUrl?: string;\n private readonly podLookupRepository?: PodWebIdLookupRepository;\n private readonly fetch: FetchLike;\n\n public constructor(options: ScopedPickWebIdHandlerOptions) {\n super();\n this.webIdStore = options.webIdStore;\n this.providerFactory = options.providerFactory;\n this.storageBaseUrl = normalizeOptionalUrl(options.storageBaseUrl);\n this.provisionBaseUrl = normalizeOptionalUrl(options.provisionBaseUrl);\n this.podLookupRepository = options.podLookupRepository ??\n (options.identityDbUrl ? new PodLookupRepository(getIdentityDatabase(options.identityDbUrl)) : undefined);\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getView({ accountId, oidcInteraction }: JsonInteractionHandlerInput): Promise<JsonRepresentation> {\n assertAccountId(accountId);\n const provider = await this.providerFactory.getProvider();\n const description = parseSchema(inSchema);\n const target = await this.resolveTargetStorage(provider, oidcInteraction);\n const entries = await this.resolveScopedEntries(accountId, target);\n\n return {\n json: {\n ...description,\n webIds: entries.map((entry) => entry.webId),\n entries,\n },\n };\n }\n\n public async handle({ oidcInteraction, accountId, json }: JsonInteractionHandlerInput): Promise<never> {\n assertOidcInteraction(oidcInteraction);\n assertAccountId(accountId);\n const { webId, remember } = await validateWithError(inSchema, json);\n const provider = await this.providerFactory.getProvider();\n const target = await this.resolveTargetStorage(provider, oidcInteraction);\n\n if (!await this.isLinkedToAccount(webId, accountId)) {\n this.logger.warn(`Trying to pick WebID ${webId} which does not belong to account ${accountId}`);\n throw new BadRequestHttpError('WebID does not belong to this account.');\n }\n\n if (!await this.isResolvableByCurrentSp(webId, target)) {\n this.logger.warn(`Trying to pick WebID ${webId} which does not belong to this storage provider`);\n throw new BadRequestHttpError('WebID does not belong to this storage provider.');\n }\n\n await forgetWebId(provider, oidcInteraction);\n const location = await finishInteraction(oidcInteraction, {\n login: {\n accountId: webId,\n remember,\n },\n }, true);\n throw new FoundHttpError(location);\n }\n\n private async resolveScopedEntries(accountId: string, target: TargetStorage): Promise<WebIdEntry[]> {\n const webIds = await this.resolveCandidateWebIds(accountId);\n if (target.serviceToken) {\n return this.resolveRemoteSpEntries(webIds, target);\n }\n\n const entries: WebIdEntry[] = [];\n for (const webId of webIds) {\n const pod = await this.findSpPod(webId, target.storageUrl);\n if (!pod) {\n continue;\n }\n const storageUrl = ensureTrailingSlash(pod.storageUrl ?? pod.baseUrl);\n entries.push({\n webId,\n storageUrl,\n storageMode: deriveStorageMode(webId, storageUrl),\n });\n }\n return entries;\n }\n\n private async resolveCandidateWebIds(accountId: string): Promise<string[]> {\n const linkedWebIds = (await this.webIdStore.findLinks(accountId)).map((link) => link.webId);\n if (linkedWebIds.length > 0) {\n return dedupeStrings(linkedWebIds);\n }\n\n if (!this.podLookupRepository?.listByAccountId) {\n return [];\n }\n\n try {\n const pods = await this.podLookupRepository.listByAccountId(accountId);\n return dedupeStrings(pods.flatMap(getPodCandidateWebIds));\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for account ${accountId}: ${error}`);\n return [];\n }\n }\n\n private async isLinkedToAccount(webId: string, accountId: string): Promise<boolean> {\n if (await this.webIdStore.isLinked(webId, accountId)) {\n return true;\n }\n\n if (!this.podLookupRepository?.listByAccountId) {\n return false;\n }\n\n try {\n const pods = await this.podLookupRepository.listByAccountId(accountId);\n return pods.some((pod) => getPodCandidateWebIds(pod).includes(webId));\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for account ${accountId}: ${error}`);\n return false;\n }\n }\n\n private async isResolvableByCurrentSp(webId: string, target: TargetStorage): Promise<boolean> {\n if (target.serviceToken) {\n return (await this.resolveRemoteSpEntries([webId], target)).some((entry) => entry.webId === webId);\n }\n return Boolean(await this.findSpPod(webId, target.storageUrl));\n }\n\n private async findSpPod(webId: string, targetStorageUrl: string): Promise<PodLookupResult | undefined> {\n if (!this.podLookupRepository) {\n this.logger.warn('No PodLookupRepository configured; refusing to expose unscoped WebID choices');\n return undefined;\n }\n\n try {\n if (this.podLookupRepository.findAllByWebId) {\n const pods = await this.podLookupRepository.findAllByWebId(webId);\n return pods.find((pod) => matchesTargetStorage(pod, targetStorageUrl));\n }\n\n if (this.podLookupRepository.findByWebIds) {\n const pods = await this.podLookupRepository.findByWebIds([webId]);\n return pods.find((pod) => matchesTargetStorage(pod, targetStorageUrl));\n }\n\n const pod = await this.podLookupRepository.findByWebId(webId);\n return pod && matchesTargetStorage(pod, targetStorageUrl) ? pod : undefined;\n } catch (error) {\n this.logger.warn(`Pod lookup unavailable for WebID ${webId}: ${error}`);\n return undefined;\n }\n }\n\n private async resolveTargetStorage(\n provider: { issuer: string },\n oidcInteraction?: JsonInteractionHandlerInput['oidcInteraction'],\n ): Promise<TargetStorage> {\n const provisionCode = extractProvisionCode(oidcInteraction);\n if (!provisionCode) {\n return { storageUrl: ensureTrailingSlash(this.storageBaseUrl ?? provider.issuer) };\n }\n\n const payload = new ProvisionCodeCodec(this.provisionBaseUrl ?? provider.issuer).decode(provisionCode);\n if (!payload) {\n throw new BadRequestHttpError('Invalid or expired provisionCode.');\n }\n\n const targetUrl = payload.spDomain\n ? `https://${payload.spDomain}`\n : payload.spUrl;\n return {\n storageUrl: ensureTrailingSlash(targetUrl),\n lookupUrl: ensureTrailingSlash(payload.spUrl),\n serviceToken: payload.serviceToken,\n };\n }\n\n private async resolveRemoteSpEntries(webIds: string[], target: TargetStorage): Promise<WebIdEntry[]> {\n if (!target.lookupUrl || !target.serviceToken || webIds.length === 0) {\n return [];\n }\n\n const response = await this.fetch(new URL('/provision/webids', target.lookupUrl).toString(), {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${target.serviceToken}`,\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n },\n body: JSON.stringify({ webIds }),\n });\n\n if (!response.ok) {\n this.logger.warn(`Remote SP WebID lookup failed: HTTP ${response.status}`);\n return [];\n }\n\n const body = await response.json().catch(() => null) as { entries?: RemoteSpWebIdEntry[] } | null;\n if (!Array.isArray(body?.entries)) {\n return [];\n }\n\n const allowedWebIds = new Set(webIds);\n return body.entries\n .filter((entry) => typeof entry.webId === 'string' && allowedWebIds.has(entry.webId))\n .filter((entry) => typeof entry.storageUrl === 'string' && matchesTargetStorage(\n {\n podId: '',\n accountId: '',\n baseUrl: entry.podUrl ?? entry.storageUrl,\n storageUrl: entry.storageUrl,\n },\n target.storageUrl,\n ))\n .map((entry) => ({\n webId: entry.webId,\n storageUrl: ensureTrailingSlash(entry.storageUrl),\n storageMode: deriveStorageMode(entry.webId, entry.storageUrl),\n }));\n }\n}\n\ninterface TargetStorage {\n storageUrl: string;\n lookupUrl?: string;\n serviceToken?: string;\n}\n\ninterface RemoteSpWebIdEntry {\n webId: string;\n podUrl?: string;\n storageUrl: string;\n}\n\nfunction deriveStorageMode(webId: string, storageUrl: string): 'cloud' | 'local' | 'custom' {\n const webIdRoot = deriveStorageRoot(webId);\n const storageRoot = deriveStorageRoot(storageUrl);\n if (!webIdRoot || !storageRoot) {\n return 'custom';\n }\n return sameStorageRoot(webIdRoot, storageRoot) ? 'cloud' : 'local';\n}\n\nfunction deriveStorageRoot(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n const segments = parsed.pathname.split('/').filter(Boolean);\n if (segments.length === 0) {\n return ensureTrailingSlash(parsed.origin);\n }\n\n return ensureTrailingSlash(new URL(`/${segments[0]}/`, parsed.origin).toString());\n } catch {\n return undefined;\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/u, '') + '/';\n}\n\nfunction normalizeOptionalUrl(url: string | undefined): string | undefined {\n const trimmed = url?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction dedupeStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction getPodCandidateWebIds(pod: PodLookupResult): string[] {\n return dedupeStrings([\n pod.webId,\n ...(pod.webIds ?? []),\n ].filter((value): value is string => typeof value === 'string' && value.length > 0));\n}\n\nfunction matchesTargetStorage(pod: PodLookupResult, targetStorageUrl: string): boolean {\n const candidateUrls = (pod.storageUrl ? [pod.storageUrl] : [pod.baseUrl])\n .filter((value): value is string => typeof value === 'string' && value.length > 0);\n const targetRoot = deriveStorageRoot(targetStorageUrl);\n if (!targetRoot) {\n return false;\n }\n\n for (const candidate of candidateUrls) {\n const candidateRoot = deriveStorageRoot(candidate);\n if (candidateRoot && sameStorageRoot(candidateRoot, targetRoot)) {\n return true;\n }\n\n if (sameStorageScope(candidate, targetRoot)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction sameStorageRoot(left: string, right: string): boolean {\n if (ensureTrailingSlash(left) === ensureTrailingSlash(right)) {\n return true;\n }\n\n const leftUrl = parseUrl(left);\n const rightUrl = parseUrl(right);\n if (!leftUrl || !rightUrl) {\n return false;\n }\n\n return sameUrlAuthority(leftUrl, rightUrl)\n && normalizeUrlPath(leftUrl.pathname) === normalizeUrlPath(rightUrl.pathname);\n}\n\nfunction sameStorageScope(candidate: string, targetRoot: string): boolean {\n const candidateUrl = parseUrl(candidate);\n const targetUrl = parseUrl(targetRoot);\n if (!candidateUrl || !targetUrl || !sameUrlAuthority(candidateUrl, targetUrl)) {\n return false;\n }\n\n const candidatePath = normalizeUrlPath(candidateUrl.pathname);\n const targetPath = normalizeUrlPath(targetUrl.pathname);\n return candidatePath.startsWith(targetPath) || targetPath.startsWith(candidatePath);\n}\n\nfunction sameUrlAuthority(left: URL, right: URL): boolean {\n if (left.protocol !== right.protocol) {\n return false;\n }\n\n if (normalizePort(left) !== normalizePort(right)) {\n return false;\n }\n\n if (left.hostname === right.hostname) {\n return true;\n }\n\n return isLoopbackHostname(left.hostname) && isLoopbackHostname(right.hostname);\n}\n\nfunction parseUrl(url: string): URL | undefined {\n try {\n return new URL(url);\n } catch {\n return undefined;\n }\n}\n\nfunction normalizePort(url: URL): string {\n if (url.port) {\n return url.port;\n }\n\n if (url.protocol === 'http:') {\n return '80';\n }\n\n if (url.protocol === 'https:') {\n return '443';\n }\n\n return '';\n}\n\nfunction normalizeUrlPath(pathname: string): string {\n return ensureTrailingSlash(pathname || '/');\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]';\n}\n\nfunction extractProvisionCode(oidcInteraction: JsonInteractionHandlerInput['oidcInteraction']): string | undefined {\n const params = oidcInteraction?.params as Record<string, unknown> | undefined;\n const value = params?.provisionCode;\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n}\n"]}
|
|
@@ -23,6 +23,18 @@
|
|
|
23
23
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_providerFactory",
|
|
24
24
|
"range": "css:dist/identity/configuration/ProviderFactory.jsonld#ProviderFactory"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_storageBaseUrl",
|
|
28
|
+
"range": {
|
|
29
|
+
"@type": "ParameterRangeUnion",
|
|
30
|
+
"parameterRangeElements": [
|
|
31
|
+
"xsd:string",
|
|
32
|
+
{
|
|
33
|
+
"@type": "ParameterRangeUndefined"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
26
38
|
{
|
|
27
39
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_identityDbUrl",
|
|
28
40
|
"range": {
|
|
@@ -87,6 +99,10 @@
|
|
|
87
99
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler__member_providerFactory",
|
|
88
100
|
"memberFieldName": "providerFactory"
|
|
89
101
|
},
|
|
102
|
+
{
|
|
103
|
+
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler__member_storageBaseUrl",
|
|
104
|
+
"memberFieldName": "storageBaseUrl"
|
|
105
|
+
},
|
|
90
106
|
{
|
|
91
107
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler__member_provisionBaseUrl",
|
|
92
108
|
"memberFieldName": "provisionBaseUrl"
|
|
@@ -156,6 +172,12 @@
|
|
|
156
172
|
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_providerFactory"
|
|
157
173
|
}
|
|
158
174
|
},
|
|
175
|
+
{
|
|
176
|
+
"keyRaw": "storageBaseUrl",
|
|
177
|
+
"value": {
|
|
178
|
+
"@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_storageBaseUrl"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
159
181
|
{
|
|
160
182
|
"keyRaw": "identityDbUrl",
|
|
161
183
|
"value": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.41",
|
|
4
4
|
"description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
|
|
5
5
|
"repository": "https://github.com/undefinedsco/xpod",
|
|
6
6
|
"author": "developer@undefineds.co",
|