fedipod-server 0.12.1 → 0.13.1
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/README.md +7 -0
- package/dist/claims.d.ts +8 -0
- package/dist/claims.js +10 -0
- package/dist/handler.d.ts +7 -0
- package/dist/handler.js +33 -1
- package/dist/handler.jsonld +4 -0
- package/lib/client/c2s.mjs +6 -4
- package/lib/core/as2.mjs +69 -18
- package/lib/core/graphview.mjs +289 -0
- package/lib/core/intake/index.mjs +4 -4
- package/lib/core/intake/verify.mjs +4 -3
- package/lib/core/publisher/restore.mjs +7 -4
- package/lib/gateway/front-core.mjs +14 -6
- package/lib/server/embed.mjs +59 -1
- package/package.json +1 -1
- package/web/app/agent.mjs +7 -6
- package/web/app/boot.mjs +2 -3
- package/web/app/deliver-relay.mjs +18 -1
- package/web/app/dist/boot.js +22 -3
- package/web/app/dist/boot.js.map +2 -2
- package/web/app/dist/sw.js +261 -44
- package/web/app/dist/sw.js.map +4 -4
- package/web/app/keys-browser.mjs +27 -4
- package/web/app/signup.mjs +2 -3
- package/web/app/site/boot.js +22 -3
- package/web/app/site/sw.js +341 -58
package/web/app/keys-browser.mjs
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
// this returns the same, RSA only. FEP-8b32 Ed25519 proofs are optional — the
|
|
5
5
|
// deliverer treats a null edPrivate as "no proof", and an unproved activity
|
|
6
6
|
// still federates — so the browser MVP omits them.
|
|
7
|
+
//
|
|
8
|
+
// The private key is imported NON-extractable, and the copy this browser keeps
|
|
9
|
+
// in IndexedDB is that CryptoKey, not the PEM: it can sign and cannot be read
|
|
10
|
+
// out, so a script that reaches this origin's storage gets nothing it can carry
|
|
11
|
+
// away. The PEM record exists only in memory, between unwrap and import.
|
|
7
12
|
import { kvGet, kvPut } from './idb-kv.mjs';
|
|
8
13
|
import * as podState from '../../lib/pod/state.mjs';
|
|
9
14
|
import { isKeyEnvelope, KeyPasswordNeeded } from './keystore.mjs';
|
|
@@ -13,10 +18,26 @@ const pemToDer = (pem) => Uint8Array.from(
|
|
|
13
18
|
|
|
14
19
|
export async function importSigningKey(keysRecord) {
|
|
15
20
|
const rsaPrivate = await crypto.subtle.importKey('pkcs8', pemToDer(keysRecord.rsa.privatePem),
|
|
16
|
-
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
|
|
21
|
+
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['sign']);
|
|
17
22
|
return { rsaPrivate, rsaPublicPem: keysRecord.rsa.publicPem, edPrivate: null, edPublicMultibase: null };
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
/** What the cache holds: the non-extractable key and the public half. */
|
|
26
|
+
const fromCache = (c) => ({ rsaPrivate: c.rsaPrivate, rsaPublicPem: c.rsaPublicPem, edPrivate: null, edPublicMultibase: null });
|
|
27
|
+
const isOpenedKey = (c) => c?.rsaPrivate?.type === 'private' && typeof c.rsaPublicPem === 'string';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Keep this browser's opened copy: import the record and store the CryptoKey.
|
|
31
|
+
* Best effort on the store — a browser that refuses IndexedDB (private mode)
|
|
32
|
+
* asks for the password on the way back in. Returns the imported keys.
|
|
33
|
+
*/
|
|
34
|
+
export async function cacheOpenedKeys(actorUrl, keysRecord) {
|
|
35
|
+
const keys = await importSigningKey(keysRecord);
|
|
36
|
+
await kvPut(keyCacheKey(actorUrl), { rsaPrivate: keys.rsaPrivate, rsaPublicPem: keys.rsaPublicPem })
|
|
37
|
+
.catch(() => {});
|
|
38
|
+
return keys;
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
// Where this browser keeps its own opened copy of the signing key. The pod's
|
|
21
42
|
// copy is wrapped under the account password; this one is not, because it never
|
|
22
43
|
// leaves the origin — the same protection the OIDC session in the next IDB row
|
|
@@ -34,13 +55,15 @@ export const keyCacheKey = (actorUrl) => `signing-keys:${actorUrl}`;
|
|
|
34
55
|
// needs the password and so has to go back to the page.
|
|
35
56
|
export async function loadKeysFromPod(remote, urls) {
|
|
36
57
|
const cached = await kvGet(keyCacheKey(urls.actor)).catch(() => null);
|
|
37
|
-
if (cached
|
|
58
|
+
if (isOpenedKey(cached)) return fromCache(cached);
|
|
59
|
+
// A copy stored as PEM by an earlier build: import it and store the key form
|
|
60
|
+
// in its place, so the PEM is gone from storage after one boot.
|
|
61
|
+
if (cached?.rsa?.privatePem) return cacheOpenedKeys(urls.actor, cached);
|
|
38
62
|
|
|
39
63
|
const doc = await podState.readWrappedKeys(remote, urls);
|
|
40
64
|
if (isKeyEnvelope(doc)) throw new KeyPasswordNeeded();
|
|
41
65
|
if (!doc || !doc.rsa) throw new Error('no signing key on the pod — sign up did not finish');
|
|
42
66
|
// A pre-wrapping install. Cache it so the next boot is one read, and leave
|
|
43
67
|
// the pod's copy alone: re-wrapping it would need the password we do not have.
|
|
44
|
-
|
|
45
|
-
return importSigningKey(doc);
|
|
68
|
+
return cacheOpenedKeys(urls.actor, doc);
|
|
46
69
|
}
|
package/web/app/signup.mjs
CHANGED
|
@@ -14,8 +14,7 @@ import { createAccountWithPod, mintCredential, makeDpopSession, revokeCredential
|
|
|
14
14
|
import { generateKeys, wrapKeys } from './keystore.mjs';
|
|
15
15
|
import { BrowserRemotePod } from './pod-remote.mjs';
|
|
16
16
|
import * as podState from '../../lib/pod/state.mjs';
|
|
17
|
-
import {
|
|
18
|
-
import { keyCacheKey } from './keys-browser.mjs';
|
|
17
|
+
import { cacheOpenedKeys } from './keys-browser.mjs';
|
|
19
18
|
|
|
20
19
|
// The container everything the agent publishes hangs under. New pods made here
|
|
21
20
|
// use `fedipod/`; the agent's own default stays `activitypods-js/` for installs
|
|
@@ -167,7 +166,7 @@ export async function signUp(answers, { onStep = () => {}, frontOrigin = null }
|
|
|
167
166
|
// This browser's own opened copy, so the boot after the login redirect
|
|
168
167
|
// needs no password. Best effort: a browser that refuses IndexedDB (private
|
|
169
168
|
// mode) simply asks for the password on the way back in.
|
|
170
|
-
await
|
|
169
|
+
await cacheOpenedKeys(actorUrl, keys);
|
|
171
170
|
prog.keys = keys; prog.keysStored = true;
|
|
172
171
|
keysStep.ok();
|
|
173
172
|
} else {
|
package/web/app/site/boot.js
CHANGED
|
@@ -33231,6 +33231,26 @@ async function kvPut(key, val) {
|
|
|
33231
33231
|
}
|
|
33232
33232
|
|
|
33233
33233
|
// web/app/keys-browser.mjs
|
|
33234
|
+
var pemToDer = (pem) => Uint8Array.from(
|
|
33235
|
+
atob(pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "")),
|
|
33236
|
+
(c) => c.charCodeAt(0)
|
|
33237
|
+
);
|
|
33238
|
+
async function importSigningKey(keysRecord) {
|
|
33239
|
+
const rsaPrivate = await crypto.subtle.importKey(
|
|
33240
|
+
"pkcs8",
|
|
33241
|
+
pemToDer(keysRecord.rsa.privatePem),
|
|
33242
|
+
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
|
|
33243
|
+
false,
|
|
33244
|
+
["sign"]
|
|
33245
|
+
);
|
|
33246
|
+
return { rsaPrivate, rsaPublicPem: keysRecord.rsa.publicPem, edPrivate: null, edPublicMultibase: null };
|
|
33247
|
+
}
|
|
33248
|
+
async function cacheOpenedKeys(actorUrl, keysRecord) {
|
|
33249
|
+
const keys = await importSigningKey(keysRecord);
|
|
33250
|
+
await kvPut(keyCacheKey(actorUrl), { rsaPrivate: keys.rsaPrivate, rsaPublicPem: keys.rsaPublicPem }).catch(() => {
|
|
33251
|
+
});
|
|
33252
|
+
return keys;
|
|
33253
|
+
}
|
|
33234
33254
|
var keyCacheKey = (actorUrl) => `signing-keys:${actorUrl}`;
|
|
33235
33255
|
|
|
33236
33256
|
// web/app/signup.mjs
|
|
@@ -33321,8 +33341,7 @@ async function signUp(answers, { onStep = () => {
|
|
|
33321
33341
|
} catch (e) {
|
|
33322
33342
|
throw new Error(`could not store the signing key on the pod (${e.message}). The credential is for ${credential.webId} \u2014 that WebID must own ${pod} and its ${AP_ROOT} must be writable by it.`);
|
|
33323
33343
|
}
|
|
33324
|
-
await
|
|
33325
|
-
});
|
|
33344
|
+
await cacheOpenedKeys(actorUrl, keys);
|
|
33326
33345
|
prog.keys = keys;
|
|
33327
33346
|
prog.keysStored = true;
|
|
33328
33347
|
keysStep.ok();
|
|
@@ -33668,7 +33687,7 @@ window.fedipodUnlock = async (password) => {
|
|
|
33668
33687
|
if (!isKeyEnvelope(doc)) throw new Error("this account's key is not locked \u2014 nothing to unlock");
|
|
33669
33688
|
const rec = await unwrapKeys(doc, password);
|
|
33670
33689
|
const actorUrl = `${cfg.remotePod}${cfg.root || AP_ROOT}ap/actor`;
|
|
33671
|
-
await
|
|
33690
|
+
await cacheOpenedKeys(actorUrl, rec);
|
|
33672
33691
|
await bootWorker();
|
|
33673
33692
|
};
|
|
33674
33693
|
window.fedipodSignup = async ({ onStep, ...answers }) => {
|