@pdsjs/spaces 2.0.1 → 2.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdsjs/spaces",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -20,10 +20,11 @@
20
20
  "./car": "./src/car.js",
21
21
  "./token": "./src/token.js",
22
22
  "./authority": "./src/authority.js",
23
- "./service-auth": "./src/service-auth.js"
23
+ "./service-auth": "./src/service-auth.js",
24
+ "./admin": "./src/admin.js"
24
25
  },
25
26
  "dependencies": {
26
- "@pdsjs/core": "2.1.0"
27
+ "@pdsjs/core": "2.3.0"
27
28
  },
28
29
  "publishConfig": {
29
30
  "access": "public"
package/src/admin.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @param {Object} ctx
3
+ * @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
4
+ * @param {() => Promise<string|null>} ctx.getDid
5
+ * @returns {import('@pdsjs/core/ports').SpaceAdminPort}
6
+ */
7
+ export declare function createSpaceAdmin({ spaceStorage, getDid }: {
8
+ spaceStorage: import('@pdsjs/core/ports').SpaceStoragePort;
9
+ getDid: () => Promise<string | null>;
10
+ }): import('@pdsjs/core/ports').SpaceAdminPort;
package/src/admin.js ADDED
@@ -0,0 +1,115 @@
1
+ // @pdsjs/spaces/admin - space management for the account page.
2
+ //
3
+ // The account page runs behind the owner's own session, so these skip the
4
+ // scope checks the XRPC management endpoints apply to third-party callers.
5
+ // They mutate the same storage the same way com.atproto.simplespace.* does.
6
+
7
+ import { cborDecode, createTid } from '@pdsjs/core/repo';
8
+ import { LtHash } from './lthash.js';
9
+ import { makeSpaceRow } from './space-row.js';
10
+ import { formatSpaceUri } from './uri.js';
11
+ import { applyWrites } from './writer.js';
12
+
13
+ /**
14
+ * @param {Object} ctx
15
+ * @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
16
+ * @param {() => Promise<string|null>} ctx.getDid
17
+ * @returns {import('@pdsjs/core/ports').SpaceAdminPort}
18
+ */
19
+ export function createSpaceAdmin({ spaceStorage, getDid }) {
20
+ /** @returns {Promise<string>} */
21
+ async function requireDid() {
22
+ const did = await getDid();
23
+ if (!did) throw new Error('server not initialised');
24
+ return did;
25
+ }
26
+
27
+ /**
28
+ * @param {string} space
29
+ * @returns {Promise<void>}
30
+ */
31
+ async function requireOwnedSpace(space) {
32
+ const row = await spaceStorage.getSpace(space);
33
+ if (!row?.isOwner || row.deletedAt) {
34
+ throw new Error(`no such space: ${space}`);
35
+ }
36
+ }
37
+
38
+ return {
39
+ async createSpace({ type, skey }) {
40
+ if (!type.includes('.')) {
41
+ throw new Error(`space type must be an NSID: ${type}`);
42
+ }
43
+ if (skey && skey.length > 512) {
44
+ throw new Error('skey is too long');
45
+ }
46
+ const did = await requireDid();
47
+ const uri = formatSpaceUri({
48
+ spaceDid: did,
49
+ spaceType: type,
50
+ skey: skey || createTid(),
51
+ });
52
+ if (await spaceStorage.getSpace(uri)) {
53
+ throw new Error(`space already exists: ${uri}`);
54
+ }
55
+ await spaceStorage.putSpace(makeSpaceRow(uri, { isOwner: true }));
56
+ // The owner joins their own space: a member-list space checks membership
57
+ // before it issues a credential, and that is the default policy.
58
+ await spaceStorage.addMember(uri, did);
59
+ return { uri };
60
+ },
61
+
62
+ async addMember(space, did) {
63
+ if (!did.startsWith('did:')) {
64
+ throw new Error(`not a DID: ${did}`);
65
+ }
66
+ await requireOwnedSpace(space);
67
+ await spaceStorage.addMember(space, did);
68
+ },
69
+
70
+ async removeMember(space, did) {
71
+ const owner = await requireDid();
72
+ if (did === owner) {
73
+ throw new Error('the owner cannot leave their own space');
74
+ }
75
+ await requireOwnedSpace(space);
76
+ await spaceStorage.removeMember(space, did);
77
+ },
78
+
79
+ async getRecord(space, collection, rkey) {
80
+ await requireOwnedSpace(space);
81
+ const row = await spaceStorage.getSpaceRecord(space, collection, rkey);
82
+ return row ? /** @type {Object} */ (cborDecode(row.value)) : null;
83
+ },
84
+
85
+ async createRecord(space, collection, rkey, record) {
86
+ await writeRecord(space, 'create', collection, rkey, record);
87
+ },
88
+
89
+ async putRecord(space, collection, rkey, record) {
90
+ await writeRecord(space, 'put', collection, rkey, record);
91
+ },
92
+ };
93
+
94
+ /**
95
+ * @param {string} space
96
+ * @param {'create'|'put'} action
97
+ * @param {string} collection
98
+ * @param {string} rkey
99
+ * @param {Object} record
100
+ * @returns {Promise<void>}
101
+ */
102
+ async function writeRecord(space, action, collection, rkey, record) {
103
+ const did = await requireDid();
104
+ await requireOwnedSpace(space);
105
+ const commit = await applyWrites(spaceStorage, {
106
+ space,
107
+ writes: [{ action, collection, rkey, record }],
108
+ });
109
+ // What fireNotifyWrite does after an XRPC write into the owner's own
110
+ // space: record the owner in the writer set, so listRepos knows this
111
+ // repo before its first push.
112
+ const hash = await new LtHash(commit.setHash).digest();
113
+ await spaceStorage.putSpaceWriter(space, did, commit.rev, hash);
114
+ }
115
+ }
@@ -32,6 +32,16 @@ export declare function atprotoSigningKey(didDoc: any, did: string): string;
32
32
  * @returns {string}
33
33
  */
34
34
  export declare function spaceHostEndpoint(didDoc: any, did: string): string;
35
+ /**
36
+ * Where a service identifier points. A service identifier is a DID with an
37
+ * optional fragment naming the entry in its DID document. Without a fragment
38
+ * the account's ordinary PDS entry answers.
39
+ *
40
+ * @param {any} didDoc
41
+ * @param {string} service - e.g. 'did:web:syncer.example#atproto_space_syncer'
42
+ * @returns {string|null}
43
+ */
44
+ export declare function namedServiceEndpoint(didDoc: any, service: string): string | null;
35
45
  /**
36
46
  * The `aud` a delegation token must carry for a given authority.
37
47
  * @param {string} spaceDid
@@ -41,7 +51,7 @@ export declare function spaceHostAudience(spaceDid: string): string;
41
51
  /**
42
52
  * Build the `getSigningKey` callback verifySpaceToken expects.
43
53
  *
44
- * @param {(did: string) => Promise<any>} resolveDid
45
- * @returns {(iss: string, kid?: string) => Promise<string>}
54
+ * @param {import('@pdsjs/core/ports').DidResolverPort} resolveDid
55
+ * @returns {(iss: string, kid?: string, forceRefresh?: boolean) => Promise<string>}
46
56
  */
47
- export declare function didKeyResolver(resolveDid: (did: string) => Promise<any>): (iss: string, kid?: string) => Promise<string>;
57
+ export declare function didKeyResolver(resolveDid: import('@pdsjs/core/ports').DidResolverPort): (iss: string, kid?: string, forceRefresh?: boolean) => Promise<string>;
package/src/authority.js CHANGED
@@ -120,6 +120,24 @@ export function spaceHostEndpoint(didDoc, did) {
120
120
  return endpoint;
121
121
  }
122
122
 
123
+ /**
124
+ * Where a service identifier points. A service identifier is a DID with an
125
+ * optional fragment naming the entry in its DID document. Without a fragment
126
+ * the account's ordinary PDS entry answers.
127
+ *
128
+ * @param {any} didDoc
129
+ * @param {string} service - e.g. 'did:web:syncer.example#atproto_space_syncer'
130
+ * @returns {string|null}
131
+ */
132
+ export function namedServiceEndpoint(didDoc, service) {
133
+ const [did, fragment] = service.split('#');
134
+ return serviceEndpoint(
135
+ didDoc,
136
+ did,
137
+ fragment ? `#${fragment}` : ATPROTO_PDS_ID,
138
+ );
139
+ }
140
+
123
141
  /**
124
142
  * The `aud` a delegation token must carry for a given authority.
125
143
  * @param {string} spaceDid
@@ -132,12 +150,12 @@ export function spaceHostAudience(spaceDid) {
132
150
  /**
133
151
  * Build the `getSigningKey` callback verifySpaceToken expects.
134
152
  *
135
- * @param {(did: string) => Promise<any>} resolveDid
136
- * @returns {(iss: string, kid?: string) => Promise<string>}
153
+ * @param {import('@pdsjs/core/ports').DidResolverPort} resolveDid
154
+ * @returns {(iss: string, kid?: string, forceRefresh?: boolean) => Promise<string>}
137
155
  */
138
156
  export function didKeyResolver(resolveDid) {
139
- return async (iss, kid) => {
140
- const didDoc = await resolveDid(iss);
157
+ return async (iss, kid, forceRefresh = false) => {
158
+ const didDoc = await resolveDid(iss, { forceRefresh });
141
159
  if (!didDoc) {
142
160
  throw new SpaceAuthorityError(`Could not resolve ${iss}`, 'DidNotFound');
143
161
  }
package/src/car.d.ts CHANGED
@@ -15,16 +15,18 @@ export type SerializedRecord = {
15
15
  rkey: string;
16
16
  cid: string;
17
17
  /**
18
- * - DAG-CBOR encoded record
18
+ * - DAG-CBOR encoded record, absent under
19
+ * excludeValues
19
20
  */
20
- value: Uint8Array;
21
+ value?: Uint8Array;
21
22
  };
22
23
  /**
23
24
  * @typedef {Object} SerializedRecord
24
25
  * @property {string} collection
25
26
  * @property {string} rkey
26
27
  * @property {string} cid
27
- * @property {Uint8Array} value - DAG-CBOR encoded record
28
+ * @property {Uint8Array} [value] - DAG-CBOR encoded record, absent under
29
+ * excludeValues
28
30
  */
29
31
  /**
30
32
  * Serialize a repo as a CAR: two roots in order — the signed commit, then the
@@ -34,8 +36,15 @@ export type SerializedRecord = {
34
36
  * Records are collected up front because the index has to precede the blocks it
35
37
  * describes.
36
38
  *
39
+ * Under `excludeValues` the CAR carries the two roots and no other block. The
40
+ * index still folds into the same set hash, so a syncer authenticates it against
41
+ * the commit and then fetches only the records it lacks.
42
+ *
37
43
  * @param {import('./commit.js').SignedCommit} commit
38
44
  * @param {Iterable<SerializedRecord>} records
45
+ * @param {{excludeValues?: boolean}} [opts]
39
46
  * @returns {Promise<Uint8Array>}
40
47
  */
41
- export declare function serializeRepo(commit: import('./commit.js').SignedCommit, records: Iterable<SerializedRecord>): Promise<Uint8Array>;
48
+ export declare function serializeRepo(commit: import('./commit.js').SignedCommit, records: Iterable<SerializedRecord>, opts?: {
49
+ excludeValues?: boolean;
50
+ }): Promise<Uint8Array>;
package/src/car.js CHANGED
@@ -31,7 +31,8 @@ export function byCanonicalKey(a, b) {
31
31
  * @property {string} collection
32
32
  * @property {string} rkey
33
33
  * @property {string} cid
34
- * @property {Uint8Array} value - DAG-CBOR encoded record
34
+ * @property {Uint8Array} [value] - DAG-CBOR encoded record, absent under
35
+ * excludeValues
35
36
  */
36
37
 
37
38
  /**
@@ -42,11 +43,16 @@ export function byCanonicalKey(a, b) {
42
43
  * Records are collected up front because the index has to precede the blocks it
43
44
  * describes.
44
45
  *
46
+ * Under `excludeValues` the CAR carries the two roots and no other block. The
47
+ * index still folds into the same set hash, so a syncer authenticates it against
48
+ * the commit and then fetches only the records it lacks.
49
+ *
45
50
  * @param {import('./commit.js').SignedCommit} commit
46
51
  * @param {Iterable<SerializedRecord>} records
52
+ * @param {{excludeValues?: boolean}} [opts]
47
53
  * @returns {Promise<Uint8Array>}
48
54
  */
49
- export async function serializeRepo(commit, records) {
55
+ export async function serializeRepo(commit, records, opts = {}) {
50
56
  /** @type {Map<string, SerializedRecord>} */
51
57
  const byPath = new Map();
52
58
  for (const record of records) {
@@ -71,10 +77,15 @@ export async function serializeRepo(commit, records) {
71
77
  const blocks = [
72
78
  { cid: commitRoot, data: commitBytes },
73
79
  { cid: indexRoot, data: indexBytes },
74
- ...paths.map((path) => {
75
- const record = /** @type {SerializedRecord} */ (byPath.get(path));
76
- return { cid: record.cid, data: record.value };
77
- }),
80
+ ...(opts.excludeValues
81
+ ? []
82
+ : paths.map((path) => {
83
+ const record = /** @type {SerializedRecord} */ (byPath.get(path));
84
+ return {
85
+ cid: record.cid,
86
+ data: /** @type {Uint8Array} */ (record.value),
87
+ };
88
+ })),
78
89
  ];
79
90
 
80
91
  return buildCarFile([commitRoot, indexRoot], blocks);
package/src/dpop.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The key an application asks a credential to be bound to, read from the proof
3
+ * it presents when it asks.
4
+ *
5
+ * A thumbprint named in the request body is a claim: any holder of the
6
+ * delegation token can make it about a key somebody else controls. A proof
7
+ * demonstrates possession of the key it names. The proof carries no `ath`,
8
+ * because a delegation token is an authorization grant rather than an access
9
+ * token, so there is no bound token to hash.
10
+ *
11
+ * @param {Request} request
12
+ * @returns {Promise<string|null>} the proof's thumbprint, or null when the
13
+ * request carries no proof
14
+ * @throws {SpaceTokenError} when a proof is present but invalid or replayed
15
+ */
16
+ export declare function credentialBindingFromProof(request: Request): Promise<string | null>;
17
+ /**
18
+ * Check the DPoP proof presented with a space credential.
19
+ *
20
+ * @param {Object} opts
21
+ * @param {Request} opts.request - the request the proof must cover
22
+ * @param {string} opts.credential - the raw credential, hashed into `ath`
23
+ * @param {string} opts.jkt - the thumbprint from the credential's `cnf.jkt`
24
+ * @returns {Promise<void>}
25
+ * @throws {SpaceTokenError} when the proof is absent, invalid, or replayed
26
+ */
27
+ export declare function verifyCredentialProof({ request, credential, jkt }: {
28
+ request: Request;
29
+ credential: string;
30
+ jkt: string;
31
+ }): Promise<void>;
package/src/dpop.js ADDED
@@ -0,0 +1,121 @@
1
+ // @pdsjs/spaces/dpop - the key a space credential is bound to (RFC 9449).
2
+ //
3
+ // One credential reads a whole space, and the holder presents it to every repo
4
+ // host in that space. A bearer credential would therefore be a shared secret: a
5
+ // host handed one to serve its own repo could replay it against every other host
6
+ // in the space. So the authority binds the credential to a key the application
7
+ // names as a thumbprint, and each request carries a fresh proof signed by that
8
+ // key.
9
+ //
10
+ // The proof itself is verified by @pdsjs/core/oauth, which does the same work
11
+ // for OAuth access tokens. What this module adds is the space rules: the proof
12
+ // is mandatory, and a jti is accepted once.
13
+
14
+ import { parseDpopProof } from '@pdsjs/core/oauth';
15
+ import { SpaceTokenError } from './token.js';
16
+
17
+ // parseDpopProof accepts an `iat` up to 300 seconds old, so a jti has to be
18
+ // remembered for longer than that. Otherwise a proof outlives the memory of it
19
+ // and the last few seconds of its life are replayable.
20
+ const JTI_RETENTION_MS = 360_000;
21
+
22
+ /** @type {Map<string, number>} jti -> the instant it may be forgotten */
23
+ const seenJtis = new Map();
24
+
25
+ /**
26
+ * Record a jti, answering whether this is the first time it was presented.
27
+ *
28
+ * Held in memory, which is sound for the same reason the write queue in
29
+ * writer.js is: one space is served by exactly one process, a Node PDS or a
30
+ * single Durable Object. Expired entries are dropped on each call, so the map
31
+ * holds one request's worth of traffic rather than growing without bound.
32
+ *
33
+ * @param {string} jti
34
+ * @param {number} now
35
+ * @returns {boolean}
36
+ */
37
+ function firstUse(jti, now) {
38
+ for (const [seen, expiresAt] of seenJtis) {
39
+ if (expiresAt <= now) seenJtis.delete(seen);
40
+ }
41
+ if (seenJtis.has(jti)) return false;
42
+ seenJtis.set(jti, now + JTI_RETENTION_MS);
43
+ return true;
44
+ }
45
+
46
+ /**
47
+ * The key an application asks a credential to be bound to, read from the proof
48
+ * it presents when it asks.
49
+ *
50
+ * A thumbprint named in the request body is a claim: any holder of the
51
+ * delegation token can make it about a key somebody else controls. A proof
52
+ * demonstrates possession of the key it names. The proof carries no `ath`,
53
+ * because a delegation token is an authorization grant rather than an access
54
+ * token, so there is no bound token to hash.
55
+ *
56
+ * @param {Request} request
57
+ * @returns {Promise<string|null>} the proof's thumbprint, or null when the
58
+ * request carries no proof
59
+ * @throws {SpaceTokenError} when a proof is present but invalid or replayed
60
+ */
61
+ export async function credentialBindingFromProof(request) {
62
+ const proof = request.headers.get('dpop');
63
+ if (!proof) return null;
64
+
65
+ /** @type {import('@pdsjs/core/oauth').DpopProofResult} */
66
+ let parsed;
67
+ try {
68
+ parsed = await parseDpopProof(proof, request.method, request.url);
69
+ } catch (err) {
70
+ throw new SpaceTokenError(
71
+ err instanceof Error ? err.message : String(err),
72
+ 'BadDpopProof',
73
+ );
74
+ }
75
+
76
+ if (!firstUse(parsed.jti, Date.now())) {
77
+ throw new SpaceTokenError('DPoP proof replayed', 'BadDpopProof');
78
+ }
79
+ return parsed.jkt;
80
+ }
81
+
82
+ /**
83
+ * Check the DPoP proof presented with a space credential.
84
+ *
85
+ * @param {Object} opts
86
+ * @param {Request} opts.request - the request the proof must cover
87
+ * @param {string} opts.credential - the raw credential, hashed into `ath`
88
+ * @param {string} opts.jkt - the thumbprint from the credential's `cnf.jkt`
89
+ * @returns {Promise<void>}
90
+ * @throws {SpaceTokenError} when the proof is absent, invalid, or replayed
91
+ */
92
+ export async function verifyCredentialProof({ request, credential, jkt }) {
93
+ const proof = request.headers.get('dpop');
94
+ if (!proof) {
95
+ throw new SpaceTokenError(
96
+ 'A space credential requires a DPoP proof',
97
+ 'MissingDpopProof',
98
+ );
99
+ }
100
+
101
+ /** @type {import('@pdsjs/core/oauth').DpopProofResult} */
102
+ let parsed;
103
+ try {
104
+ parsed = await parseDpopProof(
105
+ proof,
106
+ request.method,
107
+ request.url,
108
+ jkt,
109
+ credential,
110
+ );
111
+ } catch (err) {
112
+ throw new SpaceTokenError(
113
+ err instanceof Error ? err.message : String(err),
114
+ 'BadDpopProof',
115
+ );
116
+ }
117
+
118
+ if (!firstUse(parsed.jti, Date.now())) {
119
+ throw new SpaceTokenError('DPoP proof replayed', 'BadDpopProof');
120
+ }
121
+ }
@@ -2,8 +2,8 @@
2
2
  * @param {Object} ctx
3
3
  * @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
4
4
  * @param {() => Promise<string|null>} ctx.getDid
5
- * @param {() => Promise<{sign: (bytes: Uint8Array) => Promise<Uint8Array>}>} ctx.getSigner
6
- * @param {(did: string) => Promise<any>} ctx.resolveDid
5
+ * @param {() => Promise<import('../token.js').SpaceSigner>} ctx.getSigner
6
+ * @param {import('@pdsjs/core/ports').DidResolverPort} ctx.resolveDid
7
7
  * @param {import('@pdsjs/core/ports').SignatureVerifierPort} ctx.verifier
8
8
  * @param {typeof fetch} [ctx.fetch]
9
9
  * @returns {import('@pdsjs/core/pds').Routes}
@@ -11,30 +11,83 @@
11
11
  export declare function createAuthRoutes(ctx: {
12
12
  spaceStorage: import('@pdsjs/core/ports').SpaceStoragePort;
13
13
  getDid: () => Promise<string | null>;
14
- getSigner: () => Promise<{
15
- sign: (bytes: Uint8Array) => Promise<Uint8Array>;
16
- }>;
17
- resolveDid: (did: string) => Promise<any>;
14
+ getSigner: () => Promise<import('../token.js').SpaceSigner>;
15
+ resolveDid: import('@pdsjs/core/ports').DidResolverPort;
18
16
  verifier: import('@pdsjs/core/ports').SignatureVerifierPort;
19
17
  fetch?: typeof fetch;
20
18
  }): import('@pdsjs/core/pds').Routes;
21
19
  /**
22
- * Verify a space credential presented to a repo host.
20
+ * Build the read check the space endpoints share: the hosted account's own
21
+ * session, or a space credential for the space being read.
22
+ *
23
+ * @param {Object} ctx
24
+ * @param {() => Promise<string|null>} ctx.getDid
25
+ * @param {import('@pdsjs/core/ports').DidResolverPort} ctx.resolveDid
26
+ * @param {import('@pdsjs/core/ports').SignatureVerifierPort} ctx.verifier
27
+ * @returns {(request: Request, space: string, auth: {did: string}|null) => Promise<Response|null>}
28
+ * a Response to return, or null to proceed
29
+ */
30
+ export declare function createReadAuthorizer({ getDid, resolveDid, verifier }: {
31
+ getDid: () => Promise<string | null>;
32
+ resolveDid: import('@pdsjs/core/ports').DidResolverPort;
33
+ verifier: import('@pdsjs/core/ports').SignatureVerifierPort;
34
+ }): (request: Request, space: string, auth: {
35
+ did: string;
36
+ } | null) => Promise<Response | null>;
37
+ /**
38
+ * The space credential a request presents, or null.
39
+ *
40
+ * `DPoP`, never `Bearer`: a credential names the key it is bound to, and a
41
+ * presentation that proves nothing about that key is what the binding exists to
42
+ * refuse.
43
+ *
44
+ * @param {Request} request
45
+ * @returns {string|null}
46
+ */
47
+ export declare function credentialFromRequest(request: Request): string | null;
48
+ /**
49
+ * Check a presented space credential: the token itself, then the caller's
50
+ * possession of the key it names.
51
+ *
52
+ * The two questions are one function so that no endpoint accepting a credential
53
+ * can answer the first and forget the second.
23
54
  *
24
55
  * @param {Object} opts
56
+ * @param {Request} opts.request - the request the DPoP proof must cover
25
57
  * @param {string} opts.credential - the raw JWT
26
58
  * @param {string} opts.space - the space the request targets
27
- * @param {(did: string) => Promise<any>} opts.resolveDid
59
+ * @param {import('@pdsjs/core/ports').DidResolverPort} opts.resolveDid
28
60
  * @param {import('@pdsjs/core/ports').SignatureVerifierPort} opts.verifier
29
61
  * @returns {Promise<{iss: string}>}
30
62
  */
63
+ export declare function verifyPresentedCredential({ request, credential, space, resolveDid, verifier, }: {
64
+ request: Request;
65
+ credential: string;
66
+ space: string;
67
+ resolveDid: import('@pdsjs/core/ports').DidResolverPort;
68
+ verifier: import('@pdsjs/core/ports').SignatureVerifierPort;
69
+ }): Promise<{
70
+ iss: string;
71
+ }>;
72
+ /**
73
+ * Verify a space credential presented to a repo host.
74
+ *
75
+ * @param {Object} opts
76
+ * @param {string} opts.credential - the raw JWT
77
+ * @param {string} opts.space - the space the request targets
78
+ * @param {import('@pdsjs/core/ports').DidResolverPort} opts.resolveDid
79
+ * @param {import('@pdsjs/core/ports').SignatureVerifierPort} opts.verifier
80
+ * @returns {Promise<{iss: string, jkt: string}>} the authority, and the key the
81
+ * holder must prove possession of
82
+ */
31
83
  export declare function verifySpaceCredential({ credential, space, resolveDid, verifier, }: {
32
84
  credential: string;
33
85
  space: string;
34
- resolveDid: (did: string) => Promise<any>;
86
+ resolveDid: import('@pdsjs/core/ports').DidResolverPort;
35
87
  verifier: import('@pdsjs/core/ports').SignatureVerifierPort;
36
88
  }): Promise<{
37
89
  iss: string;
90
+ jkt: string;
38
91
  }>;
39
92
  /**
40
93
  * Ask a space's managing app whether to authorize a user.
@@ -48,8 +101,8 @@ export declare function verifySpaceCredential({ credential, space, resolveDid, v
48
101
  * @param {string} opts.space
49
102
  * @param {string} opts.userDid
50
103
  * @param {string|undefined} opts.clientId
51
- * @param {(did: string) => Promise<any>} opts.resolveDid
52
- * @param {() => Promise<{sign: (bytes: Uint8Array) => Promise<Uint8Array>}>} opts.getSigner
104
+ * @param {import('@pdsjs/core/ports').DidResolverPort} opts.resolveDid
105
+ * @param {() => Promise<import('../token.js').SpaceSigner>} opts.getSigner
53
106
  * @param {typeof fetch} [opts.fetch]
54
107
  * @returns {Promise<boolean>}
55
108
  */
@@ -59,9 +112,7 @@ export declare function checkUserAccess({ managingApp, authorityDid, space, user
59
112
  space: string;
60
113
  userDid: string;
61
114
  clientId: string | undefined;
62
- resolveDid: (did: string) => Promise<any>;
63
- getSigner: () => Promise<{
64
- sign: (bytes: Uint8Array) => Promise<Uint8Array>;
65
- }>;
115
+ resolveDid: import('@pdsjs/core/ports').DidResolverPort;
116
+ getSigner: () => Promise<import('../token.js').SpaceSigner>;
66
117
  fetch?: typeof fetch;
67
118
  }): Promise<boolean>;