@sphereon/oid4vci-client 0.10.3 → 0.10.4-next.17

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 (60) hide show
  1. package/README.md +18 -0
  2. package/dist/AccessTokenClient.d.ts.map +1 -1
  3. package/dist/AccessTokenClient.js +11 -16
  4. package/dist/AccessTokenClient.js.map +1 -1
  5. package/dist/CredentialOfferClient.d.ts.map +1 -1
  6. package/dist/CredentialOfferClient.js +8 -7
  7. package/dist/CredentialOfferClient.js.map +1 -1
  8. package/dist/CredentialRequestClient.d.ts +14 -3
  9. package/dist/CredentialRequestClient.d.ts.map +1 -1
  10. package/dist/CredentialRequestClient.js +18 -19
  11. package/dist/CredentialRequestClient.js.map +1 -1
  12. package/dist/CredentialRequestClientBuilder.d.ts +3 -1
  13. package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
  14. package/dist/CredentialRequestClientBuilder.js +4 -0
  15. package/dist/CredentialRequestClientBuilder.js.map +1 -1
  16. package/dist/MetadataClient.d.ts.map +1 -1
  17. package/dist/MetadataClient.js +1 -2
  18. package/dist/MetadataClient.js.map +1 -1
  19. package/dist/OpenID4VCIClient.d.ts +7 -2
  20. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  21. package/dist/OpenID4VCIClient.js +15 -1
  22. package/dist/OpenID4VCIClient.js.map +1 -1
  23. package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
  24. package/dist/ProofOfPossessionBuilder.js +1 -2
  25. package/dist/ProofOfPossessionBuilder.js.map +1 -1
  26. package/dist/functions/index.d.ts +1 -3
  27. package/dist/functions/index.d.ts.map +1 -1
  28. package/dist/functions/index.js +1 -3
  29. package/dist/functions/index.js.map +1 -1
  30. package/dist/functions/notifications.d.ts +4 -0
  31. package/dist/functions/notifications.d.ts.map +1 -0
  32. package/dist/functions/notifications.js +41 -0
  33. package/dist/functions/notifications.js.map +1 -0
  34. package/dist/types/index.d.ts +2 -0
  35. package/dist/types/index.d.ts.map +1 -1
  36. package/dist/types/index.js +5 -0
  37. package/dist/types/index.js.map +1 -1
  38. package/lib/AccessTokenClient.ts +12 -13
  39. package/lib/CredentialOfferClient.ts +7 -5
  40. package/lib/CredentialRequestClient.ts +26 -7
  41. package/lib/CredentialRequestClientBuilder.ts +7 -0
  42. package/lib/MetadataClient.ts +1 -2
  43. package/lib/OpenID4VCIClient.ts +25 -2
  44. package/lib/ProofOfPossessionBuilder.ts +1 -2
  45. package/lib/__tests__/CredentialRequestClient.spec.ts +5 -1
  46. package/lib/__tests__/HttpUtils.spec.ts +1 -1
  47. package/lib/__tests__/IT.spec.ts +42 -3
  48. package/lib/__tests__/IssuanceInitiation.spec.ts +22 -0
  49. package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +1 -1
  50. package/lib/__tests__/SdJwt.spec.ts +3 -1
  51. package/lib/__tests__/SphereonE2E.spec.test.ts +2 -1
  52. package/lib/functions/index.ts +1 -3
  53. package/lib/functions/notifications.ts +32 -0
  54. package/lib/types/index.ts +6 -0
  55. package/package.json +4 -4
  56. package/dist/functions/ProofUtil.d.ts +0 -30
  57. package/dist/functions/ProofUtil.d.ts.map +0 -1
  58. package/dist/functions/ProofUtil.js +0 -106
  59. package/dist/functions/ProofUtil.js.map +0 -1
  60. package/lib/functions/ProofUtil.ts +0 -128
@@ -11,12 +11,15 @@ import {
11
11
  CredentialSupported,
12
12
  DefaultURISchemes,
13
13
  EndpointMetadataResult,
14
+ ExperimentalSubjectIssuance,
14
15
  getClientIdFromCredentialOfferPayload,
15
16
  getIssuerFromCredentialOfferPayload,
16
17
  getSupportedCredentials,
17
18
  getTypesFromCredentialSupported,
18
19
  JWK,
19
20
  KID_JWK_X5C_ERROR,
21
+ NotificationRequest,
22
+ NotificationResult,
20
23
  OID4VCICredentialFormat,
21
24
  OpenId4VCIVersion,
22
25
  PKCEOpts,
@@ -29,10 +32,12 @@ import Debug from 'debug';
29
32
  import { AccessTokenClient } from './AccessTokenClient';
30
33
  import { createAuthorizationRequestUrl } from './AuthorizationCodeClient';
31
34
  import { CredentialOfferClient } from './CredentialOfferClient';
35
+ import { CredentialRequestOpts } from './CredentialRequestClient';
32
36
  import { CredentialRequestClientBuilder } from './CredentialRequestClientBuilder';
33
37
  import { MetadataClient } from './MetadataClient';
34
38
  import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
35
39
  import { generateMissingPKCEOpts } from './functions/AuthorizationUtil';
40
+ import { sendNotification } from './functions/notifications';
36
41
 
37
42
  const debug = Debug('sphereon:oid4vci');
38
43
 
@@ -339,7 +344,8 @@ export class OpenID4VCIClient {
339
344
  jti?: string;
340
345
  deferredCredentialAwait?: boolean;
341
346
  deferredCredentialIntervalInMS?: number;
342
- }): Promise<CredentialResponse> {
347
+ experimentalHolderIssuanceSupported?: boolean;
348
+ }): Promise<CredentialResponse & { access_token: string }> {
343
349
  if ([jwk, kid].filter((v) => v !== undefined).length > 1) {
344
350
  throw new Error(KID_JWK_X5C_ERROR + `. jwk: ${jwk !== undefined}, kid: ${kid !== undefined}`);
345
351
  }
@@ -362,6 +368,7 @@ export class OpenID4VCIClient {
362
368
 
363
369
  requestBuilder.withTokenFromResponse(this.accessTokenResponse);
364
370
  requestBuilder.withDeferredCredentialAwait(deferredCredentialAwait ?? false, deferredCredentialIntervalInMS);
371
+ let subjectIssuance: ExperimentalSubjectIssuance | undefined;
365
372
  if (this.endpointMetadata?.credentialIssuerMetadata) {
366
373
  const metadata = this.endpointMetadata.credentialIssuerMetadata;
367
374
  const types = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
@@ -376,6 +383,9 @@ export class OpenID4VCIClient {
376
383
  (types.length === 1 && (types[0] === supportedCredential.id || subTypes.includes(types[0])))
377
384
  ) {
378
385
  typeSupported = true;
386
+ if (supportedCredential.credential_subject_issuance) {
387
+ subjectIssuance = { credential_subject_issuance: supportedCredential.credential_subject_issuance };
388
+ }
379
389
  }
380
390
  });
381
391
 
@@ -391,6 +401,10 @@ export class OpenID4VCIClient {
391
401
  }
392
402
  // todo: Format check? We might end up with some disjoint type / format combinations supported by the server
393
403
  }
404
+ if (subjectIssuance) {
405
+ requestBuilder.withSubjectIssuance(subjectIssuance);
406
+ }
407
+
394
408
  const credentialRequestClient = requestBuilder.build();
395
409
  const proofBuilder = ProofOfPossessionBuilder.fromAccessTokenResponse({
396
410
  accessTokenResponse: this.accessTokenResponse,
@@ -418,6 +432,7 @@ export class OpenID4VCIClient {
418
432
  credentialTypes,
419
433
  context,
420
434
  format,
435
+ subjectIssuance,
421
436
  });
422
437
  if (response.errorBody) {
423
438
  debug(`Credential request error:\r\n${JSON.stringify(response.errorBody)}`);
@@ -434,7 +449,7 @@ export class OpenID4VCIClient {
434
449
  } for issuer ${this.getIssuer()} failed as there was no success response body`,
435
450
  );
436
451
  }
437
- return response.successBody;
452
+ return { ...response.successBody, access_token: response.access_token };
438
453
  }
439
454
 
440
455
  public async exportState(): Promise<string> {
@@ -457,6 +472,14 @@ export class OpenID4VCIClient {
457
472
  });
458
473
  }
459
474
 
475
+ public async sendNotification(
476
+ credentialRequestOpts: CredentialRequestOpts,
477
+ request: NotificationRequest,
478
+ accessToken?: string,
479
+ ): Promise<NotificationResult> {
480
+ return sendNotification(credentialRequestOpts, request, accessToken ?? this.accessTokenResponse.access_token);
481
+ }
482
+
460
483
  getCredentialOfferTypes(): string[][] {
461
484
  if (!this.credentialOffer) {
462
485
  return [];
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AccessTokenResponse,
3
3
  Alg,
4
+ createProofOfPossession,
4
5
  EndpointMetadata,
5
6
  JWK,
6
7
  Jwt,
@@ -12,8 +13,6 @@ import {
12
13
  Typ,
13
14
  } from '@sphereon/oid4vci-common';
14
15
 
15
- import { createProofOfPossession } from './functions';
16
-
17
16
  export class ProofOfPossessionBuilder<DIDDoc> {
18
17
  private readonly proof?: ProofOfPossession;
19
18
  private readonly callbacks?: ProofOfPossessionCallbacks<DIDDoc>;
@@ -1,3 +1,6 @@
1
+ // Walt uses a self signed cert
2
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
3
+
1
4
  import { KeyObject } from 'crypto';
2
5
 
3
6
  import {
@@ -164,7 +167,8 @@ describe('Credential Request Client with Walt.id ', () => {
164
167
  afterEach(() => {
165
168
  nock.cleanAll();
166
169
  });
167
- it('should have correct metadata endpoints', async function () {
170
+ // Walt id has cert issue
171
+ it.skip('should have correct metadata endpoints', async function () {
168
172
  nock.cleanAll();
169
173
  const WALT_IRR_URI =
170
174
  'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Foidc%2F&credential_type=OpenBadgeCredential&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhOTUyZjUxNi1jYWVmLTQ4YjMtODIxYy00OTRkYzgyNjljZjAiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.YE5DlalcLC2ChGEg47CQDaN1gTxbaQqSclIVqsSAUHE&user_pin_required=false';
@@ -1,4 +1,4 @@
1
- import { isValidURL } from '../functions';
1
+ import { isValidURL } from '@sphereon/oid4vci-common';
2
2
 
3
3
  describe('httputils.isValidURL', () => {
4
4
  it('Should return true for http://localhost', () => {
@@ -50,7 +50,13 @@ describe('OID4VCI-Client should', () => {
50
50
  const INITIATE_QR =
51
51
  'openid-initiate-issuance://?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredentialUrl&pre-authorized_code=4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp&user_pin_required=true';
52
52
  const OFFER_QR =
53
- 'openid-credential-offer://credential_offer=%7B%22credential_issuer%22:%22https://credential-issuer.example.com%22,%22credentials%22:%5B%7B%22format%22:%22jwt_vc_json%22,%22types%22:%5B%22VerifiableCredential%22,%22UniversityDegreeCredential%22%5D%7D%5D,%22issuer_state%22:%22eyJhbGciOiJSU0Et...FYUaBy%22%7D';
53
+ 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22adhjhdjajkdkhjhdj%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D';
54
+ const HTTPS_INITIATE_QR =
55
+ 'https://issuer.research.identiproof.io?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredentialUrl&pre-authorized_code=4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp&user_pin_required=true';
56
+ const HTTPS_OFFER_QR_AUTHORIZATION_CODE =
57
+ 'https://issuer.research.identiproof.io?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22eyJhbGciOiJSU0Et...FYUaBy%22%7D%7D%7D';
58
+ const HTTPS_OFFER_QR_PRE_AUTHORIZED =
59
+ 'https://issuer.research.identiproof.io?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22adhjhdjajkdkhjhdj%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D';
54
60
 
55
61
  function succeedWithAFullFlowWithClientSetup() {
56
62
  nock(IDENTIPROOF_ISSUER_URL).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
@@ -78,7 +84,7 @@ describe('OID4VCI-Client should', () => {
78
84
  await assertionOfsucceedWithAFullFlowWithClient(client);
79
85
  });
80
86
 
81
- test.skip('succeed with a full flow wit the client using OpenID4VCI version 11', async () => {
87
+ it('succeed with a full flow with the client using OpenID4VCI version 11 and deeplink', async () => {
82
88
  succeedWithAFullFlowWithClientSetup();
83
89
  const client = await OpenID4VCIClient.fromURI({
84
90
  uri: OFFER_QR,
@@ -89,6 +95,39 @@ describe('OID4VCI-Client should', () => {
89
95
  await assertionOfsucceedWithAFullFlowWithClient(client);
90
96
  });
91
97
 
98
+ it('succeed with a full flow with the client using OpenID4VCI draft < 9 and https', async () => {
99
+ succeedWithAFullFlowWithClientSetup();
100
+ const client = await OpenID4VCIClient.fromURI({
101
+ uri: HTTPS_INITIATE_QR,
102
+ kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
103
+ alg: Alg.ES256,
104
+ clientId: 'test-clientId',
105
+ });
106
+ await assertionOfsucceedWithAFullFlowWithClient(client);
107
+ });
108
+
109
+ it('should succeed with a full flow with the client using OpenID4VCI draft > 11, https and authorization_code flow', async () => {
110
+ succeedWithAFullFlowWithClientSetup();
111
+ const client = await OpenID4VCIClient.fromURI({
112
+ uri: HTTPS_OFFER_QR_AUTHORIZATION_CODE,
113
+ kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
114
+ alg: Alg.ES256,
115
+ clientId: 'test-clientId',
116
+ });
117
+ await assertionOfsucceedWithAFullFlowWithClient(client);
118
+ });
119
+
120
+ it('should succeed with a full flow with the client using OpenID4VCI draft > 11, https and preauthorized_code flow', async () => {
121
+ succeedWithAFullFlowWithClientSetup();
122
+ const client = await OpenID4VCIClient.fromURI({
123
+ uri: HTTPS_OFFER_QR_PRE_AUTHORIZED,
124
+ kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
125
+ alg: Alg.ES256,
126
+ clientId: 'test-clientId',
127
+ });
128
+ await assertionOfsucceedWithAFullFlowWithClient(client);
129
+ });
130
+
92
131
  async function assertionOfsucceedWithAFullFlowWithClient(client: OpenID4VCIClient) {
93
132
  expect(client.credentialOffer).toBeDefined();
94
133
  expect(client.endpointMetadata).toBeDefined();
@@ -96,7 +135,7 @@ describe('OID4VCI-Client should', () => {
96
135
  expect(client.getCredentialEndpoint()).toEqual('https://issuer.research.identiproof.io/credential');
97
136
  expect(client.getAccessTokenEndpoint()).toEqual('https://auth.research.identiproof.io/oauth2/token');
98
137
 
99
- const accessToken = await client.acquireAccessToken({ pin: '1234' });
138
+ const accessToken = await client.acquireAccessToken({ pin: '1234', code: 'ABCD' });
100
139
  expect(accessToken).toEqual(mockedAccessTokenResponse);
101
140
 
102
141
  const credentialResponse = await client.acquireCredentials({
@@ -58,4 +58,26 @@ describe('Issuance Initiation', () => {
58
58
  expect(client.credential_offer.credential_issuer).toEqual('https://launchpad.vii.electron.mattrlabs.io');
59
59
  expect(client.preAuthorizedCode).toEqual('UPZohaodPlLBnGsqB02n2tIupCIg8nKRRUEUHWA665X');
60
60
  });
61
+
62
+ it('Should take an https url as input and return a Credential Offer', async () => {
63
+ const client = await CredentialOfferClient.fromURI(
64
+ 'https://launchpad.vii.electron.mattrlabs.io?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Flaunchpad.vii.electron.mattrlabs.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22ldp_vc%22%2C%22types%22%3A%5B%22OpenBadgeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22UPZohaodPlLBnGsqB02n2tIupCIg8nKRRUEUHWA665X%22%7D%7D%7D',
65
+ );
66
+ expect(client.version).toEqual(OpenId4VCIVersion.VER_1_0_11);
67
+ expect(client.baseUrl).toEqual('https://launchpad.vii.electron.mattrlabs.io');
68
+ expect(client.scheme).toEqual('https');
69
+ expect(client.credential_offer.credential_issuer).toEqual('https://launchpad.vii.electron.mattrlabs.io');
70
+ expect(client.preAuthorizedCode).toEqual('UPZohaodPlLBnGsqB02n2tIupCIg8nKRRUEUHWA665X');
71
+ })
72
+
73
+ it('Should take an http url as input and return a Credential Offer', async () => {
74
+ const client = await CredentialOfferClient.fromURI(
75
+ 'http://launchpad.vii.electron.mattrlabs.io?credential_offer=%7B%22credential_issuer%22%3A%22http%3A%2F%2Flaunchpad.vii.electron.mattrlabs.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22ldp_vc%22%2C%22types%22%3A%5B%22OpenBadgeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22UPZohaodPlLBnGsqB02n2tIupCIg8nKRRUEUHWA665X%22%7D%7D%7D',
76
+ );
77
+ expect(client.version).toEqual(OpenId4VCIVersion.VER_1_0_11);
78
+ expect(client.baseUrl).toEqual('http://launchpad.vii.electron.mattrlabs.io');
79
+ expect(client.scheme).toEqual('http');
80
+ expect(client.credential_offer.credential_issuer).toEqual('http://launchpad.vii.electron.mattrlabs.io');
81
+ expect(client.preAuthorizedCode).toEqual('UPZohaodPlLBnGsqB02n2tIupCIg8nKRRUEUHWA665X');
82
+ })
61
83
  });
@@ -9,7 +9,7 @@ import { IDENTIPROOF_ISSUER_URL } from './MetadataMocks';
9
9
 
10
10
  const jwt: Jwt = {
11
11
  header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'jwt' },
12
- payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now()/1000 },
12
+ payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now() / 1000 },
13
13
  };
14
14
 
15
15
  const kid = 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1';
@@ -43,7 +43,7 @@ const vcIssuer = new VcIssuerBuilder()
43
43
  },
44
44
  payload: {
45
45
  aud: issuerMetadata.credential_issuer,
46
- iat: +new Date()/1000,
46
+ iat: +new Date() / 1000,
47
47
  nonce: 'a-c-nonce',
48
48
  },
49
49
  },
@@ -152,6 +152,8 @@ describe('sd-jwt vc', () => {
152
152
  });
153
153
 
154
154
  expect(credentials).toEqual({
155
+ notification_id: expect.any(String),
156
+ access_token: 'ey.val.ue',
155
157
  c_nonce: 'new-c-nonce',
156
158
  c_nonce_expires_in: 300,
157
159
  credential: 'sd-jwt',
@@ -115,7 +115,8 @@ async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promi
115
115
  }
116
116
 
117
117
  describe('ismapolis bug report #63, https://github.com/Sphereon-Opensource/OID4VC-demo/issues/63, should', () => {
118
- it('work as expected provided a correct JWT is supplied', async () => {
118
+ // Sphereon infra is not working currently
119
+ it.skip('work as expected provided a correct JWT is supplied', async () => {
119
120
  debug.enable('*');
120
121
  const { uri } = await getCredentialOffer('jwt_vc_json');
121
122
  const client = await OpenID4VCIClient.fromURI({ uri: uri, clientId: 'test-clientID' });
@@ -1,3 +1 @@
1
- export * from '@sphereon/oid4vci-common/dist/functions/Encoding';
2
- export * from '@sphereon/oid4vci-common/dist/functions/HttpUtils';
3
- export * from './ProofUtil';
1
+ export * from './AuthorizationUtil';
@@ -0,0 +1,32 @@
1
+ import { NotificationErrorResponse, NotificationRequest, NotificationResult, post } from '@sphereon/oid4vci-common';
2
+
3
+ import { CredentialRequestOpts } from '../CredentialRequestClient';
4
+ import { LOG } from '../types';
5
+
6
+ export async function sendNotification(
7
+ credentialRequestOpts: CredentialRequestOpts,
8
+ request: NotificationRequest,
9
+ accessToken?: string,
10
+ ): Promise<NotificationResult> {
11
+ LOG.info(`Sending status notification event '${request.event}' for id ${request.notification_id}`);
12
+ if (!credentialRequestOpts.notificationEndpoint) {
13
+ throw Error(`Cannot send notification when no notification endpoint is provided`);
14
+ }
15
+ const token = accessToken ?? credentialRequestOpts.token;
16
+ const response = await post<NotificationErrorResponse>(credentialRequestOpts.notificationEndpoint, JSON.stringify(request), {
17
+ bearerToken: token,
18
+ });
19
+ const error = response.errorBody?.error !== undefined;
20
+ const result = {
21
+ error,
22
+ response: error ? await response.errorBody?.json() : undefined,
23
+ };
24
+ if (error) {
25
+ LOG.warning(
26
+ `Notification endpoint returned an error for event '${request.event}' and id ${request.notification_id}: ${await response.errorBody?.json()}`,
27
+ );
28
+ } else {
29
+ LOG.debug(`Notification endpoint returned success for event '${request.event}' and id ${request.notification_id}`);
30
+ }
31
+ return result;
32
+ }
@@ -0,0 +1,6 @@
1
+ import { VCI_LOGGERS } from '@sphereon/oid4vci-common';
2
+ import { ISimpleLogger, LogMethod } from '@sphereon/ssi-types';
3
+
4
+ export const LOG: ISimpleLogger<string> = VCI_LOGGERS.options('sphereon:oid4vci:client', { methods: [LogMethod.EVENT, LogMethod.DEBUG_PKG] }).get(
5
+ 'sphereon:oid4vci:client',
6
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/oid4vci-client",
3
- "version": "0.10.3",
3
+ "version": "0.10.4-next.17+126f976",
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.3",
19
- "@sphereon/ssi-types": "^0.23.0",
18
+ "@sphereon/oid4vci-common": "0.10.4-next.17+126f976",
19
+ "@sphereon/ssi-types": "0.24.1-unstable.112",
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": "4d46a7282a475c4e78d496f82f24114f700ee5e0"
72
+ "gitHead": "126f97644a247d3c86faea8b1bd96e68f8bcf52c"
73
73
  }
@@ -1,30 +0,0 @@
1
- import { JWK, Jwt, ProofOfPossession, ProofOfPossessionCallbacks, Typ } from '@sphereon/oid4vci-common';
2
- /**
3
- *
4
- * - proofOfPossessionCallback: JWTSignerCallback
5
- * Mandatory if you want to create (sign) ProofOfPossession
6
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
7
- * If exists, verifies the ProofOfPossession
8
- * - proofOfPossessionCallbackArgs: ProofOfPossessionCallbackArgs
9
- * arguments needed for signing ProofOfPossession
10
- * @param callbacks:
11
- * - proofOfPossessionCallback: JWTSignerCallback
12
- * Mandatory to create (sign) ProofOfPossession
13
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
14
- * If exists, verifies the ProofOfPossession
15
- * @param jwtProps
16
- * @param existingJwt
17
- * - Optional, clientId of the party requesting the credential
18
- */
19
- export declare const createProofOfPossession: <DIDDoc>(callbacks: ProofOfPossessionCallbacks<DIDDoc>, jwtProps?: JwtProps, existingJwt?: Jwt) => Promise<ProofOfPossession>;
20
- export interface JwtProps {
21
- typ?: Typ;
22
- kid?: string;
23
- jwk?: JWK;
24
- issuer?: string;
25
- clientId?: string;
26
- alg?: string;
27
- jti?: string;
28
- nonce?: string;
29
- }
30
- //# sourceMappingURL=ProofUtil.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProofUtil.d.ts","sourceRoot":"","sources":["../../lib/functions/ProofUtil.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,GAAG,EAEH,GAAG,EAGH,iBAAiB,EACjB,0BAA0B,EAC1B,GAAG,EACJ,MAAM,0BAA0B,CAAC;AAKlC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,qEAEvB,QAAQ,gBACL,GAAG,KAChB,QAAQ,iBAAiB,CA0B3B,CAAC;AAQF,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -1,106 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.createProofOfPossession = void 0;
16
- const oid4vci_common_1 = require("@sphereon/oid4vci-common");
17
- const debug_1 = __importDefault(require("debug"));
18
- const debug = (0, debug_1.default)('sphereon:openid4vci:token');
19
- /**
20
- *
21
- * - proofOfPossessionCallback: JWTSignerCallback
22
- * Mandatory if you want to create (sign) ProofOfPossession
23
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
24
- * If exists, verifies the ProofOfPossession
25
- * - proofOfPossessionCallbackArgs: ProofOfPossessionCallbackArgs
26
- * arguments needed for signing ProofOfPossession
27
- * @param callbacks:
28
- * - proofOfPossessionCallback: JWTSignerCallback
29
- * Mandatory to create (sign) ProofOfPossession
30
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
31
- * If exists, verifies the ProofOfPossession
32
- * @param jwtProps
33
- * @param existingJwt
34
- * - Optional, clientId of the party requesting the credential
35
- */
36
- const createProofOfPossession = (callbacks, jwtProps, existingJwt) => __awaiter(void 0, void 0, void 0, function* () {
37
- if (!callbacks.signCallback) {
38
- debug(`no jwt signer callback or arguments supplied!`);
39
- throw new Error(oid4vci_common_1.BAD_PARAMS);
40
- }
41
- const signerArgs = createJWT(jwtProps, existingJwt);
42
- const jwt = yield callbacks.signCallback(signerArgs, signerArgs.header.kid);
43
- const proof = {
44
- proof_type: 'jwt',
45
- jwt,
46
- };
47
- try {
48
- partiallyValidateJWS(jwt);
49
- if (callbacks.verifyCallback) {
50
- debug(`Calling supplied verify callback....`);
51
- yield callbacks.verifyCallback({ jwt, kid: signerArgs.header.kid });
52
- debug(`Supplied verify callback return success result`);
53
- }
54
- }
55
- catch (_a) {
56
- debug(`JWS was not valid`);
57
- throw new Error(oid4vci_common_1.JWS_NOT_VALID);
58
- }
59
- debug(`Proof of Possession JWT:\r\n${jwt}`);
60
- return proof;
61
- });
62
- exports.createProofOfPossession = createProofOfPossession;
63
- const partiallyValidateJWS = (jws) => {
64
- if (jws.split('.').length !== 3 || !jws.startsWith('ey')) {
65
- throw new Error(oid4vci_common_1.JWS_NOT_VALID);
66
- }
67
- };
68
- const createJWT = (jwtProps, existingJwt) => {
69
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
70
- const aud = getJwtProperty('aud', true, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.issuer, (_a = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.payload) === null || _a === void 0 ? void 0 : _a.aud);
71
- const iss = getJwtProperty('iss', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.clientId, (_b = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.payload) === null || _b === void 0 ? void 0 : _b.iss);
72
- const jti = getJwtProperty('jti', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.jti, (_c = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.payload) === null || _c === void 0 ? void 0 : _c.jti);
73
- const typ = getJwtProperty('typ', true, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.typ, (_d = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.header) === null || _d === void 0 ? void 0 : _d.typ, 'jwt');
74
- const nonce = getJwtProperty('nonce', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.nonce, (_e = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.payload) === null || _e === void 0 ? void 0 : _e.nonce); // Officially this is required, but some implementations don't have it
75
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
76
- const alg = getJwtProperty('alg', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.alg, (_f = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.header) === null || _f === void 0 ? void 0 : _f.alg, 'ES256');
77
- const kid = getJwtProperty('kid', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.kid, (_g = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.header) === null || _g === void 0 ? void 0 : _g.kid);
78
- const jwk = getJwtProperty('jwk', false, jwtProps === null || jwtProps === void 0 ? void 0 : jwtProps.jwk, (_h = existingJwt === null || existingJwt === void 0 ? void 0 : existingJwt.header) === null || _h === void 0 ? void 0 : _h.jwk);
79
- const jwt = existingJwt ? existingJwt : {};
80
- const now = +new Date();
81
- const jwtPayload = Object.assign(Object.assign({ aud, iat: (_k = (_j = jwt.payload) === null || _j === void 0 ? void 0 : _j.iat) !== null && _k !== void 0 ? _k : Math.round(now / 1000 - 60), exp: (_m = (_l = jwt.payload) === null || _l === void 0 ? void 0 : _l.exp) !== null && _m !== void 0 ? _m : Math.round(now / 1000 + 10 * 60), nonce }, (iss ? { iss } : {})), (jti ? { jti } : {}));
82
- const jwtHeader = {
83
- typ,
84
- alg,
85
- kid,
86
- jwk,
87
- };
88
- return {
89
- payload: Object.assign(Object.assign({}, jwt.payload), jwtPayload),
90
- header: Object.assign(Object.assign({}, jwt.header), jwtHeader),
91
- };
92
- };
93
- const getJwtProperty = (propertyName, required, option, jwtProperty, defaultValue) => {
94
- if (typeof option === 'string' && option && jwtProperty && option !== jwtProperty) {
95
- throw Error(`Cannot have a property '${propertyName}' with value '${option}' and different JWT value '${jwtProperty}' at the same time`);
96
- }
97
- let result = (jwtProperty ? jwtProperty : option);
98
- if (!result) {
99
- if (required) {
100
- throw Error(`No ${propertyName} property provided either in a JWT or as option`);
101
- }
102
- result = defaultValue;
103
- }
104
- return result;
105
- };
106
- //# sourceMappingURL=ProofUtil.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProofUtil.js","sourceRoot":"","sources":["../../lib/functions/ProofUtil.ts"],"names":[],"mappings":"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"}
@@ -1,128 +0,0 @@
1
- import {
2
- BAD_PARAMS,
3
- BaseJWK,
4
- JWK,
5
- JWS_NOT_VALID,
6
- Jwt,
7
- JWTHeader,
8
- JWTPayload,
9
- ProofOfPossession,
10
- ProofOfPossessionCallbacks,
11
- Typ,
12
- } from '@sphereon/oid4vci-common';
13
- import Debug from 'debug';
14
-
15
- const debug = Debug('sphereon:openid4vci:token');
16
-
17
- /**
18
- *
19
- * - proofOfPossessionCallback: JWTSignerCallback
20
- * Mandatory if you want to create (sign) ProofOfPossession
21
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
22
- * If exists, verifies the ProofOfPossession
23
- * - proofOfPossessionCallbackArgs: ProofOfPossessionCallbackArgs
24
- * arguments needed for signing ProofOfPossession
25
- * @param callbacks:
26
- * - proofOfPossessionCallback: JWTSignerCallback
27
- * Mandatory to create (sign) ProofOfPossession
28
- * - proofOfPossessionVerifierCallback?: JWTVerifyCallback
29
- * If exists, verifies the ProofOfPossession
30
- * @param jwtProps
31
- * @param existingJwt
32
- * - Optional, clientId of the party requesting the credential
33
- */
34
- export const createProofOfPossession = async <DIDDoc>(
35
- callbacks: ProofOfPossessionCallbacks<DIDDoc>,
36
- jwtProps?: JwtProps,
37
- existingJwt?: Jwt,
38
- ): Promise<ProofOfPossession> => {
39
- if (!callbacks.signCallback) {
40
- debug(`no jwt signer callback or arguments supplied!`);
41
- throw new Error(BAD_PARAMS);
42
- }
43
-
44
- const signerArgs = createJWT(jwtProps, existingJwt);
45
- const jwt = await callbacks.signCallback(signerArgs, signerArgs.header.kid);
46
- const proof = {
47
- proof_type: 'jwt',
48
- jwt,
49
- } as ProofOfPossession;
50
-
51
- try {
52
- partiallyValidateJWS(jwt);
53
- if (callbacks.verifyCallback) {
54
- debug(`Calling supplied verify callback....`);
55
- await callbacks.verifyCallback({ jwt, kid: signerArgs.header.kid });
56
- debug(`Supplied verify callback return success result`);
57
- }
58
- } catch {
59
- debug(`JWS was not valid`);
60
- throw new Error(JWS_NOT_VALID);
61
- }
62
- debug(`Proof of Possession JWT:\r\n${jwt}`);
63
- return proof;
64
- };
65
-
66
- const partiallyValidateJWS = (jws: string): void => {
67
- if (jws.split('.').length !== 3 || !jws.startsWith('ey')) {
68
- throw new Error(JWS_NOT_VALID);
69
- }
70
- };
71
-
72
- export interface JwtProps {
73
- typ?: Typ;
74
- kid?: string;
75
- jwk?: JWK;
76
- issuer?: string;
77
- clientId?: string;
78
- alg?: string;
79
- jti?: string;
80
- nonce?: string;
81
- }
82
-
83
- const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => {
84
- const aud = getJwtProperty<string | string[]>('aud', true, jwtProps?.issuer, existingJwt?.payload?.aud);
85
- const iss = getJwtProperty<string>('iss', false, jwtProps?.clientId, existingJwt?.payload?.iss);
86
- const jti = getJwtProperty<string>('jti', false, jwtProps?.jti, existingJwt?.payload?.jti);
87
- const typ = getJwtProperty<string>('typ', true, jwtProps?.typ, existingJwt?.header?.typ, 'jwt');
88
- const nonce = getJwtProperty<string>('nonce', false, jwtProps?.nonce, existingJwt?.payload?.nonce); // Officially this is required, but some implementations don't have it
89
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
90
- const alg = getJwtProperty<string>('alg', false, jwtProps?.alg, existingJwt?.header?.alg, 'ES256')!;
91
- const kid = getJwtProperty<string>('kid', false, jwtProps?.kid, existingJwt?.header?.kid);
92
- const jwk = getJwtProperty<BaseJWK>('jwk', false, jwtProps?.jwk, existingJwt?.header?.jwk);
93
- const jwt: Partial<Jwt> = existingJwt ? existingJwt : {};
94
- const now = +new Date();
95
- const jwtPayload: Partial<JWTPayload> = {
96
- aud,
97
- iat: jwt.payload?.iat ?? Math.round(now / 1000 - 60), // Let's ensure we subtract 60 seconds for potential time offsets
98
- exp: jwt.payload?.exp ?? Math.round(now / 1000 + 10 * 60),
99
- nonce,
100
- ...(iss ? { iss } : {}),
101
- ...(jti ? { jti } : {}),
102
- };
103
-
104
- const jwtHeader: JWTHeader = {
105
- typ,
106
- alg,
107
- kid,
108
- jwk,
109
- };
110
- return {
111
- payload: { ...jwt.payload, ...jwtPayload },
112
- header: { ...jwt.header, ...jwtHeader },
113
- };
114
- };
115
-
116
- const getJwtProperty = <T>(propertyName: string, required: boolean, option?: string | JWK, jwtProperty?: T, defaultValue?: T): T | undefined => {
117
- if (typeof option === 'string' && option && jwtProperty && option !== jwtProperty) {
118
- throw Error(`Cannot have a property '${propertyName}' with value '${option}' and different JWT value '${jwtProperty}' at the same time`);
119
- }
120
- let result = (jwtProperty ? jwtProperty : option) as T | undefined;
121
- if (!result) {
122
- if (required) {
123
- throw Error(`No ${propertyName} property provided either in a JWT or as option`);
124
- }
125
- result = defaultValue;
126
- }
127
- return result;
128
- };