Using browser.ssi
browser.ssi
is the underlying API that is more powerful than window.ssi. It does everything window.ssi does, and more:
- Can read all the credentials across protocols (if the user self-sovereignly does allow)
- Can read the user’s setting states (as long as there are no privacy and security issues)
- Can integrate element APIs to create any flow you want
Instead, this API is limited where you can use. It would be assumed to be built by Built-in API or Experimental API of Firefox based on WebExtensions, so you can access through a web extension.
Basically, this API can be called in background script, but possibly content script and devtools as well. You can check what scope an browser.ssi has in your using implementation by looking at ssi’s manifest file. See for example here.
Set up
To use this API, you declare in the permissions
property in your WebExtension’s manifest.json
file. For example:
{
"manifest_version": 2,
"name": "Example Web Extension",
"permissions": ["ssi"],
}
If you want the subset of the specific protocol, please add the one with ssi
together.
{
"permissions": ["ssi", "ssi.nostr"],
}
Basic usage
The ssi declared in the permissions property in your manifest file is injected into the global object browser
.
await browser.ssi.searchCredentialsWithoutSecret(protocolName, credentialName, primary)
Search store
Example of a full search for credential store service:
// set arguments to empty
const credentials = await browser.ssi.searchCredentialsWithoutSecret("", "", false)
store.set(credentials.map(credential => doSomething(credential)))
In effect, the results returned are filtered by the user’s preferences, privacy and security reasons.
Request a task related to the key
You can sign and encrypt/decrypt using the internal key that is set as primary for a specific protocol.
const signature = await browser.ssi.nostr.sign(message)
Get user settings
You will get what you need:
const preferences = await browser.ssi.nostr.getPrefs()
store.set(preferences)
Receive notifications when user settings status changes
You can listen to this as a special event.
const onPrimaryChangedCallback = async () => {
// Get the new primary key
const credentials = await browser.ssi.searchCredentialsWithoutSecret(
"nostr",
"nsec",
true
)
const pubkey = decodeNpub(credentials[0].identifier)
// Send the message to the contents
const tabs = await browser.tabs.query()
for (const tab of tabs) {
browser.tabs
.sendMessage(tab.id, {
action: "accountChanged",
args: { pubkey },
})
}
}
browser.ssi.nostr.onPrimaryChanged.addListener(onPrimaryChangedCallback)
At first glance it looks similar to a normal EventListener, but it may be useful to know that it is a different mechanism to cross IPC.
Authorization
It’s always the libra between the UX and security for powerful features. Just like the history of camera and notification permissions, this one requires careful considerations and is one of the topics we explore the most: Authorization flow.
Currently, the combination of the trusted site method and password authentication is implemented in browser.ssi.askPermission
. This is a two-step method.
The trusted site that the user has explicitly declared trust for by specifying the app’s URL is the top level. If that is NG, the browser’s primary password or OS account password authentication is presented to the user. However, this is the self-sovereign browser, so, if both settings are explicitly disabled, it is accepted the user has self-sovereignly chosen that the app does not need the user’s permission to use their key, and the request will be allowed through unconditionally.
This can be done below.
const permitted = await browser.ssi.askPermission(protocolName, credentialName, tabId, message)