@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.
Files changed (98) hide show
  1. package/dist/AccessTokenClient.d.ts +5 -5
  2. package/dist/AccessTokenClient.d.ts.map +1 -1
  3. package/dist/AccessTokenClient.js +42 -22
  4. package/dist/AccessTokenClient.js.map +1 -1
  5. package/dist/AccessTokenClientV1_0_11.d.ts +29 -0
  6. package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -0
  7. package/dist/AccessTokenClientV1_0_11.js +212 -0
  8. package/dist/AccessTokenClientV1_0_11.js.map +1 -0
  9. package/dist/AuthorizationCodeClient.d.ts +4 -4
  10. package/dist/AuthorizationCodeClient.d.ts.map +1 -1
  11. package/dist/AuthorizationCodeClient.js +14 -3
  12. package/dist/AuthorizationCodeClient.js.map +1 -1
  13. package/dist/AuthorizationCodeClientV1_0_11.d.ts +9 -0
  14. package/dist/AuthorizationCodeClientV1_0_11.d.ts.map +1 -0
  15. package/dist/AuthorizationCodeClientV1_0_11.js +132 -0
  16. package/dist/AuthorizationCodeClientV1_0_11.js.map +1 -0
  17. package/dist/CredentialOfferClient.d.ts.map +1 -1
  18. package/dist/CredentialOfferClient.js +14 -25
  19. package/dist/CredentialOfferClient.js.map +1 -1
  20. package/dist/CredentialOfferClientV1_0_11.d.ts +10 -0
  21. package/dist/CredentialOfferClientV1_0_11.d.ts.map +1 -0
  22. package/dist/CredentialOfferClientV1_0_11.js +103 -0
  23. package/dist/CredentialOfferClientV1_0_11.js.map +1 -0
  24. package/dist/CredentialRequestClient.d.ts +1 -1
  25. package/dist/CredentialRequestClient.d.ts.map +1 -1
  26. package/dist/CredentialRequestClient.js +9 -6
  27. package/dist/CredentialRequestClient.js.map +1 -1
  28. package/dist/CredentialRequestClientBuilder.d.ts +3 -3
  29. package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
  30. package/dist/CredentialRequestClientBuilder.js +2 -2
  31. package/dist/CredentialRequestClientBuilder.js.map +1 -1
  32. package/dist/CredentialRequestClientBuilderV1_0_11.d.ts +46 -0
  33. package/dist/CredentialRequestClientBuilderV1_0_11.d.ts.map +1 -0
  34. package/dist/CredentialRequestClientBuilderV1_0_11.js +117 -0
  35. package/dist/CredentialRequestClientBuilderV1_0_11.js.map +1 -0
  36. package/dist/CredentialRequestClientV1_0_11.d.ts +44 -0
  37. package/dist/CredentialRequestClientV1_0_11.d.ts.map +1 -0
  38. package/dist/CredentialRequestClientV1_0_11.js +151 -0
  39. package/dist/CredentialRequestClientV1_0_11.js.map +1 -0
  40. package/dist/MetadataClient.d.ts +5 -15
  41. package/dist/MetadataClient.d.ts.map +1 -1
  42. package/dist/MetadataClient.js +13 -33
  43. package/dist/MetadataClient.js.map +1 -1
  44. package/dist/MetadataClientV1_0_11.d.ts +31 -0
  45. package/dist/MetadataClientV1_0_11.d.ts.map +1 -0
  46. package/dist/MetadataClientV1_0_11.js +182 -0
  47. package/dist/MetadataClientV1_0_11.js.map +1 -0
  48. package/dist/OpenID4VCIClient.d.ts +5 -17
  49. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  50. package/dist/OpenID4VCIClient.js +3 -74
  51. package/dist/OpenID4VCIClient.js.map +1 -1
  52. package/dist/OpenID4VCIClientV1_0_11.d.ts +107 -0
  53. package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -0
  54. package/dist/OpenID4VCIClientV1_0_11.js +462 -0
  55. package/dist/OpenID4VCIClientV1_0_11.js.map +1 -0
  56. package/dist/functions/OpenIDUtils.d.ts +12 -0
  57. package/dist/functions/OpenIDUtils.d.ts.map +1 -0
  58. package/dist/functions/OpenIDUtils.js +37 -0
  59. package/dist/functions/OpenIDUtils.js.map +1 -0
  60. package/dist/index.d.ts +8 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +8 -1
  63. package/dist/index.js.map +1 -1
  64. package/lib/AccessTokenClient.ts +54 -22
  65. package/lib/AccessTokenClientV1_0_11.ts +255 -0
  66. package/lib/AuthorizationCodeClient.ts +31 -11
  67. package/lib/AuthorizationCodeClientV1_0_11.ts +167 -0
  68. package/lib/CredentialOfferClient.ts +13 -25
  69. package/lib/CredentialOfferClientV1_0_11.ts +112 -0
  70. package/lib/CredentialRequestClient.ts +11 -7
  71. package/lib/CredentialRequestClientBuilder.ts +9 -8
  72. package/lib/CredentialRequestClientBuilderV1_0_11.ts +156 -0
  73. package/lib/CredentialRequestClientV1_0_11.ts +190 -0
  74. package/lib/MetadataClient.ts +26 -48
  75. package/lib/MetadataClientV1_0_11.ts +186 -0
  76. package/lib/OpenID4VCIClient.ts +10 -92
  77. package/lib/OpenID4VCIClientV1_0_11.ts +644 -0
  78. package/lib/__tests__/AccessTokenClient.spec.ts +34 -6
  79. package/lib/__tests__/CredentialRequestClient.spec.ts +56 -39
  80. package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +4 -4
  81. package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +316 -0
  82. package/lib/__tests__/EBSIE2E.spec.test.ts +2 -2
  83. package/lib/__tests__/IT.spec.ts +222 -11
  84. package/lib/__tests__/IssuanceInitiation.spec.ts +32 -51
  85. package/lib/__tests__/IssuanceInitiationV1_0_11.spec.ts +62 -0
  86. package/lib/__tests__/MattrE2E.spec.test.ts +2 -2
  87. package/lib/__tests__/MetadataClient.spec.ts +70 -6
  88. package/lib/__tests__/MetadataMocks.ts +41 -2
  89. package/lib/__tests__/OpenID4VCIClient.spec.ts +1 -1
  90. package/lib/__tests__/{OpenID4VCIClientPAR.spec.ts → OpenID4VCIClientPARV1_0_11.spec.ts} +5 -5
  91. package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +202 -0
  92. package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +1 -1
  93. package/lib/__tests__/SdJwt.spec.ts +31 -21
  94. package/lib/__tests__/SphereonE2E.spec.test.ts +3 -3
  95. package/lib/__tests__/data/VciDataFixtures.ts +664 -27
  96. package/lib/functions/OpenIDUtils.ts +25 -0
  97. package/lib/index.ts +8 -1
  98. package/package.json +3 -3
@@ -1,9 +1,6 @@
1
1
  import {
2
- CredentialOffer,
3
- CredentialOfferPayload,
4
- CredentialOfferPayloadV1_0_09,
5
2
  CredentialOfferRequestWithBaseUrl,
6
- CredentialOfferV1_0_11,
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
- let credentialOffer: CredentialOffer;
29
- let credentialOfferPayload: CredentialOfferPayload;
30
- if (version < OpenId4VCIVersion.VER_1_0_11) {
31
- credentialOfferPayload = convertURIToJsonObject(uri, {
32
- arrayTypeProperties: ['credential_type'],
33
- requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['issuer', 'credential_type'],
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
- userPinRequired: request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.user_pin_required ?? false,
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.VER_1_0_11
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 Posession builder`);
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
- const request = getCredentialRequestForVersion(uniformRequest, this.version());
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.isV11OrHigher() && types.length !== 1) {
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.VER_1_0_11;
206
+ return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_13;
203
207
  }
204
208
 
205
- private isV11OrHigher(): boolean {
206
- return this.version() >= OpenId4VCIVersion.VER_1_0_11;
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
- CredentialIssuerMetadata,
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
- getTypesFromOffer,
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(getTypesFromOffer(request.credential_offer));
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: CredentialIssuerMetadata): this {
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: CredentialIssuerMetadata): this {
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
+ }