Skip to content

WebID UI#16

Open
langsamu wants to merge 5 commits into
mainfrom
webid-picker
Open

WebID UI#16
langsamu wants to merge 5 commits into
mainfrom
webid-picker

Conversation

@langsamu

@langsamu langsamu commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Adds a new custom element for getting a WebID URI from the user via a modal dialog.

Integrates same into existing custom element that gets an authorization server URI from the user.
(But new element can be used on its own as well.)

Supports the well-known Solid app UX pattern of getting IdP from profile.


New button Use WebID to get authorization server URI from WebID profile:
image

New custom element toi get a WebID URI from user:
image

@langsamu langsamu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explanations.

Comment thread src/SimpleDataset.ts
import type { DatasetCore, Quad, Term } from "@rdfjs/types"

// TODO: Eliminate once N3 stops trying to nodejs the browser
export class SimpleDataset implements DatasetCore {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the proper way to do this is.

I find N3 unusable in a modern module way. Maybe I just don't know how to configure package dependencies and/or import maps and/or unpackagers.

Specifically N3 seems to be pulling in readable-stream which is Node only, so here's this minimal implementation instead of N3.Store.

Comment thread src/WebIdPicker.ts
</dialog>
`

export class WebIdPicker extends HTMLElement {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very simple custom element that is quite similar to the existing IdP picker.

It's a modal that asks for a WebID URI, with an optional datalist for soft select functionality, as well as slots for text customization and cancel support.
I'll add CSS parts later.

Comment thread src/WebIdPicker.ts
Comment on lines +62 to +67
this.#input.addEventListener("change", () => {
if (this.#input.value.includes(searchString)) {
const start = this.#input.value.indexOf(searchString)
this.#input.setSelectionRange(start, start + searchString.length, "forward")
}
})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the options include the string "YOUR_USERNAME" in the WebID URI template then it will be selected for the user to type over.

Comment thread index.html
Comment on lines -11 to +13
"dpop": "https://cdn.jsdelivr.net/npm/dpop@2.1.1/+esm"
"dpop": "https://cdn.jsdelivr.net/npm/dpop@2.1.1/+esm",
"n3": "https://esm.sh/n3@2.0.3",
"@rdfjs/wrapper": "https://cdn.jsdelivr.net/npm/@rdfjs/wrapper@0.34.0/+esm"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, this is for the demo only. Just so I don't have to worry about transpilation and bundling at the moment.

Comment thread index.html
Comment on lines +21 to +29
/* Reactive fetch infrastructure */
const ui = document.querySelector("authorization-code-flow")
const issuerUi = document.querySelector("idp-picker")
const callbackUri = new URL("/callback.html", location.href).toString()

const dPoPTokenProvider = new DPoPTokenProvider(callbackUri, ui.getCode.bind(ui), issuerUi.getIssuer.bind(issuerUi))

const fetch = new ReactiveFetchManager([dPoPTokenProvider]).fetch

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Substantial change here is that in the demo, global fetch is no longer patched. Because now we're using it to get WebIDs too.

Fun fact: Some Solid servers return 401 for a bogus WebID (e.g. https://YOUR_USERNAME.solidcommunity.net/profile/card#me).

Comment thread index.html
Comment on lines +89 to +96
<webid-picker slot="webid-picker">
<option value="https://YOUR_USERNAME.solidcommunity.net/profile/card#me"></option>
<option value="https://id.inrupt.com/YOUR_USERNAME"></option>
<option value="https://YOUR_USERNAME.datapod.igrant.io/profile/card#me"></option>
<option value="https://YOUR_USERNAME.solidweb.app/profile/card.jsonld#me"></option>
<option value="https://teamid.live/YOUR_USERNAME/profile/card#me"></option>
<option value="https://YOUR_USERNAME.solidweb.org/profile/card#me"></option>
</webid-picker>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting pattern of composing custom elements:

The new <webid-picker/> is a standalone web component.
But it also integrates this way with the existing <idp-picker/>.
Advantage over programmatic construction is customizability and stylability.

Comment thread src/IdpPicker.ts
Comment on lines +39 to +41
<button type="button" id="webid" hidden disabled accesskey="w">
<slot name="webid-button">Use <u>W</u>ebID</slot>
</button>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New button on existing UI that allows invoking the new element to get WebID from there.

Comment thread src/IdpPicker.ts
shadow.querySelector("datalist")!.appendChild(option.cloneNode())
}

this.#webIdButton.disabled = this.#webIdButton.hidden = this.#webIdPicker === null

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IdP selecto works as before if there's no WebID picker child. But if there is then it can be invoked here.

Comment thread src/IdpPicker.ts
Comment on lines +125 to +136
async #useWebId() {
this.#webIdButton.disabled = true
try {
const webid = await this.#webIdPicker!.getWebId(this.#request!)
const issuer = await issuerFromWebId(webid, this.#request!.signal)
this.#input.value = issuer.href
} finally {
this.#webIdButton.disabled = false
this.#input.focus()
}
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React to click of new button. Get URI, load, parse, extract issuer and populate our own input.

Comment thread src/IdpPicker.ts
}
}

async function issuerFromWebId(webId: URL, signal: AbortSignal): Promise<URL> {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very simple and intentionally paranoid method to load and parse WebID and extract issuer.

@langsamu langsamu marked this pull request as ready for review June 14, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant