@sphereon/oid4vci-client 0.10.4-unstable.2 → 0.10.4-unstable.21
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/AccessTokenClient.d.ts +5 -5
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +42 -22
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AccessTokenClientV1_0_11.d.ts +29 -0
- package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -0
- package/dist/AccessTokenClientV1_0_11.js +212 -0
- package/dist/AccessTokenClientV1_0_11.js.map +1 -0
- package/dist/AuthorizationCodeClient.d.ts +4 -4
- package/dist/AuthorizationCodeClient.d.ts.map +1 -1
- package/dist/AuthorizationCodeClient.js +14 -3
- package/dist/AuthorizationCodeClient.js.map +1 -1
- package/dist/AuthorizationCodeClientV1_0_11.d.ts +9 -0
- package/dist/AuthorizationCodeClientV1_0_11.d.ts.map +1 -0
- package/dist/AuthorizationCodeClientV1_0_11.js +132 -0
- package/dist/AuthorizationCodeClientV1_0_11.js.map +1 -0
- package/dist/CredentialOfferClient.d.ts.map +1 -1
- package/dist/CredentialOfferClient.js +14 -25
- package/dist/CredentialOfferClient.js.map +1 -1
- package/dist/CredentialOfferClientV1_0_11.d.ts +10 -0
- package/dist/CredentialOfferClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialOfferClientV1_0_11.js +103 -0
- package/dist/CredentialOfferClientV1_0_11.js.map +1 -0
- package/dist/CredentialRequestClient.d.ts +1 -1
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +9 -6
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/CredentialRequestClientBuilder.d.ts +3 -3
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js +2 -2
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts +46 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js +117 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts +44 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.js +151 -0
- package/dist/CredentialRequestClientV1_0_11.js.map +1 -0
- package/dist/MetadataClient.d.ts +5 -15
- package/dist/MetadataClient.d.ts.map +1 -1
- package/dist/MetadataClient.js +13 -33
- package/dist/MetadataClient.js.map +1 -1
- package/dist/MetadataClientV1_0_11.d.ts +31 -0
- package/dist/MetadataClientV1_0_11.d.ts.map +1 -0
- package/dist/MetadataClientV1_0_11.js +182 -0
- package/dist/MetadataClientV1_0_11.js.map +1 -0
- package/dist/OpenID4VCIClient.d.ts +5 -17
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +3 -74
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/OpenID4VCIClientV1_0_11.d.ts +107 -0
- package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -0
- package/dist/OpenID4VCIClientV1_0_11.js +462 -0
- package/dist/OpenID4VCIClientV1_0_11.js.map +1 -0
- package/dist/functions/OpenIDUtils.d.ts +12 -0
- package/dist/functions/OpenIDUtils.d.ts.map +1 -0
- package/dist/functions/OpenIDUtils.js +37 -0
- package/dist/functions/OpenIDUtils.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/lib/AccessTokenClient.ts +54 -22
- package/lib/AccessTokenClientV1_0_11.ts +255 -0
- package/lib/AuthorizationCodeClient.ts +31 -11
- package/lib/AuthorizationCodeClientV1_0_11.ts +167 -0
- package/lib/CredentialOfferClient.ts +13 -25
- package/lib/CredentialOfferClientV1_0_11.ts +112 -0
- package/lib/CredentialRequestClient.ts +11 -7
- package/lib/CredentialRequestClientBuilder.ts +9 -8
- package/lib/CredentialRequestClientBuilderV1_0_11.ts +156 -0
- package/lib/CredentialRequestClientV1_0_11.ts +190 -0
- package/lib/MetadataClient.ts +26 -48
- package/lib/MetadataClientV1_0_11.ts +186 -0
- package/lib/OpenID4VCIClient.ts +10 -92
- package/lib/OpenID4VCIClientV1_0_11.ts +644 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +34 -6
- package/lib/__tests__/CredentialRequestClient.spec.ts +56 -39
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +4 -4
- package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +316 -0
- package/lib/__tests__/EBSIE2E.spec.test.ts +2 -2
- package/lib/__tests__/IT.spec.ts +222 -11
- package/lib/__tests__/IssuanceInitiation.spec.ts +32 -51
- package/lib/__tests__/IssuanceInitiationV1_0_11.spec.ts +62 -0
- package/lib/__tests__/MattrE2E.spec.test.ts +2 -2
- package/lib/__tests__/MetadataClient.spec.ts +70 -6
- package/lib/__tests__/MetadataMocks.ts +41 -2
- package/lib/__tests__/OpenID4VCIClient.spec.ts +1 -1
- package/lib/__tests__/{OpenID4VCIClientPAR.spec.ts → OpenID4VCIClientPARV1_0_11.spec.ts} +5 -5
- package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +202 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +1 -1
- package/lib/__tests__/SdJwt.spec.ts +31 -21
- package/lib/__tests__/SphereonE2E.spec.test.ts +3 -3
- package/lib/__tests__/data/VciDataFixtures.ts +664 -27
- package/lib/functions/OpenIDUtils.ts +25 -0
- package/lib/index.ts +8 -1
- package/package.json +3 -3
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CredentialOffer,
|
|
3
|
-
CredentialOfferPayload,
|
|
4
|
-
CredentialOfferPayloadV1_0_09,
|
|
5
2
|
CredentialOfferRequestWithBaseUrl,
|
|
6
|
-
|
|
3
|
+
CredentialOfferV1_0_13,
|
|
7
4
|
determineSpecVersionFromURI,
|
|
8
5
|
getClientIdFromCredentialOfferPayload,
|
|
9
6
|
OpenId4VCIVersion,
|
|
@@ -25,24 +22,12 @@ export class CredentialOfferClient {
|
|
|
25
22
|
const scheme = uri.split('://')[0];
|
|
26
23
|
const baseUrl = uri.split('?')[0];
|
|
27
24
|
const version = determineSpecVersionFromURI(uri);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}) as CredentialOfferPayloadV1_0_09;
|
|
35
|
-
credentialOffer = {
|
|
36
|
-
credential_offer: credentialOfferPayload,
|
|
37
|
-
};
|
|
38
|
-
} else {
|
|
39
|
-
credentialOffer = convertURIToJsonObject(uri, {
|
|
40
|
-
arrayTypeProperties: ['credentials'],
|
|
41
|
-
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['credential_offer'],
|
|
42
|
-
}) as CredentialOfferV1_0_11;
|
|
43
|
-
if (credentialOffer?.credential_offer_uri === undefined && !credentialOffer?.credential_offer) {
|
|
44
|
-
throw Error('Either a credential_offer or credential_offer_uri should be present in ' + uri);
|
|
45
|
-
}
|
|
25
|
+
const credentialOffer: CredentialOfferV1_0_13 = convertURIToJsonObject(uri, {
|
|
26
|
+
arrayTypeProperties: ['credential_configuration_ids'],
|
|
27
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['credential_offer'],
|
|
28
|
+
}) as CredentialOfferV1_0_13;
|
|
29
|
+
if (credentialOffer?.credential_offer_uri === undefined && !credentialOffer?.credential_offer) {
|
|
30
|
+
throw Error('Either a credential_offer or credential_offer_uri should be present in ' + uri);
|
|
46
31
|
}
|
|
47
32
|
|
|
48
33
|
const request = await toUniformCredentialOfferRequest(credentialOffer, {
|
|
@@ -55,13 +40,16 @@ export class CredentialOfferClient {
|
|
|
55
40
|
return {
|
|
56
41
|
scheme,
|
|
57
42
|
baseUrl,
|
|
58
|
-
clientId,
|
|
43
|
+
...(clientId && { clientId }),
|
|
59
44
|
...request,
|
|
60
45
|
...(grants?.authorization_code?.issuer_state && { issuerState: grants.authorization_code.issuer_state }),
|
|
61
46
|
...(grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] && {
|
|
62
47
|
preAuthorizedCode: grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'],
|
|
63
48
|
}),
|
|
64
|
-
|
|
49
|
+
...(request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.tx_code &&
|
|
50
|
+
{
|
|
51
|
+
// txCode: request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.tx_code,
|
|
52
|
+
}),
|
|
65
53
|
};
|
|
66
54
|
}
|
|
67
55
|
|
|
@@ -101,7 +89,7 @@ export class CredentialOfferClient {
|
|
|
101
89
|
arrayTypeProperties: isUri ? [] : ['credential_type'],
|
|
102
90
|
uriTypeProperties: isUri
|
|
103
91
|
? ['credential_offer_uri']
|
|
104
|
-
: version >= OpenId4VCIVersion.
|
|
92
|
+
: version >= OpenId4VCIVersion.VER_1_0_13
|
|
105
93
|
? ['credential_issuer', 'credential_type']
|
|
106
94
|
: ['issuer', 'credential_type'],
|
|
107
95
|
param,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialOffer,
|
|
3
|
+
CredentialOfferPayload,
|
|
4
|
+
CredentialOfferPayloadV1_0_09,
|
|
5
|
+
CredentialOfferRequestWithBaseUrl,
|
|
6
|
+
CredentialOfferRequestWithBaseUrlV1_0_11,
|
|
7
|
+
CredentialOfferV1_0_11,
|
|
8
|
+
determineSpecVersionFromURI,
|
|
9
|
+
getClientIdFromCredentialOfferPayload,
|
|
10
|
+
OpenId4VCIVersion,
|
|
11
|
+
toUniformCredentialOfferRequestV1_0_11,
|
|
12
|
+
} from '@sphereon/oid4vci-common';
|
|
13
|
+
import Debug from 'debug';
|
|
14
|
+
|
|
15
|
+
import { convertJsonToURI, convertURIToJsonObject } from './functions';
|
|
16
|
+
|
|
17
|
+
const debug = Debug('sphereon:oid4vci:offer');
|
|
18
|
+
|
|
19
|
+
export class CredentialOfferClientV1_0_11 {
|
|
20
|
+
public static async fromURI(uri: string, opts?: { resolve?: boolean }): Promise<CredentialOfferRequestWithBaseUrlV1_0_11> {
|
|
21
|
+
debug(`Credential Offer URI: ${uri}`);
|
|
22
|
+
if (!uri.includes('?') || !uri.includes('://')) {
|
|
23
|
+
debug(`Invalid Credential Offer URI: ${uri}`);
|
|
24
|
+
throw Error(`Invalid Credential Offer Request`);
|
|
25
|
+
}
|
|
26
|
+
const scheme = uri.split('://')[0];
|
|
27
|
+
const baseUrl = uri.split('?')[0];
|
|
28
|
+
const version = determineSpecVersionFromURI(uri);
|
|
29
|
+
let credentialOffer: CredentialOffer;
|
|
30
|
+
let credentialOfferPayload: CredentialOfferPayload;
|
|
31
|
+
if (version < OpenId4VCIVersion.VER_1_0_11) {
|
|
32
|
+
credentialOfferPayload = convertURIToJsonObject(uri, {
|
|
33
|
+
arrayTypeProperties: ['credential_type'],
|
|
34
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['issuer', 'credential_type'],
|
|
35
|
+
}) as CredentialOfferPayloadV1_0_09;
|
|
36
|
+
credentialOffer = {
|
|
37
|
+
credential_offer: credentialOfferPayload,
|
|
38
|
+
};
|
|
39
|
+
} else {
|
|
40
|
+
credentialOffer = convertURIToJsonObject(uri, {
|
|
41
|
+
arrayTypeProperties: ['credentials'],
|
|
42
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['credential_offer'],
|
|
43
|
+
}) as CredentialOfferV1_0_11;
|
|
44
|
+
if (credentialOffer?.credential_offer_uri === undefined && !credentialOffer?.credential_offer) {
|
|
45
|
+
throw Error('Either a credential_offer or credential_offer_uri should be present in ' + uri);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const request = await toUniformCredentialOfferRequestV1_0_11(credentialOffer, {
|
|
50
|
+
...opts,
|
|
51
|
+
version,
|
|
52
|
+
});
|
|
53
|
+
const clientId = getClientIdFromCredentialOfferPayload(request.credential_offer);
|
|
54
|
+
const grants = request.credential_offer?.grants;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
scheme,
|
|
58
|
+
baseUrl,
|
|
59
|
+
clientId,
|
|
60
|
+
...request,
|
|
61
|
+
...(grants?.authorization_code?.issuer_state && { issuerState: grants.authorization_code.issuer_state }),
|
|
62
|
+
...(grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] && {
|
|
63
|
+
preAuthorizedCode: grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'],
|
|
64
|
+
}),
|
|
65
|
+
userPinRequired: request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.user_pin_required ?? false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static toURI(
|
|
70
|
+
requestWithBaseUrl: CredentialOfferRequestWithBaseUrl,
|
|
71
|
+
opts?: {
|
|
72
|
+
version?: OpenId4VCIVersion;
|
|
73
|
+
},
|
|
74
|
+
): string {
|
|
75
|
+
debug(`Credential Offer Request with base URL: ${JSON.stringify(requestWithBaseUrl)}`);
|
|
76
|
+
const version = opts?.version ?? requestWithBaseUrl.version;
|
|
77
|
+
let baseUrl = requestWithBaseUrl.baseUrl.includes(requestWithBaseUrl.scheme)
|
|
78
|
+
? requestWithBaseUrl.baseUrl
|
|
79
|
+
: `${requestWithBaseUrl.scheme.replace('://', '')}://${requestWithBaseUrl.baseUrl}`;
|
|
80
|
+
let param: string | undefined;
|
|
81
|
+
|
|
82
|
+
const isUri = requestWithBaseUrl.credential_offer_uri !== undefined;
|
|
83
|
+
|
|
84
|
+
if (version.valueOf() >= OpenId4VCIVersion.VER_1_0_11.valueOf()) {
|
|
85
|
+
// v11 changed from encoding every param to a encoded json object with a credential_offer param key
|
|
86
|
+
if (!baseUrl.includes('?')) {
|
|
87
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
88
|
+
} else {
|
|
89
|
+
const split = baseUrl.split('?');
|
|
90
|
+
if (split.length > 1 && split[1] !== '') {
|
|
91
|
+
if (baseUrl.endsWith('&')) {
|
|
92
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
93
|
+
} else if (!baseUrl.endsWith('=')) {
|
|
94
|
+
baseUrl += `&`;
|
|
95
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return convertJsonToURI(requestWithBaseUrl.credential_offer_uri ?? requestWithBaseUrl.original_credential_offer, {
|
|
101
|
+
baseUrl,
|
|
102
|
+
arrayTypeProperties: isUri ? [] : ['credential_type'],
|
|
103
|
+
uriTypeProperties: isUri
|
|
104
|
+
? ['credential_offer_uri']
|
|
105
|
+
: version >= OpenId4VCIVersion.VER_1_0_11
|
|
106
|
+
? ['credential_issuer', 'credential_type']
|
|
107
|
+
: ['issuer', 'credential_type'],
|
|
108
|
+
param,
|
|
109
|
+
version,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
acquireDeferredCredential,
|
|
3
|
+
CredentialRequestV1_0_13,
|
|
3
4
|
CredentialResponse,
|
|
4
5
|
getCredentialRequestForVersion,
|
|
5
6
|
getUniformFormat,
|
|
@@ -15,8 +16,8 @@ import { CredentialFormat } from '@sphereon/ssi-types';
|
|
|
15
16
|
import Debug from 'debug';
|
|
16
17
|
|
|
17
18
|
import { CredentialRequestClientBuilder } from './CredentialRequestClientBuilder';
|
|
18
|
-
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
19
19
|
import { isValidURL, post } from './functions';
|
|
20
|
+
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
20
21
|
|
|
21
22
|
const debug = Debug('sphereon:oid4vci:credential');
|
|
22
23
|
|
|
@@ -41,7 +42,7 @@ export async function buildProof<DIDDoc>(
|
|
|
41
42
|
) {
|
|
42
43
|
if ('proof_type' in proofInput) {
|
|
43
44
|
if (opts.cNonce) {
|
|
44
|
-
throw Error(`Cnonce param is only supported when using a Proof of
|
|
45
|
+
throw Error(`Cnonce param is only supported when using a Proof of possession builder`);
|
|
45
46
|
}
|
|
46
47
|
return await ProofOfPossessionBuilder.fromProof(proofInput as ProofOfPossession, opts.version).build();
|
|
47
48
|
}
|
|
@@ -88,7 +89,10 @@ export class CredentialRequestClient {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
public async acquireCredentialsUsingRequest(uniformRequest: UniformCredentialRequest): Promise<OpenIDResponse<CredentialResponse>> {
|
|
91
|
-
|
|
92
|
+
if (this.version() < OpenId4VCIVersion.VER_1_0_13) {
|
|
93
|
+
throw new Error('Versions below v1.0.13 (draft 13) are not supported.');
|
|
94
|
+
}
|
|
95
|
+
const request: CredentialRequestV1_0_13 = getCredentialRequestForVersion(uniformRequest, this.version()) as CredentialRequestV1_0_13;
|
|
92
96
|
const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
|
|
93
97
|
if (!isValidURL(credentialEndpoint)) {
|
|
94
98
|
debug(`Invalid credential endpoint: ${credentialEndpoint}`);
|
|
@@ -154,7 +158,7 @@ export class CredentialRequestClient {
|
|
|
154
158
|
throw Error(`Credential type(s) need to be provided`);
|
|
155
159
|
}
|
|
156
160
|
// FIXME: this is mixing up the type (as id) from v8/v9 and the types (from the vc.type) from v11
|
|
157
|
-
else if (!this.
|
|
161
|
+
else if (!this.isV13OrHigher() && types.length !== 1) {
|
|
158
162
|
throw Error('Only a single credential type is supported for V8/V9');
|
|
159
163
|
}
|
|
160
164
|
const proof = await buildProof(proofInput, opts);
|
|
@@ -199,10 +203,10 @@ export class CredentialRequestClient {
|
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
private version(): OpenId4VCIVersion {
|
|
202
|
-
return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.
|
|
206
|
+
return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_13;
|
|
203
207
|
}
|
|
204
208
|
|
|
205
|
-
private
|
|
206
|
-
return this.version() >= OpenId4VCIVersion.
|
|
209
|
+
private isV13OrHigher(): boolean {
|
|
210
|
+
return this.version() >= OpenId4VCIVersion.VER_1_0_13;
|
|
207
211
|
}
|
|
208
212
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AccessTokenResponse,
|
|
3
|
-
|
|
3
|
+
CredentialIssuerMetadataV1_0_13,
|
|
4
4
|
CredentialOfferPayloadV1_0_08,
|
|
5
|
+
CredentialOfferPayloadV1_0_11,
|
|
5
6
|
CredentialOfferRequestWithBaseUrl,
|
|
6
7
|
determineSpecVersionFromOffer,
|
|
7
8
|
EndpointMetadata,
|
|
8
9
|
getIssuerFromCredentialOfferPayload,
|
|
9
|
-
|
|
10
|
+
getTypesFromOfferV1_0_11,
|
|
10
11
|
OID4VCICredentialFormat,
|
|
11
12
|
OpenId4VCIVersion,
|
|
12
|
-
UniformCredentialOfferRequest
|
|
13
|
-
} from '@sphereon/oid4vci-common'
|
|
13
|
+
UniformCredentialOfferRequest
|
|
14
|
+
} from '@sphereon/oid4vci-common'
|
|
14
15
|
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
15
16
|
|
|
16
17
|
import { CredentialOfferClient } from './CredentialOfferClient';
|
|
@@ -73,9 +74,9 @@ export class CredentialRequestClientBuilder {
|
|
|
73
74
|
if (version <= OpenId4VCIVersion.VER_1_0_08) {
|
|
74
75
|
//todo: This basically sets all types available during initiation. Probably the user only wants a subset. So do we want to do this?
|
|
75
76
|
builder.withCredentialType((request.original_credential_offer as CredentialOfferPayloadV1_0_08).credential_type);
|
|
76
|
-
} else {
|
|
77
|
+
} else if (version <= OpenId4VCIVersion.VER_1_0_11) {
|
|
77
78
|
// todo: look whether this is correct
|
|
78
|
-
builder.withCredentialType(
|
|
79
|
+
builder.withCredentialType(getTypesFromOfferV1_0_11(request.credential_offer as CredentialOfferPayloadV1_0_11));
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
return builder;
|
|
@@ -95,7 +96,7 @@ export class CredentialRequestClientBuilder {
|
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
public withCredentialEndpointFromMetadata(metadata:
|
|
99
|
+
public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadataV1_0_13): this {
|
|
99
100
|
this.credentialEndpoint = metadata.credential_endpoint;
|
|
100
101
|
return this;
|
|
101
102
|
}
|
|
@@ -105,7 +106,7 @@ export class CredentialRequestClientBuilder {
|
|
|
105
106
|
return this;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
public withDeferredCredentialEndpointFromMetadata(metadata:
|
|
109
|
+
public withDeferredCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadataV1_0_13): this {
|
|
109
110
|
this.deferredCredentialEndpoint = metadata.deferred_credential_endpoint;
|
|
110
111
|
return this;
|
|
111
112
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccessTokenResponse,
|
|
3
|
+
CredentialIssuerMetadata,
|
|
4
|
+
CredentialOfferPayloadV1_0_08,
|
|
5
|
+
CredentialOfferPayloadV1_0_11,
|
|
6
|
+
CredentialOfferRequestWithBaseUrl,
|
|
7
|
+
determineSpecVersionFromOffer,
|
|
8
|
+
EndpointMetadata,
|
|
9
|
+
getIssuerFromCredentialOfferPayload,
|
|
10
|
+
getTypesFromOfferV1_0_11,
|
|
11
|
+
OID4VCICredentialFormat,
|
|
12
|
+
OpenId4VCIVersion,
|
|
13
|
+
UniformCredentialOfferRequest,
|
|
14
|
+
} from '@sphereon/oid4vci-common';
|
|
15
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
16
|
+
|
|
17
|
+
import { CredentialOfferClientV1_0_11 } from './CredentialOfferClientV1_0_11';
|
|
18
|
+
import { CredentialRequestClientV1_0_11 } from './CredentialRequestClientV1_0_11';
|
|
19
|
+
|
|
20
|
+
export class CredentialRequestClientBuilderV1_0_11 {
|
|
21
|
+
credentialEndpoint?: string;
|
|
22
|
+
deferredCredentialEndpoint?: string;
|
|
23
|
+
deferredCredentialAwait = false;
|
|
24
|
+
deferredCredentialIntervalInMS = 5000;
|
|
25
|
+
credentialTypes: string[] = [];
|
|
26
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
27
|
+
token?: string;
|
|
28
|
+
version?: OpenId4VCIVersion;
|
|
29
|
+
|
|
30
|
+
public static fromCredentialIssuer({
|
|
31
|
+
credentialIssuer,
|
|
32
|
+
metadata,
|
|
33
|
+
version,
|
|
34
|
+
credentialTypes,
|
|
35
|
+
}: {
|
|
36
|
+
credentialIssuer: string;
|
|
37
|
+
metadata?: EndpointMetadata;
|
|
38
|
+
version?: OpenId4VCIVersion;
|
|
39
|
+
credentialTypes: string | string[];
|
|
40
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
41
|
+
const issuer = credentialIssuer;
|
|
42
|
+
const builder = new CredentialRequestClientBuilderV1_0_11();
|
|
43
|
+
builder.withVersion(version ?? OpenId4VCIVersion.VER_1_0_11);
|
|
44
|
+
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
|
|
45
|
+
if (metadata?.deferred_credential_endpoint) {
|
|
46
|
+
builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
|
|
47
|
+
}
|
|
48
|
+
builder.withCredentialType(credentialTypes);
|
|
49
|
+
return builder;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise<CredentialRequestClientBuilderV1_0_11> {
|
|
53
|
+
const offer = await CredentialOfferClientV1_0_11.fromURI(uri);
|
|
54
|
+
return CredentialRequestClientBuilderV1_0_11.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public static fromCredentialOfferRequest(opts: {
|
|
58
|
+
request: UniformCredentialOfferRequest;
|
|
59
|
+
scheme?: string;
|
|
60
|
+
baseUrl?: string;
|
|
61
|
+
version?: OpenId4VCIVersion;
|
|
62
|
+
metadata?: EndpointMetadata;
|
|
63
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
64
|
+
const { request, metadata } = opts;
|
|
65
|
+
const version = opts.version ?? request.version ?? determineSpecVersionFromOffer(request.original_credential_offer);
|
|
66
|
+
const builder = new CredentialRequestClientBuilderV1_0_11();
|
|
67
|
+
const issuer = getIssuerFromCredentialOfferPayload(request.credential_offer) ?? (metadata?.issuer as string);
|
|
68
|
+
builder.withVersion(version);
|
|
69
|
+
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
|
|
70
|
+
if (metadata?.deferred_credential_endpoint) {
|
|
71
|
+
builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (version <= OpenId4VCIVersion.VER_1_0_08) {
|
|
75
|
+
//todo: This basically sets all types available during initiation. Probably the user only wants a subset. So do we want to do this?
|
|
76
|
+
builder.withCredentialType((request.original_credential_offer as CredentialOfferPayloadV1_0_08).credential_type);
|
|
77
|
+
} else if (version <= OpenId4VCIVersion.VER_1_0_11) {
|
|
78
|
+
// todo: look whether this is correct
|
|
79
|
+
builder.withCredentialType(getTypesFromOfferV1_0_11(request.credential_offer as CredentialOfferPayloadV1_0_11));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return builder;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public static fromCredentialOffer({
|
|
86
|
+
credentialOffer,
|
|
87
|
+
metadata,
|
|
88
|
+
}: {
|
|
89
|
+
credentialOffer: CredentialOfferRequestWithBaseUrl;
|
|
90
|
+
metadata?: EndpointMetadata;
|
|
91
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
92
|
+
return CredentialRequestClientBuilderV1_0_11.fromCredentialOfferRequest({
|
|
93
|
+
request: credentialOffer,
|
|
94
|
+
metadata,
|
|
95
|
+
version: credentialOffer.version,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
|
|
100
|
+
this.credentialEndpoint = metadata.credential_endpoint;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public withCredentialEndpoint(credentialEndpoint: string): this {
|
|
105
|
+
this.credentialEndpoint = credentialEndpoint;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public withDeferredCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
|
|
110
|
+
this.deferredCredentialEndpoint = metadata.deferred_credential_endpoint;
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public withDeferredCredentialEndpoint(deferredCredentialEndpoint: string): this {
|
|
115
|
+
this.deferredCredentialEndpoint = deferredCredentialEndpoint;
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public withDeferredCredentialAwait(deferredCredentialAwait: boolean, deferredCredentialIntervalInMS?: number): this {
|
|
120
|
+
this.deferredCredentialAwait = deferredCredentialAwait;
|
|
121
|
+
this.deferredCredentialIntervalInMS = deferredCredentialIntervalInMS ?? 5000;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public withCredentialType(credentialTypes: string | string[]): this {
|
|
126
|
+
this.credentialTypes = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public withFormat(format: CredentialFormat | OID4VCICredentialFormat): this {
|
|
131
|
+
this.format = format;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public withToken(accessToken: string): this {
|
|
136
|
+
this.token = accessToken;
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public withTokenFromResponse(response: AccessTokenResponse): this {
|
|
141
|
+
this.token = response.access_token;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public withVersion(version: OpenId4VCIVersion): this {
|
|
146
|
+
this.version = version;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public build(): CredentialRequestClientV1_0_11 {
|
|
151
|
+
if (!this.version) {
|
|
152
|
+
this.withVersion(OpenId4VCIVersion.VER_1_0_11);
|
|
153
|
+
}
|
|
154
|
+
return new CredentialRequestClientV1_0_11(this);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acquireDeferredCredential,
|
|
3
|
+
CredentialResponse,
|
|
4
|
+
getCredentialRequestForVersion,
|
|
5
|
+
getUniformFormat,
|
|
6
|
+
isDeferredCredentialResponse,
|
|
7
|
+
OID4VCICredentialFormat,
|
|
8
|
+
OpenId4VCIVersion,
|
|
9
|
+
OpenIDResponse,
|
|
10
|
+
ProofOfPossession,
|
|
11
|
+
UniformCredentialRequest,
|
|
12
|
+
URL_NOT_VALID,
|
|
13
|
+
} from '@sphereon/oid4vci-common';
|
|
14
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
15
|
+
import Debug from 'debug';
|
|
16
|
+
|
|
17
|
+
import { buildProof } from './CredentialRequestClient';
|
|
18
|
+
import { CredentialRequestClientBuilderV1_0_11 } from './CredentialRequestClientBuilderV1_0_11';
|
|
19
|
+
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
20
|
+
import { isValidURL, post } from './functions';
|
|
21
|
+
|
|
22
|
+
const debug = Debug('sphereon:oid4vci:credential');
|
|
23
|
+
|
|
24
|
+
export interface CredentialRequestOptsV1_0_11 {
|
|
25
|
+
deferredCredentialAwait?: boolean;
|
|
26
|
+
deferredCredentialIntervalInMS?: number;
|
|
27
|
+
credentialEndpoint: string;
|
|
28
|
+
deferredCredentialEndpoint?: string;
|
|
29
|
+
credentialTypes: string[];
|
|
30
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
31
|
+
proof: ProofOfPossession;
|
|
32
|
+
token: string;
|
|
33
|
+
version: OpenId4VCIVersion;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class CredentialRequestClientV1_0_11 {
|
|
37
|
+
private readonly _credentialRequestOpts: Partial<CredentialRequestOptsV1_0_11>;
|
|
38
|
+
private _isDeferred = false;
|
|
39
|
+
|
|
40
|
+
get credentialRequestOpts(): CredentialRequestOptsV1_0_11 {
|
|
41
|
+
return this._credentialRequestOpts as CredentialRequestOptsV1_0_11;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public isDeferred(): boolean {
|
|
45
|
+
return this._isDeferred;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public getCredentialEndpoint(): string {
|
|
49
|
+
return this.credentialRequestOpts.credentialEndpoint;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public getDeferredCredentialEndpoint(): string | undefined {
|
|
53
|
+
return this.credentialRequestOpts.deferredCredentialEndpoint;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public constructor(builder: CredentialRequestClientBuilderV1_0_11) {
|
|
57
|
+
this._credentialRequestOpts = { ...builder };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async acquireCredentialsUsingProof<DIDDoc>(opts: {
|
|
61
|
+
proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
|
|
62
|
+
credentialTypes?: string | string[];
|
|
63
|
+
context?: string[];
|
|
64
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
65
|
+
}): Promise<OpenIDResponse<CredentialResponse>> {
|
|
66
|
+
const { credentialTypes, proofInput, format, context } = opts;
|
|
67
|
+
|
|
68
|
+
const request = await this.createCredentialRequest({ proofInput, credentialTypes, context, format, version: this.version() });
|
|
69
|
+
return await this.acquireCredentialsUsingRequest(request);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public async acquireCredentialsUsingRequest(uniformRequest: UniformCredentialRequest): Promise<OpenIDResponse<CredentialResponse>> {
|
|
73
|
+
const request = getCredentialRequestForVersion(uniformRequest, this.version());
|
|
74
|
+
const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
|
|
75
|
+
if (!isValidURL(credentialEndpoint)) {
|
|
76
|
+
debug(`Invalid credential endpoint: ${credentialEndpoint}`);
|
|
77
|
+
throw new Error(URL_NOT_VALID);
|
|
78
|
+
}
|
|
79
|
+
debug(`Acquiring credential(s) from: ${credentialEndpoint}`);
|
|
80
|
+
debug(`request\n: ${JSON.stringify(request, null, 2)}`);
|
|
81
|
+
const requestToken: string = this.credentialRequestOpts.token;
|
|
82
|
+
let response: OpenIDResponse<CredentialResponse> = await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken });
|
|
83
|
+
this._isDeferred = isDeferredCredentialResponse(response);
|
|
84
|
+
if (this.isDeferred() && this.credentialRequestOpts.deferredCredentialAwait && response.successBody) {
|
|
85
|
+
response = await this.acquireDeferredCredential(response.successBody, { bearerToken: this.credentialRequestOpts.token });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`);
|
|
89
|
+
return response;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public async acquireDeferredCredential(
|
|
93
|
+
response: Pick<CredentialResponse, 'transaction_id' | 'acceptance_token' | 'c_nonce'>,
|
|
94
|
+
opts?: {
|
|
95
|
+
bearerToken?: string;
|
|
96
|
+
},
|
|
97
|
+
): Promise<OpenIDResponse<CredentialResponse>> {
|
|
98
|
+
const transactionId = response.transaction_id;
|
|
99
|
+
const bearerToken = response.acceptance_token ?? opts?.bearerToken;
|
|
100
|
+
const deferredCredentialEndpoint = this.getDeferredCredentialEndpoint();
|
|
101
|
+
if (!deferredCredentialEndpoint) {
|
|
102
|
+
throw Error(`No deferred credential endpoint supplied.`);
|
|
103
|
+
} else if (!bearerToken) {
|
|
104
|
+
throw Error(`No bearer token present and refresh for defered endpoint not supported yet`);
|
|
105
|
+
// todo updated bearer token with new c_nonce
|
|
106
|
+
}
|
|
107
|
+
return await acquireDeferredCredential({
|
|
108
|
+
bearerToken,
|
|
109
|
+
transactionId,
|
|
110
|
+
deferredCredentialEndpoint,
|
|
111
|
+
deferredCredentialAwait: this.credentialRequestOpts.deferredCredentialAwait,
|
|
112
|
+
deferredCredentialIntervalInMS: this.credentialRequestOpts.deferredCredentialIntervalInMS,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async createCredentialRequest<DIDDoc>(opts: {
|
|
117
|
+
proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
|
|
118
|
+
credentialTypes?: string | string[];
|
|
119
|
+
context?: string[];
|
|
120
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
121
|
+
version: OpenId4VCIVersion;
|
|
122
|
+
}): Promise<UniformCredentialRequest> {
|
|
123
|
+
const { proofInput } = opts;
|
|
124
|
+
const formatSelection = opts.format ?? this.credentialRequestOpts.format;
|
|
125
|
+
|
|
126
|
+
if (!formatSelection) {
|
|
127
|
+
throw Error(`Format of credential to be issued is missing`);
|
|
128
|
+
}
|
|
129
|
+
const format = getUniformFormat(formatSelection);
|
|
130
|
+
const typesSelection =
|
|
131
|
+
opts?.credentialTypes && (typeof opts.credentialTypes === 'string' || opts.credentialTypes.length > 0)
|
|
132
|
+
? opts.credentialTypes
|
|
133
|
+
: this.credentialRequestOpts.credentialTypes;
|
|
134
|
+
const types = Array.isArray(typesSelection) ? typesSelection : [typesSelection];
|
|
135
|
+
if (types.length === 0) {
|
|
136
|
+
throw Error(`Credential type(s) need to be provided`);
|
|
137
|
+
}
|
|
138
|
+
// FIXME: this is mixing up the type (as id) from v8/v9 and the types (from the vc.type) from v11
|
|
139
|
+
else if (!this.isV11OrHigher() && types.length !== 1) {
|
|
140
|
+
throw Error('Only a single credential type is supported for V8/V9');
|
|
141
|
+
}
|
|
142
|
+
const proof = await buildProof(proofInput, opts);
|
|
143
|
+
|
|
144
|
+
// TODO: we should move format specific logic
|
|
145
|
+
if (format === 'jwt_vc_json' || format === 'jwt_vc') {
|
|
146
|
+
return {
|
|
147
|
+
types,
|
|
148
|
+
format,
|
|
149
|
+
proof,
|
|
150
|
+
};
|
|
151
|
+
} else if (format === 'jwt_vc_json-ld' || format === 'ldp_vc') {
|
|
152
|
+
if (this.version() >= OpenId4VCIVersion.VER_1_0_12 && !opts.context) {
|
|
153
|
+
throw Error('No @context value present, but it is required');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
format,
|
|
158
|
+
proof,
|
|
159
|
+
|
|
160
|
+
// Ignored because v11 does not have the context value, but it is required in v12
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
credential_definition: {
|
|
164
|
+
types,
|
|
165
|
+
...(opts.context && { '@context': opts.context }),
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
} else if (format === 'vc+sd-jwt') {
|
|
169
|
+
if (types.length > 1) {
|
|
170
|
+
throw Error(`Only a single credential type is supported for ${format}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
format,
|
|
175
|
+
proof,
|
|
176
|
+
vct: types[0],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private version(): OpenId4VCIVersion {
|
|
184
|
+
return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_11;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private isV11OrHigher(): boolean {
|
|
188
|
+
return this.version() >= OpenId4VCIVersion.VER_1_0_11;
|
|
189
|
+
}
|
|
190
|
+
}
|