@sphereon/oid4vci-client 0.10.4-unstable.98 → 0.12.0

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 (87) hide show
  1. package/README.md +7 -7
  2. package/dist/AccessTokenClient.d.ts.map +1 -1
  3. package/dist/AccessTokenClient.js +5 -6
  4. package/dist/AccessTokenClient.js.map +1 -1
  5. package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -1
  6. package/dist/AccessTokenClientV1_0_11.js +3 -5
  7. package/dist/AccessTokenClientV1_0_11.js.map +1 -1
  8. package/dist/AuthorizationCodeClient.d.ts +4 -1
  9. package/dist/AuthorizationCodeClient.d.ts.map +1 -1
  10. package/dist/AuthorizationCodeClient.js +47 -8
  11. package/dist/AuthorizationCodeClient.js.map +1 -1
  12. package/dist/AuthorizationCodeClientV1_0_11.d.ts.map +1 -1
  13. package/dist/AuthorizationCodeClientV1_0_11.js +5 -3
  14. package/dist/AuthorizationCodeClientV1_0_11.js.map +1 -1
  15. package/dist/CredentialOfferClient.d.ts.map +1 -1
  16. package/dist/CredentialOfferClient.js +26 -12
  17. package/dist/CredentialOfferClient.js.map +1 -1
  18. package/dist/CredentialOfferClientV1_0_11.js +6 -7
  19. package/dist/CredentialOfferClientV1_0_11.js.map +1 -1
  20. package/dist/CredentialOfferClientV1_0_13.d.ts +10 -0
  21. package/dist/CredentialOfferClientV1_0_13.d.ts.map +1 -0
  22. package/dist/CredentialOfferClientV1_0_13.js +94 -0
  23. package/dist/CredentialOfferClientV1_0_13.js.map +1 -0
  24. package/dist/CredentialRequestClient.js +1 -1
  25. package/dist/CredentialRequestClient.js.map +1 -1
  26. package/dist/CredentialRequestClientBuilderV1_0_11.d.ts +3 -1
  27. package/dist/CredentialRequestClientBuilderV1_0_11.d.ts.map +1 -1
  28. package/dist/CredentialRequestClientBuilderV1_0_11.js +4 -0
  29. package/dist/CredentialRequestClientBuilderV1_0_11.js.map +1 -1
  30. package/dist/CredentialRequestClientV1_0_11.d.ts +9 -3
  31. package/dist/CredentialRequestClientV1_0_11.d.ts.map +1 -1
  32. package/dist/CredentialRequestClientV1_0_11.js +2 -1
  33. package/dist/CredentialRequestClientV1_0_11.js.map +1 -1
  34. package/dist/MetadataClient.d.ts +5 -5
  35. package/dist/MetadataClient.d.ts.map +1 -1
  36. package/dist/MetadataClient.js +30 -13
  37. package/dist/MetadataClient.js.map +1 -1
  38. package/dist/MetadataClientV1_0_13.d.ts +31 -0
  39. package/dist/MetadataClientV1_0_13.d.ts.map +1 -0
  40. package/dist/MetadataClientV1_0_13.js +181 -0
  41. package/dist/MetadataClientV1_0_13.js.map +1 -0
  42. package/dist/OpenID4VCIClient.d.ts +13 -24
  43. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  44. package/dist/OpenID4VCIClient.js +126 -103
  45. package/dist/OpenID4VCIClient.js.map +1 -1
  46. package/dist/OpenID4VCIClientV1_0_11.d.ts +3 -2
  47. package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -1
  48. package/dist/OpenID4VCIClientV1_0_11.js +5 -18
  49. package/dist/OpenID4VCIClientV1_0_11.js.map +1 -1
  50. package/dist/OpenID4VCIClientV1_0_13.d.ts +112 -0
  51. package/dist/OpenID4VCIClientV1_0_13.d.ts.map +1 -0
  52. package/dist/OpenID4VCIClientV1_0_13.js +478 -0
  53. package/dist/OpenID4VCIClientV1_0_13.js.map +1 -0
  54. package/dist/ProofOfPossessionBuilder.d.ts +14 -3
  55. package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
  56. package/dist/ProofOfPossessionBuilder.js +20 -20
  57. package/dist/ProofOfPossessionBuilder.js.map +1 -1
  58. package/dist/index.d.ts +5 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +6 -0
  61. package/dist/index.js.map +1 -1
  62. package/lib/AccessTokenClient.ts +5 -11
  63. package/lib/AccessTokenClientV1_0_11.ts +3 -9
  64. package/lib/AuthorizationCodeClient.ts +47 -8
  65. package/lib/AuthorizationCodeClientV1_0_11.ts +8 -6
  66. package/lib/CredentialOfferClient.ts +31 -9
  67. package/lib/CredentialOfferClientV1_0_11.ts +6 -6
  68. package/lib/CredentialOfferClientV1_0_13.ts +103 -0
  69. package/lib/CredentialRequestClient.ts +1 -1
  70. package/lib/CredentialRequestClientBuilderV1_0_11.ts +7 -0
  71. package/lib/CredentialRequestClientV1_0_11.ts +9 -4
  72. package/lib/MetadataClient.ts +49 -14
  73. package/lib/MetadataClientV1_0_13.ts +188 -0
  74. package/lib/OpenID4VCIClient.ts +131 -115
  75. package/lib/OpenID4VCIClientV1_0_11.ts +9 -19
  76. package/lib/OpenID4VCIClientV1_0_13.ts +677 -0
  77. package/lib/ProofOfPossessionBuilder.ts +40 -9
  78. package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +2 -2
  79. package/lib/__tests__/MetadataClient.spec.ts +3 -4
  80. package/lib/__tests__/MetadataMocks.ts +1 -0
  81. package/lib/__tests__/OpenID4VCIClient.spec.ts +42 -9
  82. package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +24 -0
  83. package/lib/__tests__/OpenID4VCIClientV1_0_13.spec.ts +204 -0
  84. package/lib/__tests__/SdJwt.spec.ts +2 -2
  85. package/lib/__tests__/SphereonE2E.spec.test.ts +4 -3
  86. package/lib/index.ts +8 -0
  87. package/package.json +4 -4
@@ -7,6 +7,7 @@ import {
7
7
  Jwt,
8
8
  NO_JWT_PROVIDED,
9
9
  OpenId4VCIVersion,
10
+ PoPMode,
10
11
  PROOF_CANT_BE_CONSTRUCTED,
11
12
  ProofOfPossession,
12
13
  ProofOfPossessionCallbacks,
@@ -17,9 +18,11 @@ export class ProofOfPossessionBuilder<DIDDoc> {
17
18
  private readonly proof?: ProofOfPossession;
18
19
  private readonly callbacks?: ProofOfPossessionCallbacks<DIDDoc>;
19
20
  private readonly version: OpenId4VCIVersion;
21
+ private readonly mode: PoPMode = 'pop';
20
22
 
21
23
  private kid?: string;
22
24
  private jwk?: JWK;
25
+ private aud?: string | string[];
23
26
  private clientId?: string;
24
27
  private issuer?: string;
25
28
  private jwt?: Jwt;
@@ -34,54 +37,80 @@ export class ProofOfPossessionBuilder<DIDDoc> {
34
37
  jwt,
35
38
  accessTokenResponse,
36
39
  version,
40
+ mode = 'pop',
37
41
  }: {
38
42
  proof?: ProofOfPossession;
39
43
  callbacks?: ProofOfPossessionCallbacks<DIDDoc>;
40
44
  accessTokenResponse?: AccessTokenResponse;
41
45
  jwt?: Jwt;
42
46
  version: OpenId4VCIVersion;
47
+ mode?: PoPMode;
43
48
  }) {
49
+ this.mode = mode;
44
50
  this.proof = proof;
45
51
  this.callbacks = callbacks;
46
52
  this.version = version;
47
53
  if (jwt) {
48
54
  this.withJwt(jwt);
49
55
  } else {
50
- this.withTyp(version < OpenId4VCIVersion.VER_1_0_11 ? 'jwt' : 'openid4vci-proof+jwt');
56
+ this.withTyp(version < OpenId4VCIVersion.VER_1_0_11 || mode === 'jwt' ? 'jwt' : 'openid4vci-proof+jwt');
51
57
  }
52
58
  if (accessTokenResponse) {
53
59
  this.withAccessTokenResponse(accessTokenResponse);
54
60
  }
55
61
  }
56
62
 
63
+ static manual<DIDDoc>({
64
+ jwt,
65
+ callbacks,
66
+ version,
67
+ mode = 'jwt',
68
+ }: {
69
+ jwt?: Jwt;
70
+ callbacks: ProofOfPossessionCallbacks<DIDDoc>;
71
+ version: OpenId4VCIVersion;
72
+ mode?: PoPMode;
73
+ }): ProofOfPossessionBuilder<DIDDoc> {
74
+ return new ProofOfPossessionBuilder({ callbacks, jwt, version, mode });
75
+ }
76
+
57
77
  static fromJwt<DIDDoc>({
58
78
  jwt,
59
79
  callbacks,
60
80
  version,
81
+ mode = 'pop',
61
82
  }: {
62
83
  jwt: Jwt;
63
84
  callbacks: ProofOfPossessionCallbacks<DIDDoc>;
64
85
  version: OpenId4VCIVersion;
86
+ mode?: PoPMode;
65
87
  }): ProofOfPossessionBuilder<DIDDoc> {
66
- return new ProofOfPossessionBuilder({ callbacks, jwt, version });
88
+ return new ProofOfPossessionBuilder({ callbacks, jwt, version, mode });
67
89
  }
68
90
 
69
91
  static fromAccessTokenResponse<DIDDoc>({
70
92
  accessTokenResponse,
71
93
  callbacks,
72
94
  version,
95
+ mode = 'pop',
73
96
  }: {
74
97
  accessTokenResponse: AccessTokenResponse;
75
98
  callbacks: ProofOfPossessionCallbacks<DIDDoc>;
76
99
  version: OpenId4VCIVersion;
100
+ mode?: PoPMode;
77
101
  }): ProofOfPossessionBuilder<DIDDoc> {
78
- return new ProofOfPossessionBuilder({ callbacks, accessTokenResponse, version });
102
+ return new ProofOfPossessionBuilder({ callbacks, accessTokenResponse, version, mode });
79
103
  }
80
104
 
81
105
  static fromProof<DIDDoc>(proof: ProofOfPossession, version: OpenId4VCIVersion): ProofOfPossessionBuilder<DIDDoc> {
82
106
  return new ProofOfPossessionBuilder({ proof, version });
83
107
  }
84
108
 
109
+ withAud(aud: string | string[]): this {
110
+ this.aud = aud;
111
+ return this;
112
+ }
113
+
85
114
  withClientId(clientId: string): this {
86
115
  this.clientId = clientId;
87
116
  return this;
@@ -113,7 +142,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
113
142
  }
114
143
 
115
144
  withTyp(typ: Typ): this {
116
- if (this.version >= OpenId4VCIVersion.VER_1_0_11) {
145
+ if (this.mode === 'pop' && this.version >= OpenId4VCIVersion.VER_1_0_11) {
117
146
  if (!!typ && typ !== 'openid4vci-proof+jwt') {
118
147
  throw Error('typ must be openid4vci-proof+jwt for version 1.0.11 and up');
119
148
  }
@@ -160,7 +189,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
160
189
  if (jwt.header.typ) {
161
190
  this.withTyp(jwt.header.typ as Typ);
162
191
  }
163
- if (this.version >= OpenId4VCIVersion.VER_1_0_11) {
192
+ if (!this.typ && this.version >= OpenId4VCIVersion.VER_1_0_11) {
164
193
  this.withTyp('openid4vci-proof+jwt');
165
194
  }
166
195
  this.withAlg(jwt.header.alg);
@@ -171,8 +200,8 @@ export class ProofOfPossessionBuilder<DIDDoc> {
171
200
  }
172
201
 
173
202
  if (jwt.payload) {
174
- if (jwt.payload.iss) this.withClientId(jwt.payload.iss);
175
- if (jwt.payload.aud) this.withIssuer(jwt.payload.aud);
203
+ if (jwt.payload.iss) this.mode === 'pop' ? this.withClientId(jwt.payload.iss) : this.withIssuer(jwt.payload.iss);
204
+ if (jwt.payload.aud) this.mode === 'pop' ? this.withIssuer(jwt.payload.aud) : this.withAud(jwt.payload.aud);
176
205
  if (jwt.payload.jti) this.withJti(jwt.payload.jti);
177
206
  if (jwt.payload.nonce) this.withAccessTokenNonce(jwt.payload.nonce);
178
207
  }
@@ -184,16 +213,18 @@ export class ProofOfPossessionBuilder<DIDDoc> {
184
213
  return Promise.resolve(this.proof);
185
214
  } else if (this.callbacks) {
186
215
  return await createProofOfPossession(
216
+ this.mode,
187
217
  this.callbacks,
188
218
  {
189
- typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 ? 'jwt' : 'openid4vci-proof+jwt'),
219
+ typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 || this.mode === 'jwt' ? 'jwt' : 'openid4vci-proof+jwt'),
190
220
  kid: this.kid,
191
221
  jwk: this.jwk,
192
222
  jti: this.jti,
193
223
  alg: this.alg,
224
+ aud: this.aud,
194
225
  issuer: this.issuer,
195
226
  clientId: this.clientId,
196
- nonce: this.cNonce,
227
+ ...(this.cNonce && { nonce: this.cNonce }),
197
228
  },
198
229
  this.jwt,
199
230
  );
@@ -16,7 +16,7 @@ import * as jose from 'jose';
16
16
  // @ts-ignore
17
17
  import nock from 'nock';
18
18
 
19
- import { CredentialOfferClientV1_0_11, CredentialRequestClientBuilderV1_0_11, MetadataClient, ProofOfPossessionBuilder } from '..';
19
+ import { CredentialOfferClientV1_0_11, CredentialRequestClientBuilderV1_0_11, MetadataClientV1_0_13, ProofOfPossessionBuilder } from '..';
20
20
 
21
21
  import {
22
22
  IDENTIPROOF_ISSUER_URL,
@@ -177,7 +177,7 @@ describe('Credential Request Client with Walt.id ', () => {
177
177
  const credentialOffer = await CredentialOfferClientV1_0_11.fromURI(WALT_IRR_URI);
178
178
 
179
179
  const request = credentialOffer.credential_offer;
180
- const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request) as string);
180
+ const metadata = await MetadataClientV1_0_13.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request) as string);
181
181
  expect(metadata.credential_endpoint).toEqual(WALT_OID4VCI_METADATA.credential_endpoint);
182
182
  expect(metadata.token_endpoint).toEqual(WALT_OID4VCI_METADATA.token_endpoint);
183
183
 
@@ -22,7 +22,7 @@ import {
22
22
  import { getMockData } from './data/VciDataFixtures';
23
23
 
24
24
  //todo: skipping this. it was written for pre v13 version and we have to do some modifications to make it work
25
- describe.skip('MetadataClient with IdentiProof Issuer should', () => {
25
+ describe('MetadataClient with IdentiProof Issuer should', () => {
26
26
  beforeAll(() => {
27
27
  nock.cleanAll();
28
28
  });
@@ -99,8 +99,7 @@ describe.skip('MetadataClient with IdentiProof Issuer should', () => {
99
99
  );
100
100
  });
101
101
 
102
- // skipping because this metadata is for an older version. update it with a new metadata
103
- it.skip('Fail if there is no credential endpoint with errors enabled', async () => {
102
+ it('Fail if there is no credential endpoint with errors enabled', async () => {
104
103
  const meta = JSON.parse(JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
105
104
  delete meta.credential_endpoint;
106
105
  nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(meta));
@@ -112,7 +111,7 @@ describe.skip('MetadataClient with IdentiProof Issuer should', () => {
112
111
  );
113
112
  });
114
113
 
115
- it.skip('Succeed with default value if there is no credential endpoint with errors disabled', async () => {
114
+ it('Succeed with default value if there is no credential endpoint with errors disabled', async () => {
116
115
  nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
117
116
  nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
118
117
  nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404);
@@ -48,6 +48,7 @@ export const INITIATION_TEST: CredentialOfferRequestWithBaseUrl = {
48
48
  scheme: 'openid-credential-offer',
49
49
  supportedFlows: [AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW],
50
50
  version: 1013,
51
+ userPinRequired: true, // Determined from above tx_code
51
52
  };
52
53
  export const INITIATION_TEST_V1_0_08: CredentialOfferRequestWithBaseUrl = {
53
54
  baseUrl: 'openid-initiate-issuance://',
@@ -1,8 +1,16 @@
1
- import { CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common';
1
+ import {
2
+ CodeChallengeMethod,
3
+ CredentialOfferPayloadV1_0_13,
4
+ determineSpecVersionFromOffer,
5
+ determineSpecVersionFromURI,
6
+ OpenId4VCIVersion,
7
+ WellKnownEndpoints,
8
+ } from '@sphereon/oid4vci-common';
2
9
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
10
  // @ts-ignore
4
11
  import nock from 'nock';
5
12
 
13
+ import { createCredentialOfferURIFromObject } from '../../../issuer/lib';
6
14
  import { OpenID4VCIClient } from '../OpenID4VCIClient';
7
15
 
8
16
  const MOCK_URL = 'https://server.example.com/';
@@ -203,23 +211,48 @@ describe('OpenID4VCIClient should', () => {
203
211
  });
204
212
  });
205
213
  describe('should successfully handle isEbsi function', () => {
206
- it.skip('should return true when calling isEbsi function', async () => {
214
+ it('should return true when calling isEbsi function', async () => {
215
+ nock(MOCK_URL).get(/.*/).reply(200, {});
216
+ nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
217
+ nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
207
218
  const client = await OpenID4VCIClient.fromURI({
208
219
  clientId: 'test-client',
209
- uri: 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fserver.example.com%22%2C%22credential_configuration_ids%22%3A%5B%22TestCredential%22%5D%7D',
220
+ uri: 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fserver.example.com%22%2C%20%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22CTWalletSameAuthorisedInTime%22%5D%2C%22trust_framework%22%3A%7B%22name%22%3A%22ebsi%22%2C%22type%22%3A%22Accreditation%22%2C%22uri%22%3A%22TIR%20link%20towards%20accreditation%22%7D%7D%5D%7D',
210
221
  createAuthorizationRequestURL: false,
211
222
  });
212
- nock(MOCK_URL).get(/.*/).reply(200, {});
213
- nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
214
- nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
215
223
 
216
224
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
217
225
  // @ts-ignore
218
- client._state.endpointMetadata?.credentialIssuerMetadata?.credential_configurations_supported['TestCredential'] = {
219
- trust_framework: {
220
- name: 'ebsi_trust',
226
+ client._state.endpointMetadata?.credentialIssuerMetadata = {
227
+ credentials_supported: {
228
+ TestCredential: {
229
+ trust_framework: {
230
+ name: 'ebsi_trust',
231
+ },
232
+ },
221
233
  },
222
234
  };
223
235
  expect(client.isEBSI()).toBe(true);
224
236
  });
225
237
  });
238
+
239
+ it('determine to be version 13', async () => {
240
+ const offer = {
241
+ grants: {
242
+ 'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
243
+ 'pre-authorized_code': 'random',
244
+ },
245
+ },
246
+ credential_configuration_ids: ['Omzetbelasting'],
247
+ credential_issuer: 'https://example.com',
248
+ } satisfies CredentialOfferPayloadV1_0_13;
249
+ const offerUri = createCredentialOfferURIFromObject({ credential_offer: offer });
250
+
251
+ expect(determineSpecVersionFromOffer(offer)).toEqual(OpenId4VCIVersion.VER_1_0_13);
252
+ expect(determineSpecVersionFromURI(offerUri)).toEqual(OpenId4VCIVersion.VER_1_0_13);
253
+ });
254
+ it('determine to be version 11', async () => {
255
+ const offerUri =
256
+ 'openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22wN39X8fU4FCU2MaykNRkCr%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22dbc2023%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fssi.dutchblockchaincoalition.org%2Fagent%22%7D';
257
+ expect(determineSpecVersionFromURI(offerUri)).toEqual(OpenId4VCIVersion.VER_1_0_11);
258
+ });
@@ -200,3 +200,27 @@ describe('OpenID4VCIClientV1_0_11 should', () => {
200
200
  );
201
201
  });
202
202
  });
203
+
204
+ it('should return true when calling isEbsi function', async () => {
205
+ nock(MOCK_URL).get(/.*/).reply(200, {});
206
+ nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
207
+ nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
208
+ const client = await OpenID4VCIClientV1_0_11.fromURI({
209
+ clientId: 'test-client',
210
+ uri: 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fserver.example.com%22%2C%20%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22CTWalletSameAuthorisedInTime%22%5D%2C%22trust_framework%22%3A%7B%22name%22%3A%22ebsi%22%2C%22type%22%3A%22Accreditation%22%2C%22uri%22%3A%22TIR%20link%20towards%20accreditation%22%7D%7D%5D%7D',
211
+ createAuthorizationRequestURL: false,
212
+ });
213
+
214
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
215
+ // @ts-ignore
216
+ client._state.endpointMetadata?.credentialIssuerMetadata = {
217
+ credentials_supported: {
218
+ TestCredential: {
219
+ trust_framework: {
220
+ name: 'ebsi_trust',
221
+ },
222
+ },
223
+ },
224
+ };
225
+ expect(client.isEBSI()).toBe(true);
226
+ });
@@ -0,0 +1,204 @@
1
+ import { CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common';
2
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
+ // @ts-ignore
4
+ import nock from 'nock';
5
+
6
+ import { OpenID4VCIClientV1_0_13 } from '../OpenID4VCIClientV1_0_13';
7
+
8
+ const MOCK_URL = 'https://server.example.com/';
9
+
10
+ describe('OpenID4VCIClientV1_0_13 should', () => {
11
+ let client: OpenID4VCIClientV1_0_13;
12
+
13
+ beforeEach(async () => {
14
+ nock(MOCK_URL).get(/.*/).reply(200, {});
15
+ nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
16
+ nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
17
+ client = await OpenID4VCIClientV1_0_13.fromURI({
18
+ clientId: 'test-client',
19
+ uri: 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fserver.example.com%22%2C%22credential_configuration_ids%22%3A%5B%22TestCredential%22%5D%7D',
20
+ createAuthorizationRequestURL: false,
21
+ });
22
+ });
23
+
24
+ afterEach(() => {
25
+ nock.cleanAll();
26
+ });
27
+
28
+ it('should successfully construct an authorization request url', async () => {
29
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
+ // @ts-ignore
31
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
32
+ const url = await client.createAuthorizationRequestUrl({
33
+ authorizationRequest: {
34
+ scope: 'openid TestCredential',
35
+ redirectUri: 'http://localhost:8881/cb',
36
+ },
37
+ });
38
+
39
+ const urlSearchParams = new URLSearchParams(url.split('?')[1]);
40
+ const scope = urlSearchParams.get('scope')?.split(' ');
41
+
42
+ expect(scope?.[0]).toBe('openid');
43
+ });
44
+ it('throw an error if authorization endpoint is not set in server metadata', async () => {
45
+ await expect(
46
+ client.createAuthorizationRequestUrl({
47
+ authorizationRequest: {
48
+ scope: 'openid TestCredential',
49
+ redirectUri: 'http://localhost:8881/cb',
50
+ },
51
+ }),
52
+ ).rejects.toThrow(Error('Server metadata does not contain authorization endpoint'));
53
+ });
54
+ it("injects 'openid' as the first scope if not provided", async () => {
55
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
56
+ // @ts-ignore
57
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
58
+
59
+ const url = await client.createAuthorizationRequestUrl({
60
+ pkce: {
61
+ codeChallengeMethod: CodeChallengeMethod.S256,
62
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
63
+ },
64
+ authorizationRequest: {
65
+ scope: 'TestCredential',
66
+ redirectUri: 'http://localhost:8881/cb',
67
+ },
68
+ });
69
+
70
+ const urlSearchParams = new URLSearchParams(url.split('?')[1]);
71
+ const scope = urlSearchParams.get('scope')?.split(' ');
72
+
73
+ expect(scope?.[0]).toBe('openid');
74
+ });
75
+ it('throw an error if no scope and no authorization_details is provided', async () => {
76
+ nock(MOCK_URL).get(/.*/).reply(200, {});
77
+ nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, {});
78
+ nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(200, {});
79
+ // Use a client with issuer only to trigger the error
80
+ client = await OpenID4VCIClientV1_0_13.fromCredentialIssuer({
81
+ credentialIssuer: MOCK_URL,
82
+ createAuthorizationRequestURL: false,
83
+ retrieveServerMetadata: false,
84
+ });
85
+
86
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
87
+ // @ts-ignore
88
+ client._state.endpointMetadata = {
89
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
90
+ // @ts-ignore
91
+ credentialIssuerMetadata: {
92
+ authorization_endpoint: `${MOCK_URL}v1/auth/authorize`,
93
+ token_endpoint: `${MOCK_URL}/token`,
94
+ },
95
+ };
96
+ // client._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
97
+
98
+ await expect(
99
+ client.createAuthorizationRequestUrl({
100
+ pkce: {
101
+ codeChallengeMethod: CodeChallengeMethod.S256,
102
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
103
+ },
104
+ authorizationRequest: {
105
+ clientId: 'clientId',
106
+ redirectUri: 'http://localhost:8881/cb',
107
+ },
108
+ }),
109
+ ).rejects.toThrow(Error('Please provide a scope or authorization_details if no credential offer is present'));
110
+ });
111
+ it('create an authorization request url with authorization_details array property', async () => {
112
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
113
+ // @ts-ignore
114
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
115
+
116
+ await expect(
117
+ client.createAuthorizationRequestUrl({
118
+ pkce: {
119
+ codeChallengeMethod: CodeChallengeMethod.S256,
120
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
121
+ },
122
+ authorizationRequest: {
123
+ authorizationDetails: [
124
+ {
125
+ type: 'openid_credential',
126
+ format: 'ldp_vc',
127
+ credential_definition: {
128
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
129
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
130
+ },
131
+ },
132
+ {
133
+ type: 'openid_credential',
134
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
135
+ // @ts-ignore
136
+ format: 'mso_mdoc',
137
+ doctype: 'org.iso.18013.5.1.mDL',
138
+ },
139
+ ],
140
+ redirectUri: 'http://localhost:8881/cb',
141
+ },
142
+ }),
143
+ ).resolves.toEqual(
144
+ 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
145
+ );
146
+ });
147
+ it('create an authorization request url with authorization_details object property', async () => {
148
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
149
+ // @ts-ignore
150
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
151
+
152
+ await expect(
153
+ client.createAuthorizationRequestUrl({
154
+ pkce: {
155
+ codeChallengeMethod: CodeChallengeMethod.S256,
156
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
157
+ },
158
+ authorizationRequest: {
159
+ authorizationDetails: {
160
+ type: 'openid_credential',
161
+ format: 'ldp_vc',
162
+ credential_definition: {
163
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
164
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
165
+ },
166
+ },
167
+ redirectUri: 'http://localhost:8881/cb',
168
+ },
169
+ }),
170
+ ).resolves.toEqual(
171
+ 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
172
+ );
173
+ });
174
+
175
+ it('create an authorization request url with authorization_details and scope', async () => {
176
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
177
+ // @ts-ignore
178
+ client._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
179
+
180
+ await expect(
181
+ client.createAuthorizationRequestUrl({
182
+ pkce: {
183
+ codeChallengeMethod: CodeChallengeMethod.S256,
184
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
185
+ },
186
+ authorizationRequest: {
187
+ authorizationDetails: {
188
+ type: 'openid_credential',
189
+ format: 'ldp_vc',
190
+ locations: ['https://test.com'],
191
+ credential_definition: {
192
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
193
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
194
+ },
195
+ },
196
+ scope: 'openid',
197
+ redirectUri: 'http://localhost:8881/cb',
198
+ },
199
+ }),
200
+ ).resolves.toEqual(
201
+ 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22locations%22%3A%5B%22https%3A%2F%2Ftest%2Ecom%22%2C%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
202
+ );
203
+ });
204
+ });
@@ -8,7 +8,7 @@ import {
8
8
  // @ts-ignore
9
9
  import nock from 'nock';
10
10
 
11
- import { OpenID4VCIClient } from '..';
11
+ import { OpenID4VCIClientV1_0_13 } from '..';
12
12
  import { createAccessTokenResponse, IssuerMetadataBuilderV1_13, VcIssuerBuilder } from '../../../issuer';
13
13
 
14
14
  export const UNIT_TEST_TIMEOUT = 30000;
@@ -88,7 +88,7 @@ describe('sd-jwt vc', () => {
88
88
  'openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22123%22%2C%22tx_code%22%3A%7B%22input_mode%22%3A%22text%22%2C%22length%22%3A3%7D%7D%7D%2C%22credential_configuration_ids%22%3A%5B%22SdJwtCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%7D',
89
89
  );
90
90
 
91
- const client = await OpenID4VCIClient.fromURI({
91
+ const client = await OpenID4VCIClientV1_0_13.fromURI({
92
92
  uri: offerUri.uri,
93
93
  });
94
94
 
@@ -25,7 +25,8 @@ const jwk: JWK = {
25
25
  // priv hex: 913466d1a38d1d8c0d3c0fb0fc3b633075085a31372bbd2a8022215a88d9d1e5
26
26
  const did = `did:key:z6Mki5ZwZKN1dBQprfJTikUvkDxrHijiiQngkWviMF5gw2Hv`;
27
27
  const kid = `${did}#z6Mki5ZwZKN1dBQprfJTikUvkDxrHijiiQngkWviMF5gw2Hv`;
28
- describe('OID4VCI-Client using Sphereon issuer should', () => {
28
+ // Sphereon infra down rn
29
+ describe.skip('OID4VCI-Client using Sphereon issuer should', () => {
29
30
  async function test(format: 'ldp_vc' | 'jwt_vc_json') {
30
31
  debug.enable('*');
31
32
  const offer = await getCredentialOffer(format);
@@ -59,14 +60,14 @@ describe('OID4VCI-Client using Sphereon issuer should', () => {
59
60
  expect(format.startsWith(wrappedVC.format)).toEqual(true);
60
61
  }
61
62
 
62
- xit(
63
+ it(
63
64
  'succeed in a full flow with the client using OpenID4VCI version 11 and ldp_vc',
64
65
  async () => {
65
66
  await test('ldp_vc');
66
67
  },
67
68
  UNIT_TEST_TIMEOUT,
68
69
  );
69
- xit(
70
+ it(
70
71
  'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json',
71
72
  async () => {
72
73
  await test('jwt_vc_json');
package/lib/index.ts CHANGED
@@ -1,3 +1,8 @@
1
+ import { VCI_LOGGERS } from '@sphereon/oid4vci-common';
2
+ import { ISimpleLogger } from '@sphereon/ssi-types';
3
+
4
+ export const LOG: ISimpleLogger<string> = VCI_LOGGERS.get('sphereon:oid4vci:client');
5
+
1
6
  export * from './AccessTokenClient';
2
7
  export * from './AccessTokenClientV1_0_11';
3
8
  export * from './AuthorizationCodeClient';
@@ -5,12 +10,15 @@ export * from './AuthorizationCodeClientV1_0_11';
5
10
  export * from './CredentialRequestClient';
6
11
  export * from './CredentialOfferClient';
7
12
  export * from './CredentialOfferClientV1_0_11';
13
+ export * from './CredentialOfferClientV1_0_13';
8
14
  export * from './CredentialRequestClientV1_0_11';
9
15
  export * from './CredentialRequestClientBuilder';
10
16
  export * from './CredentialRequestClientBuilderV1_0_11';
11
17
  export * from './functions';
12
18
  export * from './MetadataClient';
19
+ export * from './MetadataClientV1_0_13';
13
20
  export * from './MetadataClientV1_0_11';
14
21
  export * from './OpenID4VCIClient';
22
+ export * from './OpenID4VCIClientV1_0_13';
15
23
  export * from './OpenID4VCIClientV1_0_11';
16
24
  export * from './ProofOfPossessionBuilder';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/oid4vci-client",
3
- "version": "0.10.4-unstable.98+b0ade3b",
3
+ "version": "0.12.0",
4
4
  "description": "OpenID for Verifiable Credential Issuance (OpenID4VCI) client",
5
5
  "source": "lib/index.ts",
6
6
  "main": "dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "build": "tsc"
16
16
  },
17
17
  "dependencies": {
18
- "@sphereon/oid4vci-common": "0.10.4-unstable.98+b0ade3b",
19
- "@sphereon/ssi-types": "0.24.1-unstable.129",
18
+ "@sphereon/oid4vci-common": "0.12.0",
19
+ "@sphereon/ssi-types": "0.25.1-unstable.87",
20
20
  "cross-fetch": "^3.1.8",
21
21
  "debug": "^4.3.4"
22
22
  },
@@ -69,5 +69,5 @@
69
69
  "OIDC4VCI",
70
70
  "OID4VCI"
71
71
  ],
72
- "gitHead": "b0ade3b9482bc6be469b46e8ce886359e3ccdf82"
72
+ "gitHead": "a76c382a36e38428391c9e8d1a6b67e477fc659a"
73
73
  }