@sphereon/ssi-sdk.vc-status-list 0.32.1-feature.MWALL.715.49 → 0.32.1-feature.SDK.56.oauth.status.list.34

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.
Files changed (51) hide show
  1. package/dist/functions.d.ts +13 -13
  2. package/dist/functions.d.ts.map +1 -1
  3. package/dist/functions.js +40 -109
  4. package/dist/functions.js.map +1 -1
  5. package/dist/impl/IStatusList.d.ts +22 -0
  6. package/dist/impl/IStatusList.d.ts.map +1 -0
  7. package/dist/impl/IStatusList.js +3 -0
  8. package/dist/impl/IStatusList.js.map +1 -0
  9. package/dist/impl/OAuthStatusList.d.ts +20 -0
  10. package/dist/impl/OAuthStatusList.d.ts.map +1 -0
  11. package/dist/impl/OAuthStatusList.js +147 -0
  12. package/dist/impl/OAuthStatusList.js.map +1 -0
  13. package/dist/impl/StatusList2021.d.ts +14 -0
  14. package/dist/impl/StatusList2021.d.ts.map +1 -0
  15. package/dist/impl/StatusList2021.js +153 -0
  16. package/dist/impl/StatusList2021.js.map +1 -0
  17. package/dist/impl/StatusListFactory.d.ts +12 -0
  18. package/dist/impl/StatusListFactory.d.ts.map +1 -0
  19. package/dist/impl/StatusListFactory.js +36 -0
  20. package/dist/impl/StatusListFactory.js.map +1 -0
  21. package/dist/impl/encoding/cbor.d.ts +6 -0
  22. package/dist/impl/encoding/cbor.d.ts.map +1 -0
  23. package/dist/impl/encoding/cbor.js +128 -0
  24. package/dist/impl/encoding/cbor.js.map +1 -0
  25. package/dist/impl/encoding/common.d.ts +12 -0
  26. package/dist/impl/encoding/common.d.ts.map +1 -0
  27. package/dist/impl/encoding/common.js +26 -0
  28. package/dist/impl/encoding/common.js.map +1 -0
  29. package/dist/impl/encoding/jwt.d.ts +7 -0
  30. package/dist/impl/encoding/jwt.d.ts.map +1 -0
  31. package/dist/impl/encoding/jwt.js +56 -0
  32. package/dist/impl/encoding/jwt.js.map +1 -0
  33. package/dist/types/index.d.ts +107 -29
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/index.js +12 -0
  36. package/dist/types/index.js.map +1 -1
  37. package/dist/utils.d.ts +17 -0
  38. package/dist/utils.d.ts.map +1 -0
  39. package/dist/utils.js +87 -0
  40. package/dist/utils.js.map +1 -0
  41. package/package.json +13 -5
  42. package/src/functions.ts +59 -158
  43. package/src/impl/IStatusList.ts +36 -0
  44. package/src/impl/OAuthStatusList.ts +165 -0
  45. package/src/impl/StatusList2021.ts +195 -0
  46. package/src/impl/StatusListFactory.ts +39 -0
  47. package/src/impl/encoding/cbor.ts +152 -0
  48. package/src/impl/encoding/common.ts +25 -0
  49. package/src/impl/encoding/jwt.ts +54 -0
  50. package/src/types/index.ts +123 -41
  51. package/src/utils.ts +91 -0
package/src/functions.ts CHANGED
@@ -1,41 +1,42 @@
1
1
  import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
2
2
  import {
3
3
  CredentialMapper,
4
- DocumentFormat,
5
- IIssuer,
6
- OriginalVerifiableCredential,
4
+ ProofFormat,
7
5
  StatusListDriverType,
8
6
  StatusListType,
7
+ StatusListVerifiableCredential,
9
8
  StatusPurpose2021,
10
9
  } from '@sphereon/ssi-types'
10
+ import { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat as VmoProofFormat } from '@veramo/core'
11
11
 
12
- import { checkStatus, StatusList } from '@sphereon/vc-status-list'
13
- import { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat } from '@veramo/core'
12
+ import { checkStatus } from '@sphereon/vc-status-list'
14
13
  import { CredentialJwtOrJSON, StatusMethod } from 'credential-status'
15
14
  import {
16
15
  CreateNewStatusListFuncArgs,
16
+ Status2021,
17
17
  StatusList2021ToVerifiableCredentialArgs,
18
- StatusListDetails,
19
18
  StatusListResult,
19
+ StatusOAuth,
20
20
  UpdateStatusListFromEncodedListArgs,
21
21
  UpdateStatusListFromStatusListCredentialArgs,
22
22
  } from './types'
23
+ import { assertValidProofType, determineStatusListType, getAssertedValue, getAssertedValues } from './utils'
24
+ import { getStatusListImplementation } from './impl/StatusListFactory'
23
25
 
24
- export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<OriginalVerifiableCredential> {
26
+ export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<StatusListVerifiableCredential> {
25
27
  const url = getAssertedValue('statusListCredential', args.statusListCredential)
26
28
  try {
27
29
  const response = await fetch(url)
28
30
  if (!response.ok) {
29
- const error = `Fetching status list ${url} resulted in an error: ${response.status} : ${response.statusText}`
30
- throw Error(error)
31
+ throw Error(`Fetching status list ${url} resulted in an error: ${response.status} : ${response.statusText}`)
31
32
  }
32
33
  const responseAsText = await response.text()
33
34
  if (responseAsText.trim().startsWith('{')) {
34
- return JSON.parse(responseAsText) as OriginalVerifiableCredential
35
+ return JSON.parse(responseAsText) as StatusListVerifiableCredential
35
36
  }
36
- return responseAsText as OriginalVerifiableCredential
37
+ return responseAsText as StatusListVerifiableCredential
37
38
  } catch (error) {
38
- console.log(`Fetching status list ${url} resulted in an unexpected error: ${error instanceof Error ? error.message : JSON.stringify(error)}`)
39
+ console.error(`Fetching status list ${url} resulted in an unexpected error: ${error instanceof Error ? error.message : JSON.stringify(error)}`)
39
40
  throw error
40
41
  }
41
42
  }
@@ -52,7 +53,7 @@ export function statusPluginStatusFunction(args: {
52
53
  const result = await checkStatusForCredential({
53
54
  ...args,
54
55
  documentLoader: args.documentLoader,
55
- credential: credential as OriginalVerifiableCredential,
56
+ credential: credential as StatusListVerifiableCredential,
56
57
  errorUnknownListType: args.errorUnknownListType,
57
58
  })
58
59
 
@@ -75,7 +76,7 @@ export function vcLibCheckStatusFunction(args: {
75
76
  }) {
76
77
  const { mandatoryCredentialStatus, verifyStatusListCredential, verifyMatchingIssuers, errorUnknownListType } = args
77
78
  return (args: {
78
- credential: OriginalVerifiableCredential
79
+ credential: StatusListVerifiableCredential
79
80
  documentLoader: any
80
81
  suite: any
81
82
  }): Promise<{
@@ -93,7 +94,7 @@ export function vcLibCheckStatusFunction(args: {
93
94
  }
94
95
 
95
96
  export async function checkStatusForCredential(args: {
96
- credential: OriginalVerifiableCredential
97
+ credential: StatusListVerifiableCredential
97
98
  documentLoader: any
98
99
  suite: any
99
100
  mandatoryCredentialStatus?: boolean
@@ -132,7 +133,7 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
132
133
  type?: StatusListType | 'StatusList2021Entry'
133
134
  id?: string
134
135
  statusListIndex: string
135
- }): Promise<boolean> {
136
+ }): Promise<number | Status2021 | StatusOAuth> {
136
137
  return checkStatusIndexFromStatusListCredential({
137
138
  ...args,
138
139
  statusListCredential: await fetchStatusListCredential(args),
@@ -140,163 +141,84 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
140
141
  }
141
142
 
142
143
  export async function checkStatusIndexFromStatusListCredential(args: {
143
- statusListCredential: OriginalVerifiableCredential
144
+ statusListCredential: StatusListVerifiableCredential
144
145
  statusPurpose?: StatusPurpose2021
145
146
  type?: StatusListType | 'StatusList2021Entry'
146
147
  id?: string
147
148
  statusListIndex: string | number
148
- }): Promise<boolean> {
149
- const requestedType = getAssertedStatusListType(args.type?.replace('Entry', '') as StatusListType)
150
- const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
151
- const { issuer, type, credentialSubject, id } = uniform
152
- getAssertedValue('issuer', issuer) // We are only checking the value here
153
- getAssertedValue('credentialSubject', credentialSubject)
154
- if (args.statusPurpose && 'statusPurpose' in credentialSubject) {
155
- if (args.statusPurpose !== credentialSubject.statusPurpose) {
156
- throw Error(
157
- `Status purpose in StatusList credential with id ${id} and value ${credentialSubject.statusPurpose} does not match supplied purpose: ${args.statusPurpose}`,
158
- )
159
- }
160
- } else if (args.id && args.id !== id) {
161
- throw Error(`Status list id ${id} did not match required supplied id: ${args.id}`)
162
- }
163
- if (!type || !(type.includes(requestedType) || type.includes(requestedType + 'Credential'))) {
164
- throw Error(`Credential type ${JSON.stringify(type)} does not contain requested type ${requestedType}`)
165
- }
166
- // @ts-ignore
167
- const encodedList = getAssertedValue('encodedList', credentialSubject['encodedList'])
168
-
169
- const statusList = await StatusList.decode({ encodedList })
170
- const status = statusList.getStatus(typeof args.statusListIndex === 'number' ? args.statusListIndex : Number.parseInt(args.statusListIndex))
171
- return status
149
+ }): Promise<number | Status2021 | StatusOAuth> {
150
+ const statusListType: StatusListType = determineStatusListType(args.statusListCredential)
151
+ const implementation = getStatusListImplementation(statusListType)
152
+ return implementation.checkStatusIndex(args)
172
153
  }
173
154
 
174
155
  export async function createNewStatusList(
175
156
  args: CreateNewStatusListFuncArgs,
176
157
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
177
158
  ): Promise<StatusListResult> {
178
- const length = args?.length ?? 250000
179
- const proofFormat = args?.proofFormat ?? 'lds'
180
- const { issuer, type, id } = getAssertedValues(args)
181
- const correlationId = getAssertedValue('correlationId', args.correlationId)
182
-
183
- const list = new StatusList({ length })
184
- const encodedList = await list.encode()
185
- const statusPurpose = args.statusPurpose ?? 'revocation'
186
- const statusListCredential = await statusList2021ToVerifiableCredential(
187
- {
188
- ...args,
189
- type,
190
- proofFormat,
191
- encodedList,
192
- },
193
- context,
194
- )
195
-
196
- return {
197
- encodedList,
198
- statusListCredential,
199
- length,
200
- type,
201
- proofFormat,
202
- id,
203
- correlationId,
204
- issuer,
205
- statusPurpose,
206
- indexingDirection: 'rightToLeft',
207
- } as StatusListResult
159
+ const { type } = getAssertedValues(args)
160
+ const implementation = getStatusListImplementation(type)
161
+ return implementation.createNewStatusList(args, context)
208
162
  }
209
163
 
210
164
  export async function updateStatusIndexFromStatusListCredential(
211
165
  args: UpdateStatusListFromStatusListCredentialArgs,
212
166
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
213
- ): Promise<StatusListDetails> {
214
- return updateStatusListIndexFromEncodedList(
215
- {
216
- ...(await statusListCredentialToDetails(args)),
217
- statusListIndex: args.statusListIndex,
218
- value: args.value,
219
- },
220
- context,
221
- )
167
+ ): Promise<StatusListResult> {
168
+ const credential = getAssertedValue('statusListCredential', args.statusListCredential)
169
+ const statusListType: StatusListType = determineStatusListType(credential)
170
+ const implementation = getStatusListImplementation(statusListType)
171
+ return implementation.updateStatusListIndex(args, context)
222
172
  }
223
173
 
174
+ // Keeping helper function for backward compatibility
224
175
  export async function statusListCredentialToDetails(args: {
225
- statusListCredential: OriginalVerifiableCredential
176
+ statusListCredential: StatusListVerifiableCredential
226
177
  correlationId?: string
227
178
  driverType?: StatusListDriverType
228
- }): Promise<StatusListDetails> {
179
+ }): Promise<StatusListResult> {
229
180
  const credential = getAssertedValue('statusListCredential', args.statusListCredential)
230
181
  const uniform = CredentialMapper.toUniformCredential(credential)
231
- const { issuer, type, credentialSubject } = uniform
232
- if (!type.includes('StatusList2021Credential')) {
233
- throw Error('StatusList2021Credential type should be present in the Verifiable Credential')
234
- }
235
- const id = getAssertedValue('id', uniform.id)
236
- // @ts-ignore
237
- const { encodedList, statusPurpose } = credentialSubject
238
- const proofFormat: ProofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
239
- return {
240
- id,
241
- encodedList,
242
- issuer,
243
- type: StatusListType.StatusList2021,
244
- proofFormat,
245
- indexingDirection: 'rightToLeft',
246
- length: (await StatusList.decode({ encodedList })).length,
247
- statusPurpose,
248
- statusListCredential: credential,
249
- ...(args.correlationId && { correlationId: args.correlationId }),
250
- ...(args.driverType && { driverType: args.driverType }),
182
+ const type = uniform.type.find((t) => t.includes('StatusList2021') || t.includes('OAuth2StatusList'))
183
+ if (!type) {
184
+ throw new Error('Invalid status list credential type')
251
185
  }
186
+ const statusListType = type.replace('Credential', '') as StatusListType
187
+ const implementation = getStatusListImplementation(statusListType)
188
+ return implementation.updateStatusListIndex(
189
+ {
190
+ statusListCredential: args.statusListCredential,
191
+ statusListIndex: 0,
192
+ value: 0,
193
+ },
194
+ {} as IAgentContext<ICredentialPlugin & IIdentifierResolution>,
195
+ )
252
196
  }
253
197
 
254
198
  export async function updateStatusListIndexFromEncodedList(
255
199
  args: UpdateStatusListFromEncodedListArgs,
256
200
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
257
- ): Promise<StatusListDetails> {
258
- const { issuer, type, id } = getAssertedValues(args)
259
- const proofFormat = args?.proofFormat ?? 'lds'
260
- const origEncodedList = getAssertedValue('encodedList', args.encodedList)
261
- const index = getAssertedValue('index', typeof args.statusListIndex === 'number' ? args.statusListIndex : Number.parseInt(args.statusListIndex))
262
- const value = getAssertedValue('value', args.value)
263
- const statusPurpose = getAssertedValue('statusPurpose', args.statusPurpose)
264
-
265
- const statusList = await StatusList.decode({ encodedList: origEncodedList })
266
- statusList.setStatus(index, value)
267
- const encodedList = await statusList.encode()
268
- const statusListCredential = await statusList2021ToVerifiableCredential(
269
- {
270
- ...args,
271
- type,
272
- proofFormat,
273
- encodedList,
274
- },
275
- context,
276
- )
277
- return {
278
- encodedList,
279
- statusListCredential,
280
- length: statusList.length - 1,
281
- type,
282
- proofFormat,
283
- id,
284
- issuer,
285
- statusPurpose,
286
- indexingDirection: 'rightToLeft',
287
- }
201
+ ): Promise<StatusListResult> {
202
+ const { type } = getAssertedValue('type', args)
203
+ const implementation = getStatusListImplementation(type!)
204
+ return implementation.updateStatusListFromEncodedList(args, context)
288
205
  }
289
206
 
207
+ // TODO Is this still in use? Or do we need to redesign this after having multiple status list types?
290
208
  export async function statusList2021ToVerifiableCredential(
291
209
  args: StatusList2021ToVerifiableCredentialArgs,
292
210
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
293
- ): Promise<OriginalVerifiableCredential> {
211
+ ): Promise<StatusListVerifiableCredential> {
294
212
  const { issuer, id, type } = getAssertedValues(args)
295
213
  const identifier = await context.agent.identifierManagedGet({
296
214
  identifier: typeof issuer === 'string' ? issuer : issuer.id,
297
215
  vmRelationship: 'assertionMethod',
298
216
  offlineWhenNoDIDRegistered: true, // FIXME Fix identifier resolution for EBSI
299
217
  })
218
+ const proofFormat: ProofFormat = args?.proofFormat ?? 'lds'
219
+ assertValidProofType(StatusListType.StatusList2021, proofFormat)
220
+ const vmoProofFormat: VmoProofFormat = proofFormat as VmoProofFormat
221
+
300
222
  const encodedList = getAssertedValue('encodedList', args.encodedList)
301
223
  const statusPurpose = getAssertedValue('statusPurpose', args.statusPurpose)
302
224
  const credential = {
@@ -316,31 +238,10 @@ export async function statusList2021ToVerifiableCredential(
316
238
  const verifiableCredential = await context.agent.createVerifiableCredential({
317
239
  credential,
318
240
  keyRef: identifier.kmsKeyRef,
319
- proofFormat: args.proofFormat ?? 'lds',
241
+ proofFormat: vmoProofFormat,
320
242
  fetchRemoteContexts: true,
321
243
  })
322
244
 
323
- return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as OriginalVerifiableCredential).original
324
- }
325
-
326
- function getAssertedStatusListType(type?: StatusListType) {
327
- const assertedType = type ?? StatusListType.StatusList2021
328
- if (assertedType !== StatusListType.StatusList2021) {
329
- throw Error(`StatusList type ${assertedType} is not supported (yet)`)
330
- }
331
- return assertedType
332
- }
333
-
334
- function getAssertedValue<T>(name: string, value: T): NonNullable<T> {
335
- if (value === undefined || value === null) {
336
- throw Error(`Missing required ${name} value`)
337
- }
338
- return value
339
- }
340
-
341
- function getAssertedValues(args: { issuer: string | IIssuer; id: string; type?: StatusListType }) {
342
- const type = getAssertedStatusListType(args?.type)
343
- const id = getAssertedValue('id', args.id)
344
- const issuer = getAssertedValue('issuer', args.issuer)
345
- return { id, issuer, type }
245
+ return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListVerifiableCredential)
246
+ .original as StatusListVerifiableCredential
346
247
  }
@@ -0,0 +1,36 @@
1
+ import { IAgentContext, ICredentialPlugin } from '@veramo/core'
2
+ import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
3
+ import {
4
+ CheckStatusIndexArgs,
5
+ CreateStatusListArgs,
6
+ Status2021,
7
+ StatusListResult,
8
+ StatusOAuth,
9
+ UpdateStatusListFromEncodedListArgs,
10
+ UpdateStatusListIndexArgs,
11
+ } from '../types'
12
+
13
+ export interface IStatusList {
14
+ /**
15
+ * Creates a new status list of the specific type
16
+ */
17
+ createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
18
+
19
+ /**
20
+ * Updates a status at the given index in the status list
21
+ */
22
+ updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
23
+
24
+ /**
25
+ * Updates a status list using a base64 encoded list of statuses
26
+ */
27
+ updateStatusListFromEncodedList(
28
+ args: UpdateStatusListFromEncodedListArgs,
29
+ context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
30
+ ): Promise<StatusListResult>
31
+
32
+ /**
33
+ * Checks the status at a given index in the status list
34
+ */
35
+ checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
36
+ }
@@ -0,0 +1,165 @@
1
+ import { IAgentContext, ICredentialPlugin } from '@veramo/core'
2
+ import { ProofFormat, StatusListType, StatusListVerifiableCredential } from '@sphereon/ssi-types'
3
+ import {
4
+ CheckStatusIndexArgs,
5
+ CreateStatusListArgs,
6
+ StatusListResult,
7
+ StatusOAuth,
8
+ UpdateStatusListFromEncodedListArgs,
9
+ UpdateStatusListIndexArgs,
10
+ } from '../types'
11
+ import { determineProofFormat, getAssertedValue, getAssertedValues } from '../utils'
12
+ import { IStatusList } from './IStatusList'
13
+ import { StatusList, StatusListJWTHeaderParameters } from '@sd-jwt/jwt-status-list'
14
+ import { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
15
+ import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
16
+ import { createSignedJwt, decodeStatusListJWT } from './encoding/jwt'
17
+ import { createSignedCbor, decodeStatusListCWT } from './encoding/cbor'
18
+
19
+ type IRequiredContext = IAgentContext<ICredentialPlugin & IJwtService & IIdentifierResolution>
20
+
21
+ export const BITS_PER_STATUS_DEFAULT = 2 // 2 bits are sufficient for 0x00 - "VALID" 0x01 - "INVALID" & 0x02 - "SUSPENDED"
22
+ export const DEFAULT_LIST_LENGTH = 250000
23
+ export const DEFAULT_PROOF_FORMAT = 'jwt' as ProofFormat
24
+ export const STATUS_LIST_JWT_HEADER: StatusListJWTHeaderParameters = {
25
+ alg: 'EdDSA',
26
+ typ: 'statuslist+jwt',
27
+ }
28
+
29
+ export class OAuthStatusListImplementation implements IStatusList {
30
+ async createNewStatusList(args: CreateStatusListArgs, context: IRequiredContext): Promise<StatusListResult> {
31
+ if (!args.oauthStatusList) {
32
+ throw new Error('OAuthStatusList options are required for type OAuthStatusList')
33
+ }
34
+
35
+ const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
36
+ const { issuer, id } = args
37
+ const length = args.length ?? DEFAULT_LIST_LENGTH
38
+ const bitsPerStatus = args.oauthStatusList.bitsPerStatus ?? BITS_PER_STATUS_DEFAULT
39
+ const issuerString = typeof issuer === 'string' ? issuer : issuer.id
40
+ const correlationId = getAssertedValue('correlationId', args.correlationId)
41
+
42
+ const statusList = new StatusList(new Array(length).fill(0), bitsPerStatus)
43
+ const encodedList = statusList.compressStatusList()
44
+ let statusListCredential: StatusListVerifiableCredential
45
+
46
+ switch (proofFormat) {
47
+ case 'jwt': {
48
+ const { statusListCredential: slJwt } = await createSignedJwt(context, statusList, issuerString, id, args.keyRef)
49
+ statusListCredential = slJwt
50
+ break
51
+ }
52
+ case 'cbor': {
53
+ const { statusListCredential: slCbor } = await createSignedCbor(context, statusList, issuerString, id, args.keyRef)
54
+ statusListCredential = slCbor
55
+ break
56
+ }
57
+ default:
58
+ throw new Error(`Invalid proof format '${proofFormat}' for OAuthStatusList`)
59
+ }
60
+
61
+ return {
62
+ encodedList,
63
+ statusListCredential,
64
+ oauthStatusList: { bitsPerStatus },
65
+ length,
66
+ type: StatusListType.OAuthStatusList,
67
+ proofFormat,
68
+ id,
69
+ correlationId,
70
+ issuer,
71
+ }
72
+ }
73
+
74
+ async updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IRequiredContext): Promise<StatusListResult> {
75
+ const { statusListCredential, value } = args
76
+ if (typeof statusListCredential !== 'string') {
77
+ return Promise.reject('statusListCredential in neither JWT nor CWT')
78
+ }
79
+
80
+ const proofFormat = determineProofFormat(statusListCredential)
81
+ const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential)
82
+ const { statusList, issuer, id } = decoded
83
+
84
+ const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
85
+ if (index < 0 || index >= statusList.statusList.length) {
86
+ throw new Error('Status list index out of bounds')
87
+ }
88
+
89
+ statusList.setStatus(index, value)
90
+ const result =
91
+ proofFormat === 'jwt'
92
+ ? await createSignedJwt(context, statusList, issuer, id, args.keyRef)
93
+ : await createSignedCbor(context, statusList, issuer, id, args.keyRef)
94
+
95
+ return {
96
+ ...result,
97
+ oauthStatusList: {
98
+ bitsPerStatus: statusList.getBitsPerStatus(),
99
+ },
100
+ length: statusList.statusList.length,
101
+ type: StatusListType.OAuthStatusList,
102
+ proofFormat,
103
+ id,
104
+ issuer,
105
+ }
106
+ }
107
+
108
+ async updateStatusListFromEncodedList(args: UpdateStatusListFromEncodedListArgs, context: IRequiredContext): Promise<StatusListResult> {
109
+ if (!args.oauthStatusList) {
110
+ throw new Error('OAuthStatusList options are required for type OAuthStatusList')
111
+ }
112
+
113
+ const proofFormat = args.proofFormat ?? DEFAULT_PROOF_FORMAT
114
+ const { issuer, id } = getAssertedValues(args)
115
+ const bitsPerStatus = args.oauthStatusList.bitsPerStatus ?? BITS_PER_STATUS_DEFAULT
116
+ const issuerString = typeof issuer === 'string' ? issuer : issuer.id
117
+
118
+ const listToUpdate = StatusList.decompressStatusList(args.encodedList, bitsPerStatus)
119
+ const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
120
+ listToUpdate.setStatus(index, args.value ? 1 : 0)
121
+
122
+ let result: { statusListCredential: StatusListVerifiableCredential; encodedList: string }
123
+
124
+ switch (proofFormat) {
125
+ case 'jwt':
126
+ result = await createSignedJwt(context, listToUpdate, issuerString, id, args.keyRef)
127
+ break
128
+ case 'cbor':
129
+ result = await createSignedCbor(context, listToUpdate, issuerString, id, args.keyRef)
130
+ break
131
+ default:
132
+ throw new Error(`Invalid proof format '${proofFormat}' for OAuthStatusList`)
133
+ }
134
+
135
+ return {
136
+ encodedList: result.encodedList,
137
+ statusListCredential: result.statusListCredential,
138
+ oauthStatusList: {
139
+ bitsPerStatus,
140
+ },
141
+ length: listToUpdate.statusList.length,
142
+ type: StatusListType.OAuthStatusList,
143
+ proofFormat,
144
+ id,
145
+ issuer,
146
+ }
147
+ }
148
+
149
+ async checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | StatusOAuth> {
150
+ const { statusListCredential, statusListIndex } = args
151
+ if (typeof statusListCredential !== 'string') {
152
+ return Promise.reject('statusListCredential in neither JWT nor CWT')
153
+ }
154
+
155
+ const proofFormat = determineProofFormat(statusListCredential)
156
+ const { statusList } = proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential)
157
+
158
+ const index = typeof statusListIndex === 'number' ? statusListIndex : parseInt(statusListIndex)
159
+ if (index < 0 || index >= statusList.statusList.length) {
160
+ throw new Error('Status list index out of bounds')
161
+ }
162
+
163
+ return statusList.getStatus(index)
164
+ }
165
+ }