@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.13 → 0.34.1-feature.SSISDK.17.bitstring.sl.14
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/dist/index.cjs +290 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -35
- package/dist/index.d.ts +67 -35
- package/dist/index.js +290 -161
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/functions.ts +44 -35
- package/src/impl/BitstringStatusListImplementation.ts +113 -66
- package/src/impl/IStatusList.ts +35 -7
- package/src/impl/OAuthStatusList.ts +77 -30
- package/src/impl/StatusList2021.ts +73 -34
- package/src/index.ts +1 -0
- package/src/types/index.ts +18 -25
- package/src/utils.ts +47 -18
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphereon/ssi-sdk.vc-status-list",
|
|
3
3
|
"description": "Sphereon SSI-SDK plugin for Status List management, like StatusList2021.",
|
|
4
|
-
"version": "0.34.1-feature.SSISDK.17.bitstring.sl.
|
|
4
|
+
"version": "0.34.1-feature.SSISDK.17.bitstring.sl.14+35c8ca99",
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"@sphereon/ssi-sdk-ext.did-utils": "0.29.0",
|
|
32
32
|
"@sphereon/ssi-sdk-ext.identifier-resolution": "0.29.0",
|
|
33
33
|
"@sphereon/ssi-sdk-ext.jwt-service": "0.29.0",
|
|
34
|
-
"@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.17.bitstring.sl.
|
|
35
|
-
"@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.17.bitstring.sl.
|
|
36
|
-
"@sphereon/ssi-types": "0.34.1-feature.SSISDK.17.bitstring.sl.
|
|
34
|
+
"@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.17.bitstring.sl.14+35c8ca99",
|
|
35
|
+
"@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.17.bitstring.sl.14+35c8ca99",
|
|
36
|
+
"@sphereon/ssi-types": "0.34.1-feature.SSISDK.17.bitstring.sl.14+35c8ca99",
|
|
37
37
|
"@sphereon/vc-status-list": "7.0.0-next.0",
|
|
38
38
|
"@veramo/core": "4.2.0",
|
|
39
39
|
"@veramo/credential-status": "4.2.0",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"SSI",
|
|
73
73
|
"StatusList2021"
|
|
74
74
|
],
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "35c8ca99b499927dfffd929ae709750a7337b017"
|
|
76
76
|
}
|
package/src/functions.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
2
|
-
import {
|
|
3
|
-
CredentialMapper,
|
|
4
|
-
type CredentialProofFormat,
|
|
5
|
-
type StatusListCredential,
|
|
6
|
-
StatusListDriverType,
|
|
7
|
-
StatusListType,
|
|
8
|
-
type StatusPurpose2021,
|
|
9
|
-
} from '@sphereon/ssi-types'
|
|
2
|
+
import { CredentialMapper, type CredentialProofFormat, type StatusListCredential, StatusListType, type StatusPurpose2021 } from '@sphereon/ssi-types'
|
|
10
3
|
import type { CredentialStatus, DIDDocument, IAgentContext, ProofFormat as VeramoProofFormat } from '@veramo/core'
|
|
11
4
|
|
|
12
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
BitstringStatusListEntryCredentialStatus,
|
|
7
|
+
IBitstringStatusListEntryEntity,
|
|
8
|
+
IStatusListEntryEntity,
|
|
9
|
+
StatusListEntity,
|
|
10
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
13
11
|
|
|
14
12
|
import { checkStatus } from '@sphereon/vc-status-list'
|
|
15
13
|
|
|
16
14
|
// @ts-ignore
|
|
17
15
|
import { CredentialJwtOrJSON, StatusMethod } from 'credential-status'
|
|
18
16
|
import {
|
|
19
|
-
BitstringStatus,
|
|
20
|
-
BitstringStatusListEntryCredentialStatus,
|
|
21
17
|
CreateNewStatusListFuncArgs,
|
|
18
|
+
IMergeDetailsWithEntityArgs,
|
|
19
|
+
IToDetailsFromCredentialArgs,
|
|
22
20
|
Status2021,
|
|
23
21
|
StatusList2021EntryCredentialStatus,
|
|
24
22
|
StatusList2021ToVerifiableCredentialArgs,
|
|
@@ -31,6 +29,12 @@ import {
|
|
|
31
29
|
import { assertValidProofType, determineStatusListType, getAssertedValue, getAssertedValues } from './utils'
|
|
32
30
|
import { getStatusListImplementation } from './impl/StatusListFactory'
|
|
33
31
|
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
32
|
+
import {
|
|
33
|
+
IBitstringStatusListImplementationResult,
|
|
34
|
+
IExtractedCredentialDetails,
|
|
35
|
+
IOAuthStatusListImplementationResult,
|
|
36
|
+
IStatusList2021ImplementationResult,
|
|
37
|
+
} from './impl/IStatusList'
|
|
34
38
|
|
|
35
39
|
export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<StatusListCredential> {
|
|
36
40
|
const url = getAssertedValue('statusListCredential', args.statusListCredential)
|
|
@@ -142,7 +146,7 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
|
|
|
142
146
|
type?: StatusListType | 'StatusList2021Entry'
|
|
143
147
|
id?: string
|
|
144
148
|
statusListIndex: string
|
|
145
|
-
}): Promise<number | Status2021 | StatusOAuth
|
|
149
|
+
}): Promise<number | Status2021 | StatusOAuth> {
|
|
146
150
|
return checkStatusIndexFromStatusListCredential({
|
|
147
151
|
...args,
|
|
148
152
|
statusListCredential: await fetchStatusListCredential(args),
|
|
@@ -156,7 +160,7 @@ export async function checkStatusIndexFromStatusListCredential(args: {
|
|
|
156
160
|
id?: string
|
|
157
161
|
statusListIndex: string | number
|
|
158
162
|
bitsPerStatus?: number
|
|
159
|
-
}): Promise<number | Status2021 | StatusOAuth
|
|
163
|
+
}): Promise<number | Status2021 | StatusOAuth> {
|
|
160
164
|
const statusListType: StatusListType = determineStatusListType(args.statusListCredential)
|
|
161
165
|
const implementation = getStatusListImplementation(statusListType)
|
|
162
166
|
return implementation.checkStatusIndex(args)
|
|
@@ -181,31 +185,36 @@ export async function updateStatusIndexFromStatusListCredential(
|
|
|
181
185
|
return implementation.updateStatusListIndex(args, context)
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
export async function
|
|
185
|
-
statusListType
|
|
186
|
-
correlationId,
|
|
187
|
-
driverType,
|
|
188
|
-
statusListCredential,
|
|
189
|
-
bitsPerStatus,
|
|
190
|
-
}: {
|
|
191
|
-
statusListType: StatusListType
|
|
192
|
-
statusListCredential: StatusListCredential
|
|
193
|
-
correlationId?: string
|
|
194
|
-
driverType?: StatusListDriverType
|
|
195
|
-
bitsPerStatus?: number
|
|
196
|
-
}): Promise<StatusListResult & Partial<IAddStatusListArgs>> {
|
|
197
|
-
const credential = getAssertedValue('statusListCredential', statusListCredential)
|
|
188
|
+
export async function extractCredentialDetails(statusListCredential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
189
|
+
const statusListType = determineStatusListType(statusListCredential)
|
|
198
190
|
const implementation = getStatusListImplementation(statusListType)
|
|
191
|
+
return implementation.extractCredentialDetails(statusListCredential)
|
|
192
|
+
}
|
|
199
193
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
correlationId: correlationId,
|
|
204
|
-
driverType: driverType,
|
|
205
|
-
bitsPerStatus: bitsPerStatus,
|
|
206
|
-
})
|
|
194
|
+
export async function toStatusListDetails(
|
|
195
|
+
args: IToDetailsFromCredentialArgs,
|
|
196
|
+
): Promise<StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)>
|
|
207
197
|
|
|
208
|
-
|
|
198
|
+
export async function toStatusListDetails(
|
|
199
|
+
args: IMergeDetailsWithEntityArgs,
|
|
200
|
+
): Promise<StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)>
|
|
201
|
+
|
|
202
|
+
export async function toStatusListDetails(
|
|
203
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
204
|
+
): Promise<
|
|
205
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
206
|
+
> {
|
|
207
|
+
if ('statusListCredential' in args) {
|
|
208
|
+
// CREATE/READ context
|
|
209
|
+
const statusListType = args.statusListType
|
|
210
|
+
const implementation = getStatusListImplementation(statusListType)
|
|
211
|
+
return implementation.toStatusListDetails(args)
|
|
212
|
+
} else {
|
|
213
|
+
// UPDATE context
|
|
214
|
+
const statusListType = args.statusListEntity.type
|
|
215
|
+
const implementation = getStatusListImplementation(statusListType)
|
|
216
|
+
return implementation.toStatusListDetails(args)
|
|
217
|
+
}
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
export async function createCredentialStatusFromStatusList(args: {
|
|
@@ -28,14 +28,13 @@ import {
|
|
|
28
28
|
StatusListType,
|
|
29
29
|
} from '@sphereon/ssi-types'
|
|
30
30
|
|
|
31
|
-
import type { IBitstringStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
31
|
+
import type { IBitstringStatusListImplementationResult, IExtractedCredentialDetails, IStatusList } from './IStatusList'
|
|
32
32
|
import {
|
|
33
|
-
BitstringStatus,
|
|
34
|
-
BitstringStatusListEntryCredentialStatus,
|
|
35
33
|
CheckStatusIndexArgs,
|
|
36
34
|
CreateStatusListArgs,
|
|
35
|
+
IMergeDetailsWithEntityArgs,
|
|
36
|
+
IToDetailsFromCredentialArgs,
|
|
37
37
|
StatusListResult,
|
|
38
|
-
ToStatusListDetailsArgs,
|
|
39
38
|
UpdateStatusListFromEncodedListArgs,
|
|
40
39
|
UpdateStatusListIndexArgs,
|
|
41
40
|
} from '../types'
|
|
@@ -48,11 +47,17 @@ import {
|
|
|
48
47
|
BitstringStatusPurpose,
|
|
49
48
|
createStatusListCredential,
|
|
50
49
|
} from '@4sure-tech/vc-bitstring-status-lists'
|
|
51
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
BitstringStatusListEntity,
|
|
52
|
+
BitstringStatusListEntryCredentialStatus,
|
|
53
|
+
IBitstringStatusListEntryEntity,
|
|
54
|
+
IStatusListEntryEntity,
|
|
55
|
+
StatusListEntity,
|
|
56
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
52
57
|
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
53
58
|
|
|
54
59
|
export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
|
|
55
|
-
export const DEFAULT_PROOF_FORMAT = '
|
|
60
|
+
export const DEFAULT_PROOF_FORMAT = 'vc+jwt' as CredentialProofFormat
|
|
56
61
|
export const DEFAULT_STATUS_PURPOSE: BitstringStatusPurpose = 'revocation'
|
|
57
62
|
|
|
58
63
|
/**
|
|
@@ -148,9 +153,10 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
148
153
|
|
|
149
154
|
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
150
155
|
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus })
|
|
151
|
-
|
|
156
|
+
const bitstringStatusId = args.value as number
|
|
157
|
+
statusList.setStatus(index, bitstringStatusId)
|
|
152
158
|
|
|
153
|
-
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
159
|
+
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
154
160
|
|
|
155
161
|
const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
156
162
|
|
|
@@ -275,7 +281,7 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
275
281
|
* @param args - Check parameters including the status list credential and index
|
|
276
282
|
* @returns Promise resolving to the status value at the specified index
|
|
277
283
|
*/
|
|
278
|
-
async checkStatusIndex(args: CheckStatusIndexArgs): Promise<
|
|
284
|
+
async checkStatusIndex(args: CheckStatusIndexArgs): Promise<number> {
|
|
279
285
|
if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
|
|
280
286
|
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (checkStatusIndex)'))
|
|
281
287
|
}
|
|
@@ -292,62 +298,111 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
292
298
|
return statusList.getStatus(numIndex)
|
|
293
299
|
}
|
|
294
300
|
|
|
301
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
302
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
303
|
+
const { issuer, credentialSubject } = uniform
|
|
304
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
id: getAssertedValue('id', uniform.id),
|
|
308
|
+
issuer,
|
|
309
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
295
313
|
/**
|
|
296
314
|
* Converts a status list credential payload to detailed status list information
|
|
297
315
|
*
|
|
298
316
|
* @param args - Conversion parameters including the status list payload
|
|
299
317
|
* @returns Promise resolving to detailed status list information
|
|
300
318
|
*/
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
proofFormat
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// Legacy nested structure for backward compatibility
|
|
340
|
-
bitstringStatusList: {
|
|
319
|
+
// For CREATE and READ contexts
|
|
320
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
321
|
+
// For UPDATE contexts
|
|
322
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
323
|
+
async toStatusListDetails(
|
|
324
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
325
|
+
): Promise<StatusListResult & IBitstringStatusListImplementationResult> {
|
|
326
|
+
if ('statusListCredential' in args) {
|
|
327
|
+
// CREATE/READ context
|
|
328
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
329
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
330
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher'))
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
334
|
+
const { issuer, credentialSubject } = uniform
|
|
335
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
336
|
+
|
|
337
|
+
const id = getAssertedValue('id', uniform.id)
|
|
338
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
339
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
340
|
+
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
341
|
+
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
342
|
+
const ttl = subject.ttl
|
|
343
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
344
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus)
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
id,
|
|
348
|
+
encodedList,
|
|
349
|
+
issuer,
|
|
350
|
+
type: StatusListType.BitstringStatusList,
|
|
351
|
+
proofFormat,
|
|
352
|
+
length: statuslistLength,
|
|
353
|
+
statusListCredential,
|
|
354
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
355
|
+
correlationId,
|
|
356
|
+
driverType,
|
|
341
357
|
statusPurpose,
|
|
342
358
|
bitsPerStatus,
|
|
343
359
|
...(validFrom && { validFrom }),
|
|
344
360
|
...(validUntil && { validUntil }),
|
|
345
361
|
...(ttl && { ttl }),
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
362
|
+
bitstringStatusList: {
|
|
363
|
+
statusPurpose,
|
|
364
|
+
bitsPerStatus,
|
|
365
|
+
...(validFrom && { validFrom }),
|
|
366
|
+
...(validUntil && { validUntil }),
|
|
367
|
+
...(ttl && { ttl }),
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
// UPDATE context
|
|
372
|
+
const { extractedDetails, statusListEntity } = args
|
|
373
|
+
const bitstringEntity = statusListEntity as BitstringStatusListEntity
|
|
374
|
+
if (!bitstringEntity.bitsPerStatus) {
|
|
375
|
+
return Promise.reject(Error('bitsPerStatus must be present for a bitstring status list'))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const proofFormat: CredentialProofFormat =
|
|
379
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
380
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(extractedDetails.encodedList, bitstringEntity.bitsPerStatus)
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
id: extractedDetails.id,
|
|
384
|
+
encodedList: extractedDetails.encodedList,
|
|
385
|
+
issuer: extractedDetails.issuer,
|
|
386
|
+
type: StatusListType.BitstringStatusList,
|
|
387
|
+
proofFormat,
|
|
388
|
+
length: statuslistLength,
|
|
389
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
390
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
391
|
+
correlationId: statusListEntity.correlationId,
|
|
392
|
+
driverType: statusListEntity.driverType,
|
|
393
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
394
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
395
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
396
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
397
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
398
|
+
bitstringStatusList: {
|
|
399
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
400
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
401
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
402
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
403
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
404
|
+
},
|
|
405
|
+
}
|
|
351
406
|
}
|
|
352
407
|
}
|
|
353
408
|
|
|
@@ -364,25 +419,17 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
364
419
|
}): Promise<BitstringStatusListEntryCredentialStatus> {
|
|
365
420
|
const { statusList, statusListEntry, statusListIndex } = args
|
|
366
421
|
|
|
367
|
-
// Type guard to ensure we have a bitstring entry
|
|
368
|
-
const isBitstringEntry = (entry: IStatusListEntryEntity | IBitstringStatusListEntryEntity): entry is IBitstringStatusListEntryEntity => {
|
|
369
|
-
return 'statusPurpose' in entry
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (!isBitstringEntry(statusListEntry)) {
|
|
373
|
-
throw new Error('Expected bitstring status list entry for bitstring status list')
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Cast to BitstringStatusListEntity to access specific properties
|
|
377
422
|
const bitstringStatusList = statusList as BitstringStatusListEntity
|
|
378
|
-
|
|
423
|
+
const bitstringStatusListEntry = statusListEntry as IBitstringStatusListEntryEntity
|
|
379
424
|
return {
|
|
380
425
|
id: `${statusList.id}#${statusListIndex}`,
|
|
381
426
|
type: 'BitstringStatusListEntry',
|
|
382
|
-
statusPurpose:
|
|
427
|
+
statusPurpose: bitstringStatusListEntry.statusPurpose,
|
|
383
428
|
statusListIndex: '' + statusListIndex,
|
|
384
429
|
statusListCredential: statusList.id,
|
|
385
430
|
bitsPerStatus: bitstringStatusList.bitsPerStatus,
|
|
431
|
+
statusMessage: bitstringStatusListEntry.statusMessage,
|
|
432
|
+
statusReference: bitstringStatusListEntry.statusReference,
|
|
386
433
|
} satisfies BitstringStatusListEntryCredentialStatus
|
|
387
434
|
}
|
|
388
435
|
|
package/src/impl/IStatusList.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { IAgentContext } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
3
|
import {
|
|
4
|
-
BitstringStatus,
|
|
5
|
-
BitstringStatusListEntryCredentialStatus,
|
|
6
4
|
CheckStatusIndexArgs,
|
|
7
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
8
8
|
Status2021,
|
|
9
9
|
StatusList2021EntryCredentialStatus,
|
|
10
10
|
StatusListOAuthEntryCredentialStatus,
|
|
11
11
|
StatusListResult,
|
|
12
12
|
StatusOAuth,
|
|
13
|
-
ToStatusListDetailsArgs,
|
|
14
13
|
UpdateStatusListFromEncodedListArgs,
|
|
15
14
|
UpdateStatusListIndexArgs,
|
|
16
15
|
} from '../types'
|
|
@@ -24,8 +23,21 @@ import {
|
|
|
24
23
|
StatusListType,
|
|
25
24
|
StatusPurpose2021,
|
|
26
25
|
} from '@sphereon/ssi-types'
|
|
27
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
BitstringStatusListEntryCredentialStatus,
|
|
28
|
+
IBitstringStatusListEntryEntity,
|
|
29
|
+
IStatusListEntryEntity,
|
|
30
|
+
StatusListEntity,
|
|
31
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
28
32
|
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
33
|
+
import { DecodedStatusListPayload } from './encoding/common'
|
|
34
|
+
|
|
35
|
+
export interface IExtractedCredentialDetails {
|
|
36
|
+
id: string
|
|
37
|
+
issuer: string | IIssuer
|
|
38
|
+
encodedList: string
|
|
39
|
+
decodedPayload?: DecodedStatusListPayload
|
|
40
|
+
}
|
|
29
41
|
|
|
30
42
|
export interface IStatusList {
|
|
31
43
|
/**
|
|
@@ -52,13 +64,29 @@ export interface IStatusList {
|
|
|
52
64
|
/**
|
|
53
65
|
* Checks the status at a given index in the status list
|
|
54
66
|
*/
|
|
55
|
-
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth
|
|
67
|
+
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
71
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
72
|
+
* It extracts all details available from the credential payload itself.
|
|
73
|
+
*/
|
|
74
|
+
extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts a credential and its known metadata into a full StatusListResult.
|
|
78
|
+
*/
|
|
79
|
+
toStatusListDetails(
|
|
80
|
+
args: IToDetailsFromCredentialArgs,
|
|
81
|
+
): Promise<
|
|
82
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
83
|
+
>
|
|
56
84
|
|
|
57
85
|
/**
|
|
58
|
-
*
|
|
86
|
+
* Merges pre-parsed details from a new credential with an existing database entity.
|
|
59
87
|
*/
|
|
60
88
|
toStatusListDetails(
|
|
61
|
-
args:
|
|
89
|
+
args: IMergeDetailsWithEntityArgs,
|
|
62
90
|
): Promise<
|
|
63
91
|
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
64
92
|
>
|
|
@@ -3,16 +3,17 @@ import { type CompactJWT, type CredentialProofFormat, type CWT, StatusListType }
|
|
|
3
3
|
import type {
|
|
4
4
|
CheckStatusIndexArgs,
|
|
5
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
6
8
|
SignedStatusListData,
|
|
7
9
|
StatusListOAuthEntryCredentialStatus,
|
|
8
10
|
StatusListResult,
|
|
9
11
|
StatusOAuth,
|
|
10
|
-
ToStatusListDetailsArgs,
|
|
11
12
|
UpdateStatusListFromEncodedListArgs,
|
|
12
13
|
UpdateStatusListIndexArgs,
|
|
13
14
|
} from '../types'
|
|
14
15
|
import { determineProofFormat, ensureDate, getAssertedValue, getAssertedValues } from '../utils'
|
|
15
|
-
import type { IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
16
|
+
import type { IExtractedCredentialDetails, IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
16
17
|
import { StatusList } from '@sd-jwt/jwt-status-list'
|
|
17
18
|
import type { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
|
|
18
19
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
@@ -164,41 +165,87 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
164
165
|
return statusList.getStatus(index)
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
async
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const { statusList, issuer, id, exp } = decoded
|
|
168
|
+
async extractCredentialDetails(credential: CompactJWT | CWT): Promise<IExtractedCredentialDetails> {
|
|
169
|
+
if (typeof credential !== 'string') {
|
|
170
|
+
return Promise.reject('statusListCredential must be a JWT or CWT string')
|
|
171
|
+
}
|
|
172
172
|
|
|
173
|
-
const
|
|
174
|
-
const
|
|
173
|
+
const proofFormat = determineProofFormat(credential)
|
|
174
|
+
const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(credential) : decodeStatusListCWT(credential)
|
|
175
175
|
|
|
176
176
|
return {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
encodedList: statusList.compressStatusList(),
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
length: statusList.statusList.length,
|
|
184
|
-
statusListCredential: statusListPayload,
|
|
185
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
186
|
-
correlationId: args.correlationId, // FIXME these do not need to be inside the impl
|
|
187
|
-
driverType: args.driverType, // FIXME these do not need to be inside the impl
|
|
177
|
+
id: decoded.id,
|
|
178
|
+
issuer: decoded.issuer,
|
|
179
|
+
encodedList: decoded.statusList.compressStatusList(),
|
|
180
|
+
decodedPayload: decoded,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
188
183
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
// For CREATE and READ contexts
|
|
185
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
186
|
+
// For UPDATE contexts
|
|
187
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
188
|
+
async toStatusListDetails(
|
|
189
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
190
|
+
): Promise<StatusListResult & IOAuthStatusListImplementationResult> {
|
|
191
|
+
if ('statusListCredential' in args) {
|
|
192
|
+
// CREATE/READ context
|
|
193
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
194
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
195
|
+
return Promise.reject(Error('bitsPerStatus must be set for OAuth status lists and must be 1 or higher'))
|
|
196
|
+
}
|
|
192
197
|
|
|
193
|
-
|
|
194
|
-
|
|
198
|
+
const proofFormat = determineProofFormat(statusListCredential as string)
|
|
199
|
+
const decoded =
|
|
200
|
+
proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential as string) : decodeStatusListCWT(statusListCredential as string)
|
|
201
|
+
const { statusList, issuer, id, exp } = decoded
|
|
202
|
+
const expiresAt = exp ? new Date(exp * 1000) : undefined
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
id,
|
|
206
|
+
encodedList: statusList.compressStatusList(),
|
|
207
|
+
issuer,
|
|
208
|
+
type: StatusListType.OAuthStatusList,
|
|
209
|
+
proofFormat,
|
|
210
|
+
length: statusList.statusList.length,
|
|
211
|
+
statusListCredential: statusListCredential as CompactJWT | CWT,
|
|
212
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
213
|
+
correlationId,
|
|
214
|
+
driverType,
|
|
195
215
|
bitsPerStatus,
|
|
196
216
|
...(expiresAt && { expiresAt }),
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
217
|
+
oauthStatusList: {
|
|
218
|
+
bitsPerStatus,
|
|
219
|
+
...(expiresAt && { expiresAt }),
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// UPDATE context
|
|
224
|
+
const { extractedDetails, statusListEntity } = args
|
|
225
|
+
const oauthEntity = statusListEntity as OAuthStatusListEntity
|
|
226
|
+
const decoded = extractedDetails.decodedPayload as { statusList: StatusList; exp?: number }
|
|
227
|
+
|
|
228
|
+
const proofFormat = determineProofFormat(statusListEntity.statusListCredential as string)
|
|
229
|
+
const expiresAt = decoded.exp ? new Date(decoded.exp * 1000) : undefined
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
id: extractedDetails.id,
|
|
233
|
+
encodedList: extractedDetails.encodedList,
|
|
234
|
+
issuer: extractedDetails.issuer,
|
|
235
|
+
type: StatusListType.OAuthStatusList,
|
|
236
|
+
proofFormat,
|
|
237
|
+
length: decoded.statusList.statusList.length,
|
|
238
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
239
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
240
|
+
correlationId: statusListEntity.correlationId,
|
|
241
|
+
driverType: statusListEntity.driverType,
|
|
242
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
243
|
+
...(expiresAt && { expiresAt }),
|
|
244
|
+
oauthStatusList: {
|
|
245
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
246
|
+
...(expiresAt && { expiresAt }),
|
|
247
|
+
},
|
|
248
|
+
}
|
|
202
249
|
}
|
|
203
250
|
}
|
|
204
251
|
|