@sphereon/ssi-sdk.vc-status-list 0.34.1-next.3 → 0.34.1-next.40
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 +692 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -37
- package/dist/index.d.ts +163 -37
- package/dist/index.js +693 -120
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/src/functions.ts +126 -47
- package/src/impl/BitstringStatusListImplementation.ts +496 -0
- package/src/impl/IStatusList.ts +102 -8
- package/src/impl/OAuthStatusList.ts +133 -38
- package/src/impl/StatusList2021.ts +114 -34
- package/src/impl/StatusListFactory.ts +2 -0
- package/src/index.ts +1 -0
- package/src/types/BitstringStatusList.ts +4 -0
- package/src/types/index.ts +56 -35
- package/src/utils.ts +82 -20
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
import type { IAgentContext,
|
|
2
|
-
import { type CompactJWT, type
|
|
1
|
+
import type { IAgentContext, IKeyManager } from '@veramo/core'
|
|
2
|
+
import { type CompactJWT, type CredentialProofFormat, type CWT, StatusListType } from '@sphereon/ssi-types'
|
|
3
3
|
import type {
|
|
4
4
|
CheckStatusIndexArgs,
|
|
5
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
6
8
|
SignedStatusListData,
|
|
9
|
+
StatusListOAuthEntryCredentialStatus,
|
|
7
10
|
StatusListResult,
|
|
8
11
|
StatusOAuth,
|
|
9
|
-
ToStatusListDetailsArgs,
|
|
10
12
|
UpdateStatusListFromEncodedListArgs,
|
|
11
13
|
UpdateStatusListIndexArgs,
|
|
12
14
|
} from '../types'
|
|
13
|
-
import { determineProofFormat, getAssertedValue, getAssertedValues } from '../utils'
|
|
14
|
-
import type { IStatusList } from './IStatusList'
|
|
15
|
+
import { determineProofFormat, ensureDate, getAssertedValue, getAssertedValues } from '../utils'
|
|
16
|
+
import type { IExtractedCredentialDetails, IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
15
17
|
import { StatusList } from '@sd-jwt/jwt-status-list'
|
|
16
18
|
import type { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
|
|
17
19
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
18
20
|
import { createSignedJwt, decodeStatusListJWT } from './encoding/jwt'
|
|
19
21
|
import { createSignedCbor, decodeStatusListCWT } from './encoding/cbor'
|
|
22
|
+
import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, OAuthStatusListEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
23
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
20
24
|
|
|
21
|
-
type IRequiredContext = IAgentContext<
|
|
25
|
+
type IRequiredContext = IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>
|
|
22
26
|
|
|
23
|
-
export const DEFAULT_BITS_PER_STATUS = 1 // 1 bit is sufficient for 0x00 - "VALID"
|
|
27
|
+
export const DEFAULT_BITS_PER_STATUS = 1 // 1 bit is sufficient for 0x00 - "VALID" 0x01 - "INVALID" saving space in the process
|
|
24
28
|
export const DEFAULT_LIST_LENGTH = 250000
|
|
25
29
|
export const DEFAULT_PROOF_FORMAT = 'jwt' as CredentialProofFormat
|
|
26
30
|
|
|
@@ -32,7 +36,8 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
32
36
|
|
|
33
37
|
const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
34
38
|
const { issuer, id, oauthStatusList, keyRef } = args
|
|
35
|
-
const { bitsPerStatus
|
|
39
|
+
const { bitsPerStatus } = oauthStatusList
|
|
40
|
+
const expiresAt = ensureDate(oauthStatusList.expiresAt)
|
|
36
41
|
const length = args.length ?? DEFAULT_LIST_LENGTH
|
|
37
42
|
const issuerString = typeof issuer === 'string' ? issuer : issuer.id
|
|
38
43
|
const correlationId = getAssertedValue('correlationId', args.correlationId)
|
|
@@ -56,7 +61,8 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
async updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IRequiredContext): Promise<StatusListResult> {
|
|
59
|
-
const { statusListCredential, value,
|
|
64
|
+
const { statusListCredential, value, keyRef } = args
|
|
65
|
+
const expiresAt = ensureDate(args.expiresAt)
|
|
60
66
|
if (typeof statusListCredential !== 'string') {
|
|
61
67
|
return Promise.reject('statusListCredential in neither JWT nor CWT')
|
|
62
68
|
}
|
|
@@ -70,6 +76,10 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
70
76
|
throw new Error('Status list index out of bounds')
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
if (typeof value !== 'number') {
|
|
80
|
+
throw new Error('Status list values should be of type number')
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
statusList.setStatus(index, value)
|
|
74
84
|
const { statusListCredential: signedCredential, encodedList } = await this.createSignedStatusList(
|
|
75
85
|
proofFormat,
|
|
@@ -102,15 +112,15 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
102
112
|
throw new Error('OAuthStatusList options are required for type OAuthStatusList')
|
|
103
113
|
}
|
|
104
114
|
const { proofFormat, oauthStatusList, keyRef } = args
|
|
105
|
-
const { bitsPerStatus
|
|
115
|
+
const { bitsPerStatus } = oauthStatusList
|
|
116
|
+
const expiresAt = ensureDate(oauthStatusList.expiresAt)
|
|
106
117
|
|
|
107
118
|
const { issuer, id } = getAssertedValues(args)
|
|
108
119
|
const issuerString = typeof issuer === 'string' ? issuer : issuer.id
|
|
109
120
|
|
|
110
121
|
const listToUpdate = StatusList.decompressStatusList(args.encodedList, bitsPerStatus ?? DEFAULT_BITS_PER_STATUS)
|
|
111
122
|
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
112
|
-
|
|
113
|
-
listToUpdate.setStatus(index, args.value ? 1 : 0)
|
|
123
|
+
listToUpdate.setStatus(index, args.value)
|
|
114
124
|
|
|
115
125
|
const { statusListCredential, encodedList } = await this.createSignedStatusList(
|
|
116
126
|
proofFormat ?? DEFAULT_PROOF_FORMAT,
|
|
@@ -138,10 +148,6 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
138
148
|
}
|
|
139
149
|
}
|
|
140
150
|
|
|
141
|
-
private buildContentType(proofFormat: 'jwt' | 'lds' | 'EthereumEip712Signature2021' | 'cbor' | undefined) {
|
|
142
|
-
return `application/statuslist+${proofFormat === 'cbor' ? 'cwt' : 'jwt'}`
|
|
143
|
-
}
|
|
144
|
-
|
|
145
151
|
async checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | StatusOAuth> {
|
|
146
152
|
const { statusListCredential, statusListIndex } = args
|
|
147
153
|
if (typeof statusListCredential !== 'string') {
|
|
@@ -153,39 +159,128 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
153
159
|
|
|
154
160
|
const index = typeof statusListIndex === 'number' ? statusListIndex : parseInt(statusListIndex)
|
|
155
161
|
if (index < 0 || index >= statusList.statusList.length) {
|
|
156
|
-
throw new Error(
|
|
162
|
+
throw new Error(`Status list index out of bounds, has ${statusList.statusList.length} items, requested ${index}`)
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
return statusList.getStatus(index)
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
170
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
171
|
+
* It extracts all details available from the credential payload itself.
|
|
172
|
+
*/
|
|
173
|
+
async extractCredentialDetails(credential: CompactJWT | CWT): Promise<IExtractedCredentialDetails> {
|
|
174
|
+
if (typeof credential !== 'string') {
|
|
175
|
+
return Promise.reject('statusListCredential must be a JWT or CWT string')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const proofFormat = determineProofFormat(credential)
|
|
179
|
+
const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(credential) : decodeStatusListCWT(credential)
|
|
167
180
|
|
|
168
181
|
return {
|
|
169
|
-
id,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
proofFormat,
|
|
174
|
-
length: statusList.statusList.length,
|
|
175
|
-
statusListCredential: statusListPayload,
|
|
176
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
177
|
-
oauthStatusList: {
|
|
178
|
-
bitsPerStatus: statusList.getBitsPerStatus(),
|
|
179
|
-
...(exp && { expiresAt: new Date(exp * 1000) }),
|
|
180
|
-
},
|
|
181
|
-
...(args.correlationId && { correlationId: args.correlationId }),
|
|
182
|
-
...(args.driverType && { driverType: args.driverType }),
|
|
182
|
+
id: decoded.id,
|
|
183
|
+
issuer: decoded.issuer,
|
|
184
|
+
encodedList: decoded.statusList.compressStatusList(),
|
|
185
|
+
decodedPayload: decoded,
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
// For CREATE and READ contexts
|
|
190
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
191
|
+
// For UPDATE contexts
|
|
192
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
193
|
+
async toStatusListDetails(
|
|
194
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
195
|
+
): Promise<StatusListResult & IOAuthStatusListImplementationResult> {
|
|
196
|
+
if ('statusListCredential' in args) {
|
|
197
|
+
// CREATE/READ context
|
|
198
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
199
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
200
|
+
return Promise.reject(Error('bitsPerStatus must be set for OAuth status lists and must be 1 or higher'))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const proofFormat = determineProofFormat(statusListCredential as string)
|
|
204
|
+
const decoded =
|
|
205
|
+
proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential as string) : decodeStatusListCWT(statusListCredential as string)
|
|
206
|
+
const { statusList, issuer, id, exp } = decoded
|
|
207
|
+
const expiresAt = exp ? new Date(exp * 1000) : undefined
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
id,
|
|
211
|
+
encodedList: statusList.compressStatusList(),
|
|
212
|
+
issuer,
|
|
213
|
+
type: StatusListType.OAuthStatusList,
|
|
214
|
+
proofFormat,
|
|
215
|
+
length: statusList.statusList.length,
|
|
216
|
+
statusListCredential: statusListCredential as CompactJWT | CWT,
|
|
217
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
218
|
+
correlationId,
|
|
219
|
+
driverType,
|
|
220
|
+
bitsPerStatus,
|
|
221
|
+
...(expiresAt && { expiresAt }),
|
|
222
|
+
oauthStatusList: {
|
|
223
|
+
bitsPerStatus,
|
|
224
|
+
...(expiresAt && { expiresAt }),
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
// UPDATE context
|
|
229
|
+
const { extractedDetails, statusListEntity } = args
|
|
230
|
+
const oauthEntity = statusListEntity as OAuthStatusListEntity
|
|
231
|
+
const decoded = extractedDetails.decodedPayload as { statusList: StatusList; exp?: number }
|
|
232
|
+
|
|
233
|
+
const proofFormat = determineProofFormat(statusListEntity.statusListCredential as string)
|
|
234
|
+
const expiresAt = decoded.exp ? new Date(decoded.exp * 1000) : undefined
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
id: extractedDetails.id,
|
|
238
|
+
encodedList: extractedDetails.encodedList,
|
|
239
|
+
issuer: extractedDetails.issuer,
|
|
240
|
+
type: StatusListType.OAuthStatusList,
|
|
241
|
+
proofFormat,
|
|
242
|
+
length: decoded.statusList.statusList.length,
|
|
243
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
244
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
245
|
+
correlationId: statusListEntity.correlationId,
|
|
246
|
+
driverType: statusListEntity.driverType,
|
|
247
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
248
|
+
...(expiresAt && { expiresAt }),
|
|
249
|
+
oauthStatusList: {
|
|
250
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
251
|
+
...(expiresAt && { expiresAt }),
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async createCredentialStatus(args: {
|
|
258
|
+
statusList: StatusListEntity
|
|
259
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
260
|
+
statusListIndex: number
|
|
261
|
+
}): Promise<StatusListOAuthEntryCredentialStatus> {
|
|
262
|
+
const { statusList, statusListIndex } = args
|
|
263
|
+
|
|
264
|
+
// Cast to OAuthStatusListEntity to access specific properties
|
|
265
|
+
const oauthStatusList = statusList as OAuthStatusListEntity
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
id: `${statusList.id}#${statusListIndex}`,
|
|
269
|
+
type: 'OAuthStatusListEntry',
|
|
270
|
+
bitsPerStatus: oauthStatusList.bitsPerStatus,
|
|
271
|
+
statusListIndex: '' + statusListIndex,
|
|
272
|
+
statusListCredential: statusList.id,
|
|
273
|
+
expiresAt: oauthStatusList.expiresAt,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private buildContentType(proofFormat: CredentialProofFormat | undefined) {
|
|
278
|
+
return `application/statuslist+${proofFormat === 'cbor' ? 'cwt' : 'jwt'}`
|
|
279
|
+
}
|
|
280
|
+
|
|
186
281
|
private async createSignedStatusList(
|
|
187
|
-
proofFormat:
|
|
188
|
-
context: IAgentContext<
|
|
282
|
+
proofFormat: CredentialProofFormat,
|
|
283
|
+
context: IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>,
|
|
189
284
|
statusList: StatusList,
|
|
190
285
|
issuerString: string,
|
|
191
286
|
id: string,
|
|
@@ -1,35 +1,37 @@
|
|
|
1
|
-
import type { IAgentContext,
|
|
1
|
+
import type { IAgentContext, ProofFormat as VeramoProofFormat } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
3
|
import {
|
|
4
4
|
CredentialMapper,
|
|
5
|
+
type CredentialProofFormat,
|
|
5
6
|
DocumentFormat,
|
|
6
7
|
type IIssuer,
|
|
7
|
-
type CredentialProofFormat,
|
|
8
8
|
type StatusListCredential,
|
|
9
9
|
StatusListType,
|
|
10
10
|
} from '@sphereon/ssi-types'
|
|
11
11
|
|
|
12
12
|
import { StatusList } from '@sphereon/vc-status-list'
|
|
13
|
-
import type { IStatusList } from './IStatusList'
|
|
13
|
+
import type { IExtractedCredentialDetails, IStatusList, IStatusList2021ImplementationResult } from './IStatusList'
|
|
14
14
|
import type {
|
|
15
15
|
CheckStatusIndexArgs,
|
|
16
16
|
CreateStatusListArgs,
|
|
17
|
+
IMergeDetailsWithEntityArgs,
|
|
18
|
+
IToDetailsFromCredentialArgs,
|
|
17
19
|
StatusListResult,
|
|
18
|
-
ToStatusListDetailsArgs,
|
|
19
20
|
UpdateStatusListFromEncodedListArgs,
|
|
20
21
|
UpdateStatusListIndexArgs,
|
|
21
22
|
} from '../types'
|
|
22
|
-
|
|
23
|
-
import { Status2021 } from '../types'
|
|
23
|
+
import { Status2021, StatusList2021EntryCredentialStatus } from '../types'
|
|
24
24
|
import { assertValidProofType, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
|
|
25
|
+
import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusList2021Entity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
26
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
25
27
|
|
|
26
28
|
export const DEFAULT_LIST_LENGTH = 250000
|
|
27
|
-
export const DEFAULT_PROOF_FORMAT = 'lds' as
|
|
29
|
+
export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
|
|
28
30
|
|
|
29
31
|
export class StatusList2021Implementation implements IStatusList {
|
|
30
32
|
async createNewStatusList(
|
|
31
33
|
args: CreateStatusListArgs,
|
|
32
|
-
context: IAgentContext<
|
|
34
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
33
35
|
): Promise<StatusListResult> {
|
|
34
36
|
const length = args?.length ?? DEFAULT_LIST_LENGTH
|
|
35
37
|
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
@@ -71,7 +73,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
71
73
|
|
|
72
74
|
async updateStatusListIndex(
|
|
73
75
|
args: UpdateStatusListIndexArgs,
|
|
74
|
-
context: IAgentContext<
|
|
76
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
75
77
|
): Promise<StatusListResult> {
|
|
76
78
|
const credential = args.statusListCredential
|
|
77
79
|
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
@@ -96,11 +98,15 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
96
98
|
context,
|
|
97
99
|
)
|
|
98
100
|
|
|
101
|
+
if (!('statusPurpose' in credentialSubject)) {
|
|
102
|
+
return Promise.reject(Error('statusPurpose is required in credentialSubject for StatusList2021'))
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
return {
|
|
100
106
|
statusListCredential: updatedCredential,
|
|
101
107
|
encodedList,
|
|
102
108
|
statusList2021: {
|
|
103
|
-
|
|
109
|
+
statusPurpose: credentialSubject.statusPurpose,
|
|
104
110
|
indexingDirection: 'rightToLeft',
|
|
105
111
|
},
|
|
106
112
|
length: statusList.length - 1,
|
|
@@ -114,7 +120,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
114
120
|
|
|
115
121
|
async updateStatusListFromEncodedList(
|
|
116
122
|
args: UpdateStatusListFromEncodedListArgs,
|
|
117
|
-
context: IAgentContext<
|
|
123
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
118
124
|
): Promise<StatusListResult> {
|
|
119
125
|
if (!args.statusList2021) {
|
|
120
126
|
throw new Error('statusList2021 options required for type StatusList2021')
|
|
@@ -126,7 +132,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
126
132
|
const { issuer, id } = getAssertedValues(args)
|
|
127
133
|
const statusList = await StatusList.decode({ encodedList: args.encodedList })
|
|
128
134
|
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
129
|
-
statusList.setStatus(index, args.value)
|
|
135
|
+
statusList.setStatus(index, args.value !== 0)
|
|
130
136
|
|
|
131
137
|
const newEncodedList = await statusList.encode()
|
|
132
138
|
const credential = await this.createVerifiableCredential(
|
|
@@ -166,32 +172,106 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
166
172
|
return status ? Status2021.Invalid : Status2021.Valid
|
|
167
173
|
}
|
|
168
174
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
177
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
178
|
+
* It extracts all details available from the credential payload itself.
|
|
179
|
+
*/
|
|
180
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
181
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
172
182
|
const { issuer, credentialSubject } = uniform
|
|
173
|
-
const
|
|
174
|
-
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
175
|
-
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
176
|
-
|
|
177
|
-
const statusPurpose = getAssertedProperty('statusPurpose', credentialSubject)
|
|
178
|
-
const list = await StatusList.decode({ encodedList })
|
|
183
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
179
184
|
|
|
180
185
|
return {
|
|
181
|
-
id,
|
|
182
|
-
encodedList,
|
|
186
|
+
id: getAssertedValue('id', uniform.id),
|
|
183
187
|
issuer,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
193
|
+
// For UPDATE contexts
|
|
194
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
195
|
+
async toStatusListDetails(
|
|
196
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
197
|
+
): Promise<StatusListResult & IStatusList2021ImplementationResult> {
|
|
198
|
+
if ('statusListCredential' in args) {
|
|
199
|
+
// CREATE/READ context
|
|
200
|
+
const { statusListCredential, correlationId, driverType } = args
|
|
201
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
202
|
+
const { issuer, credentialSubject } = uniform
|
|
203
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
204
|
+
|
|
205
|
+
const id = getAssertedValue('id', uniform.id)
|
|
206
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
207
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
208
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
209
|
+
const list = await StatusList.decode({ encodedList })
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
id,
|
|
213
|
+
encodedList,
|
|
214
|
+
issuer,
|
|
215
|
+
type: StatusListType.StatusList2021,
|
|
216
|
+
proofFormat,
|
|
217
|
+
length: list.length,
|
|
218
|
+
statusListCredential,
|
|
219
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
220
|
+
correlationId,
|
|
221
|
+
driverType,
|
|
190
222
|
indexingDirection: 'rightToLeft',
|
|
191
223
|
statusPurpose,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
224
|
+
statusList2021: {
|
|
225
|
+
indexingDirection: 'rightToLeft',
|
|
226
|
+
statusPurpose,
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
// UPDATE context
|
|
231
|
+
const { extractedDetails, statusListEntity } = args
|
|
232
|
+
const statusList2021Entity = statusListEntity as StatusList2021Entity
|
|
233
|
+
|
|
234
|
+
const proofFormat: CredentialProofFormat =
|
|
235
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
236
|
+
const list = await StatusList.decode({ encodedList: extractedDetails.encodedList })
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
id: extractedDetails.id,
|
|
240
|
+
encodedList: extractedDetails.encodedList,
|
|
241
|
+
issuer: extractedDetails.issuer,
|
|
242
|
+
type: StatusListType.StatusList2021,
|
|
243
|
+
proofFormat,
|
|
244
|
+
length: list.length,
|
|
245
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
246
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
247
|
+
correlationId: statusListEntity.correlationId,
|
|
248
|
+
driverType: statusListEntity.driverType,
|
|
249
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
250
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
251
|
+
statusList2021: {
|
|
252
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
253
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async createCredentialStatus(args: {
|
|
260
|
+
statusList: StatusListEntity
|
|
261
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
262
|
+
statusListIndex: number
|
|
263
|
+
}): Promise<StatusList2021EntryCredentialStatus> {
|
|
264
|
+
const { statusList, statusListIndex } = args
|
|
265
|
+
|
|
266
|
+
// Cast to StatusList2021Entity to access specific properties
|
|
267
|
+
const statusList2021 = statusList as StatusList2021Entity
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
id: `${statusList.id}#${statusListIndex}`,
|
|
271
|
+
type: 'StatusList2021Entry',
|
|
272
|
+
statusPurpose: statusList2021.statusPurpose ?? 'revocation',
|
|
273
|
+
statusListIndex: '' + statusListIndex,
|
|
274
|
+
statusListCredential: statusList.id,
|
|
195
275
|
}
|
|
196
276
|
}
|
|
197
277
|
|
|
@@ -203,7 +283,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
203
283
|
proofFormat: VeramoProofFormat
|
|
204
284
|
keyRef?: string
|
|
205
285
|
},
|
|
206
|
-
context: IAgentContext<
|
|
286
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
207
287
|
): Promise<StatusListCredential> {
|
|
208
288
|
const identifier = await context.agent.identifierManagedGet({
|
|
209
289
|
identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
|
|
@@ -234,7 +314,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
234
314
|
return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as StatusListCredential
|
|
235
315
|
}
|
|
236
316
|
|
|
237
|
-
private buildContentType(proofFormat:
|
|
317
|
+
private buildContentType(proofFormat: CredentialProofFormat | undefined) {
|
|
238
318
|
switch (proofFormat) {
|
|
239
319
|
case 'jwt':
|
|
240
320
|
return `application/statuslist+jwt`
|
|
@@ -2,6 +2,7 @@ import type { IStatusList } from './IStatusList'
|
|
|
2
2
|
import { StatusList2021Implementation } from './StatusList2021'
|
|
3
3
|
import { OAuthStatusListImplementation } from './OAuthStatusList'
|
|
4
4
|
import { StatusListType } from '@sphereon/ssi-types'
|
|
5
|
+
import { BitstringStatusListImplementation } from './BitstringStatusListImplementation'
|
|
5
6
|
|
|
6
7
|
export class StatusListFactory {
|
|
7
8
|
private static instance: StatusListFactory
|
|
@@ -11,6 +12,7 @@ export class StatusListFactory {
|
|
|
11
12
|
this.implementations = new Map()
|
|
12
13
|
this.implementations.set(StatusListType.StatusList2021, new StatusList2021Implementation())
|
|
13
14
|
this.implementations.set(StatusListType.OAuthStatusList, new OAuthStatusListImplementation())
|
|
15
|
+
this.implementations.set(StatusListType.BitstringStatusList, new BitstringStatusListImplementation())
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
public static getInstance(): StatusListFactory {
|
package/src/index.ts
CHANGED