@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
|
@@ -10,12 +10,13 @@ import {
|
|
|
10
10
|
} from '@sphereon/ssi-types'
|
|
11
11
|
|
|
12
12
|
import { StatusList } from '@sphereon/vc-status-list'
|
|
13
|
-
import type { IStatusList, IStatusList2021ImplementationResult } 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'
|
|
@@ -171,44 +172,82 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
171
172
|
return status ? Status2021.Invalid : Status2021.Valid
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
async
|
|
175
|
-
const
|
|
176
|
-
const uniform = CredentialMapper.toUniformCredential(statusListPayload)
|
|
175
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
176
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
177
177
|
const { issuer, credentialSubject } = uniform
|
|
178
|
-
const
|
|
179
|
-
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
180
|
-
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
181
|
-
|
|
182
|
-
const statusPurpose = getAssertedProperty('statusPurpose', credentialSubject)
|
|
183
|
-
const indexingDirection = 'rightToLeft'
|
|
184
|
-
const list = await StatusList.decode({ encodedList })
|
|
178
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
185
179
|
|
|
186
180
|
return {
|
|
187
|
-
|
|
188
|
-
id,
|
|
189
|
-
encodedList,
|
|
181
|
+
id: getAssertedValue('id', uniform.id),
|
|
190
182
|
issuer,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
statusListCredential: statusListPayload,
|
|
195
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
196
|
-
correlationId: args.correlationId, // FIXME these do not need to be inside the impl
|
|
197
|
-
driverType: args.driverType, // FIXME these do not need to be inside the impl
|
|
198
|
-
|
|
199
|
-
// Flattened StatusList2021-specific fields
|
|
200
|
-
indexingDirection,
|
|
201
|
-
statusPurpose,
|
|
183
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
202
186
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
187
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
188
|
+
// For UPDATE contexts
|
|
189
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
190
|
+
async toStatusListDetails(
|
|
191
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
192
|
+
): Promise<StatusListResult & IStatusList2021ImplementationResult> {
|
|
193
|
+
if ('statusListCredential' in args) {
|
|
194
|
+
// CREATE/READ context
|
|
195
|
+
const { statusListCredential, correlationId, driverType } = args
|
|
196
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
197
|
+
const { issuer, credentialSubject } = uniform
|
|
198
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
199
|
+
|
|
200
|
+
const id = getAssertedValue('id', uniform.id)
|
|
201
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
202
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
203
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
204
|
+
const list = await StatusList.decode({ encodedList })
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
id,
|
|
208
|
+
encodedList,
|
|
209
|
+
issuer,
|
|
210
|
+
type: StatusListType.StatusList2021,
|
|
211
|
+
proofFormat,
|
|
212
|
+
length: list.length,
|
|
213
|
+
statusListCredential,
|
|
214
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
215
|
+
correlationId,
|
|
216
|
+
driverType,
|
|
217
|
+
indexingDirection: 'rightToLeft',
|
|
206
218
|
statusPurpose,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
219
|
+
statusList2021: {
|
|
220
|
+
indexingDirection: 'rightToLeft',
|
|
221
|
+
statusPurpose,
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
// UPDATE context
|
|
226
|
+
const { extractedDetails, statusListEntity } = args
|
|
227
|
+
const statusList2021Entity = statusListEntity as StatusList2021Entity
|
|
228
|
+
|
|
229
|
+
const proofFormat: CredentialProofFormat =
|
|
230
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
231
|
+
const list = await StatusList.decode({ encodedList: extractedDetails.encodedList })
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
id: extractedDetails.id,
|
|
235
|
+
encodedList: extractedDetails.encodedList,
|
|
236
|
+
issuer: extractedDetails.issuer,
|
|
237
|
+
type: StatusListType.StatusList2021,
|
|
238
|
+
proofFormat,
|
|
239
|
+
length: list.length,
|
|
240
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
241
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
242
|
+
correlationId: statusListEntity.correlationId,
|
|
243
|
+
driverType: statusListEntity.driverType,
|
|
244
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
245
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
246
|
+
statusList2021: {
|
|
247
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
248
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
249
|
+
},
|
|
250
|
+
}
|
|
212
251
|
}
|
|
213
252
|
}
|
|
214
253
|
|
package/src/index.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ import type { SdJwtVcPayload } from '@sd-jwt/sd-jwt-vc'
|
|
|
19
19
|
import type { StatusListOpts } from '@sphereon/oid4vci-common'
|
|
20
20
|
import { BitstringStatusPurpose } from '@4sure-tech/vc-bitstring-status-lists'
|
|
21
21
|
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
22
|
+
import { IExtractedCredentialDetails } from '../impl/IStatusList'
|
|
23
|
+
import { BitstringStatusListArgs, IStatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
22
24
|
|
|
23
25
|
export enum StatusOAuth {
|
|
24
26
|
Valid = 0,
|
|
@@ -31,8 +33,6 @@ export enum Status2021 {
|
|
|
31
33
|
Invalid = 1,
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
export type BitstringStatus = number
|
|
35
|
-
|
|
36
36
|
export type StatusList2021Args = {
|
|
37
37
|
indexingDirection: StatusListIndexingDirection
|
|
38
38
|
statusPurpose?: StatusPurpose2021
|
|
@@ -44,14 +44,6 @@ export type OAuthStatusListArgs = {
|
|
|
44
44
|
expiresAt?: Date
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export type BitstringStatusListArgs = {
|
|
48
|
-
statusPurpose: BitstringStatusPurpose
|
|
49
|
-
bitsPerStatus: number
|
|
50
|
-
ttl?: number
|
|
51
|
-
validFrom?: Date
|
|
52
|
-
validUntil?: Date
|
|
53
|
-
}
|
|
54
|
-
|
|
55
47
|
export type BaseCreateNewStatusListArgs = {
|
|
56
48
|
type: StatusListType
|
|
57
49
|
id: string
|
|
@@ -102,7 +94,7 @@ export interface UpdateStatusListFromStatusListCredentialArgs {
|
|
|
102
94
|
statusListCredential: StatusListCredential // | CompactJWT
|
|
103
95
|
keyRef?: string
|
|
104
96
|
statusListIndex: number | string
|
|
105
|
-
value: number | Status2021 | StatusOAuth
|
|
97
|
+
value: number | Status2021 | StatusOAuth
|
|
106
98
|
}
|
|
107
99
|
|
|
108
100
|
export interface StatusListResult {
|
|
@@ -163,16 +155,6 @@ export interface StatusListOAuthEntryCredentialStatus extends ICredentialStatus
|
|
|
163
155
|
expiresAt?: Date
|
|
164
156
|
}
|
|
165
157
|
|
|
166
|
-
export interface BitstringStatusListEntryCredentialStatus extends ICredentialStatus {
|
|
167
|
-
type: 'BitstringStatusListEntry'
|
|
168
|
-
statusPurpose: BitstringStatusPurpose | BitstringStatusPurpose[]
|
|
169
|
-
statusListIndex: string
|
|
170
|
-
statusListCredential: string
|
|
171
|
-
bitsPerStatus?: number
|
|
172
|
-
statusMessage?: Array<BitstringStatus>
|
|
173
|
-
statusReference?: string | string[]
|
|
174
|
-
}
|
|
175
|
-
|
|
176
158
|
export interface StatusList2021ToVerifiableCredentialArgs {
|
|
177
159
|
issuer: string | IIssuer
|
|
178
160
|
id: string
|
|
@@ -198,7 +180,7 @@ export interface CreateStatusListArgs {
|
|
|
198
180
|
export interface UpdateStatusListIndexArgs {
|
|
199
181
|
statusListCredential: StatusListCredential // | CompactJWT
|
|
200
182
|
statusListIndex: number | string
|
|
201
|
-
value: number | Status2021 | StatusOAuth
|
|
183
|
+
value: number | Status2021 | StatusOAuth
|
|
202
184
|
bitsPerStatus?: number
|
|
203
185
|
keyRef?: string
|
|
204
186
|
expiresAt?: Date
|
|
@@ -210,11 +192,22 @@ export interface CheckStatusIndexArgs {
|
|
|
210
192
|
bitsPerStatus?: number
|
|
211
193
|
}
|
|
212
194
|
|
|
213
|
-
|
|
214
|
-
|
|
195
|
+
// For the CREATE and READ contexts
|
|
196
|
+
export interface IToDetailsFromCredentialArgs {
|
|
197
|
+
// The source credential we are converting
|
|
198
|
+
statusListCredential: StatusListCredential
|
|
199
|
+
|
|
200
|
+
// The required metadata that is NOT in the credential itself
|
|
201
|
+
statusListType: StatusListType
|
|
202
|
+
bitsPerStatus?: number
|
|
215
203
|
correlationId?: string
|
|
216
204
|
driverType?: StatusListDriverType
|
|
217
|
-
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// For the UPDATE context
|
|
208
|
+
export interface IMergeDetailsWithEntityArgs {
|
|
209
|
+
extractedDetails: IExtractedCredentialDetails
|
|
210
|
+
statusListEntity: IStatusListEntity
|
|
218
211
|
}
|
|
219
212
|
|
|
220
213
|
/**
|
package/src/utils.ts
CHANGED
|
@@ -53,31 +53,60 @@ export function assertValidProofType(type: StatusListType, proofFormat: Credenti
|
|
|
53
53
|
|
|
54
54
|
export function determineStatusListType(credential: StatusListCredential): StatusListType {
|
|
55
55
|
const proofFormat = determineProofFormat(credential)
|
|
56
|
+
|
|
56
57
|
switch (proofFormat) {
|
|
57
58
|
case 'jwt':
|
|
58
|
-
|
|
59
|
-
const keys = Object.keys(payload)
|
|
60
|
-
if (keys.includes('status_list')) {
|
|
61
|
-
return StatusListType.OAuthStatusList
|
|
62
|
-
} else if (keys.includes('vc')) {
|
|
63
|
-
return StatusListType.StatusList2021
|
|
64
|
-
}
|
|
65
|
-
break
|
|
59
|
+
return determineJwtStatusListType(credential as string)
|
|
66
60
|
case 'lds':
|
|
67
|
-
|
|
68
|
-
const type = uniform.type.find((t) => {
|
|
69
|
-
return Object.values(StatusListType).some((statusType) => t.includes(statusType))
|
|
70
|
-
})
|
|
71
|
-
if (!type) {
|
|
72
|
-
throw new Error('Invalid status list credential type')
|
|
73
|
-
}
|
|
74
|
-
return type.replace('Credential', '') as StatusListType
|
|
75
|
-
|
|
61
|
+
return determineLdsStatusListType(credential)
|
|
76
62
|
case 'cbor':
|
|
77
63
|
return StatusListType.OAuthStatusList
|
|
64
|
+
default:
|
|
65
|
+
throw new Error('Cannot determine status list type from credential payload')
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function determineJwtStatusListType(credential: string): StatusListType {
|
|
70
|
+
const payload: any = jwtDecode(credential)
|
|
71
|
+
|
|
72
|
+
// OAuth status list format
|
|
73
|
+
if ('status_list' in payload) {
|
|
74
|
+
return StatusListType.OAuthStatusList
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Direct credential subject
|
|
78
|
+
if ('credentialSubject' in payload) {
|
|
79
|
+
return getStatusListTypeFromSubject(payload.credentialSubject)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Wrapped VC format
|
|
83
|
+
if ('vc' in payload && 'credentialSubject' in payload.vc) {
|
|
84
|
+
return getStatusListTypeFromSubject(payload.vc.credentialSubject)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new Error('Invalid status list credential: credentialSubject not found')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function determineLdsStatusListType(credential: StatusListCredential): StatusListType {
|
|
91
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
92
|
+
const statusListType = uniform.type.find((type) => Object.values(StatusListType).some((statusType) => type.includes(statusType)))
|
|
93
|
+
|
|
94
|
+
if (!statusListType) {
|
|
95
|
+
throw new Error('Invalid status list credential type')
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
|
|
98
|
+
return statusListType.replace('Credential', '') as StatusListType
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getStatusListTypeFromSubject(credentialSubject: any): StatusListType {
|
|
102
|
+
switch (credentialSubject.type) {
|
|
103
|
+
case 'StatusList2021':
|
|
104
|
+
return StatusListType.StatusList2021
|
|
105
|
+
case 'BitstringStatusList':
|
|
106
|
+
return StatusListType.BitstringStatusList
|
|
107
|
+
default:
|
|
108
|
+
throw new Error(`Unknown credential subject type: ${credentialSubject.type}`)
|
|
109
|
+
}
|
|
81
110
|
}
|
|
82
111
|
|
|
83
112
|
export function determineProofFormat(credential: StatusListCredential): CredentialProofFormat {
|