191 lines
6.3 KiB
Markdown
191 lines
6.3 KiB
Markdown
# JavaScript SDK
|
|
|
|
The SDK is a JavaScript library embedded in participating websites. It wraps
|
|
the browser messaging layer between the page and the extension, exposing a
|
|
clean API to website developers. Without it, a site could handle Port messaging
|
|
directly — the SDK makes that unnecessary.
|
|
|
|
The SDK has no logic of its own. It does not perform cryptography, hold state,
|
|
or make network requests. It is a thin bridge.
|
|
|
|
---
|
|
|
|
## Initialisation
|
|
|
|
The website developer initialises the SDK with the site's registration
|
|
certificate, obtained from the LOCQR backend admin panel when registering the
|
|
domain. The cert is a static value that changes only on renewal.
|
|
|
|
```js
|
|
locqr.init({ cert: 'BASE64_ENCODED_CERT' });
|
|
```
|
|
|
|
The cert is not embedded in the page DOM. Passing it at initialisation is
|
|
sufficient; the SDK forwards it to the extension at connect time.
|
|
|
|
---
|
|
|
|
## Communication model
|
|
|
|
The SDK communicates with the extension service worker directly via
|
|
`chrome.runtime.connect(extensionId)` — no content script is involved. This
|
|
opens a long-lived bidirectional **Port**. The extension reads the real page
|
|
URL from `sender.tab.url` (browser-provided; cannot be spoofed by the page).
|
|
|
|
```
|
|
website code ↔ SDK ↔ chrome.runtime Port ↔ service worker
|
|
```
|
|
|
|
All SDK↔extension communication for the lifetime of the page uses this Port.
|
|
The full Port message protocol (message types, `LocqrError`, `Credential`
|
|
schemas) is defined in `../interfaces.md`.
|
|
|
|
---
|
|
|
|
## Activation
|
|
|
|
After `locqr.init()`, the SDK opens the Port and sends the cert. The extension
|
|
verifies it and responds with the outcome. `locqr.ready` is a Promise that
|
|
resolves with the authorised feature set on success, or rejects with the
|
|
verification error.
|
|
|
|
```js
|
|
try {
|
|
const { features } = await locqr.ready;
|
|
// features: e.g. ['login']
|
|
} catch (err) {
|
|
// site not registered, cert invalid, or account error
|
|
}
|
|
```
|
|
|
|
The SDK exposes only the methods corresponding to the declared features. A site
|
|
whose cert does not include `"vault"` does not get `locqr.encrypt()`.
|
|
|
|
---
|
|
|
|
## API surface
|
|
|
|
### `locqr.init(options)`
|
|
|
|
Initialises the SDK. Must be called before any other method.
|
|
|
|
```ts
|
|
locqr.init({ cert: string });
|
|
```
|
|
|
|
### `locqr.ready → Promise<{ features: string[] }>`
|
|
|
|
Resolves when the extension has verified the site and returned an authorised
|
|
feature set. Rejects with an error object if the extension is not installed, the
|
|
site is not registered, the cert is invalid, or the account is not in good
|
|
standing.
|
|
|
|
```ts
|
|
type LocqrError = {
|
|
code: 'NOT_INSTALLED' | 'NOT_REGISTERED' | 'CERT_INVALID' | 'ACCOUNT_ERROR' | 'RUN_FAILED';
|
|
reason?: string; // sub-reason for CERT_INVALID / ACCOUNT_ERROR / RUN_FAILED
|
|
security?: boolean; // true for security-class errors
|
|
}
|
|
```
|
|
|
|
`NOT_INSTALLED` is the only code produced before a Port is opened. All other
|
|
codes arrive from the extension over the Port after the connection succeeds.
|
|
|
|
### `locqr.requestCredential() → Promise<Credential>`
|
|
|
|
Signals to the extension that a credential is needed for the current site.
|
|
Triggers Phase 1 of the run. Resolves when the credential is delivered at the
|
|
end of Phase 4. Rejects if the run fails or is aborted.
|
|
|
|
```js
|
|
const { username, password } = await locqr.requestCredential();
|
|
```
|
|
|
|
```ts
|
|
type Credential = {
|
|
username: string;
|
|
password: string;
|
|
// totp and further fields deferred
|
|
}
|
|
```
|
|
|
|
### `locqr.on(event, handler)` / `locqr.off(event, handler)`
|
|
|
|
Registers and removes handlers for extension-initiated events.
|
|
|
|
| Event | When | Payload |
|
|
|---|---|---|
|
|
| `ready` | Verification complete | `{ features: string[] }` |
|
|
| `run:started` | Phase 1 complete; QR displayed | `{}` |
|
|
| `run:delivered` | Credential delivered | `{ credential: Credential }` |
|
|
| `run:error` | Run failed | `{ error: LocqrError }` |
|
|
|
|
`run:delivered` fires alongside the `requestCredential()` Promise resolving.
|
|
Sites using the event model do not need to call `requestCredential()`. Both
|
|
models are valid; mixing them on the same run is not.
|
|
|
|
---
|
|
|
|
## GYBBR: vault API (deferred)
|
|
|
|
For GYBBR, the SDK will expose cryptographic vault operations once the `"vault"`
|
|
feature is declared in the site's cert. The website requests operations by key
|
|
ID and receives results; key material never leaves the extension.
|
|
|
|
Anticipated surface (not yet designed):
|
|
|
|
```js
|
|
const ciphertext = await locqr.encrypt({ keyId: 'k1', data: plaintext });
|
|
const plaintext = await locqr.decrypt({ keyId: 'k1', data: ciphertext });
|
|
```
|
|
|
|
Extension-initiated vault notifications (e.g. identity key available) will be
|
|
delivered as events via `locqr.on`. Exact method signatures and event names are
|
|
deferred to the GYBBR design phase.
|
|
|
|
---
|
|
|
|
## Static properties
|
|
|
|
The SDK exposes stable reference values as static properties, available
|
|
immediately after the script loads — before `locqr.init()` and regardless of
|
|
whether the extension is installed. Website developers can use these to build
|
|
install prompts, help text, or footer links without hardcoding URLs themselves.
|
|
|
|
```js
|
|
locqr.installLinks // { chrome: string, firefox: string, ... }
|
|
locqr.mobileLinks // { android: string, ios: string }
|
|
locqr.website // LOCQR public website URL
|
|
```
|
|
|
|
`locqr.ready` rejecting with `code: 'NOT_INSTALLED'` is the signal to surface
|
|
these links. How they are presented is entirely the website's decision; the SDK
|
|
provides the data only. DOM helpers, install-prompt components, etc. are
|
|
deferred.
|
|
|
|
`ios` within `mobileLinks` is present as a placeholder but empty until the iOS
|
|
app exists.
|
|
|
|
---
|
|
|
|
## Distribution
|
|
|
|
How the SDK is packaged and distributed (npm, CDN script tag, bundled asset)
|
|
is deferred. The extension ID baked into the SDK is stable for the lifetime of
|
|
the Store-published extension; it changes only on a full re-publication, which
|
|
is an extraordinary event. Development builds use a different ID configured via
|
|
`locqr.init({ cert, extensionId: DEV_ID })`.
|
|
|
|
---
|
|
|
|
## Security notes
|
|
|
|
- The SDK never receives key material. It receives only operation results
|
|
(credentials, ciphertext, plaintext of things the site requested decrypted).
|
|
- The real page URL is read by the extension from `sender.tab.url` — the page
|
|
cannot supply or spoof it.
|
|
- Feature enforcement is in the extension. The SDK surface is a reflection of
|
|
what the cert permits, not an independent gate.
|
|
- Once a credential is passed to the website via `requestCredential()`, it has
|
|
left the LOCQR security boundary. What the site does with it is out of scope.
|