@sphereon/oid4vci-client 0.8.2-next.6 → 0.8.2-next.88

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 (72) hide show
  1. package/README.md +9 -8
  2. package/dist/AccessTokenClient.d.ts +0 -1
  3. package/dist/AccessTokenClient.d.ts.map +1 -1
  4. package/dist/AccessTokenClient.js +11 -18
  5. package/dist/AccessTokenClient.js.map +1 -1
  6. package/dist/AuthorizationCodeClient.d.ts +9 -0
  7. package/dist/AuthorizationCodeClient.d.ts.map +1 -0
  8. package/dist/AuthorizationCodeClient.js +132 -0
  9. package/dist/AuthorizationCodeClient.js.map +1 -0
  10. package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -1
  11. package/dist/AuthorizationDetailsBuilder.js.map +1 -1
  12. package/dist/CredentialOfferClient.d.ts.map +1 -1
  13. package/dist/CredentialOfferClient.js +3 -1
  14. package/dist/CredentialOfferClient.js.map +1 -1
  15. package/dist/CredentialRequestClient.d.ts +15 -0
  16. package/dist/CredentialRequestClient.d.ts.map +1 -1
  17. package/dist/CredentialRequestClient.js +91 -43
  18. package/dist/CredentialRequestClient.js.map +1 -1
  19. package/dist/CredentialRequestClientBuilder.d.ts +19 -7
  20. package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
  21. package/dist/CredentialRequestClientBuilder.js +31 -1
  22. package/dist/CredentialRequestClientBuilder.js.map +1 -1
  23. package/dist/MetadataClient.d.ts.map +1 -1
  24. package/dist/MetadataClient.js +12 -1
  25. package/dist/MetadataClient.js.map +1 -1
  26. package/dist/OpenID4VCIClient.d.ts +62 -27
  27. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  28. package/dist/OpenID4VCIClient.js +255 -176
  29. package/dist/OpenID4VCIClient.js.map +1 -1
  30. package/dist/ProofOfPossessionBuilder.d.ts +3 -1
  31. package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
  32. package/dist/ProofOfPossessionBuilder.js +5 -0
  33. package/dist/ProofOfPossessionBuilder.js.map +1 -1
  34. package/dist/functions/AuthorizationUtil.d.ts +3 -0
  35. package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
  36. package/dist/functions/AuthorizationUtil.js +22 -0
  37. package/dist/functions/AuthorizationUtil.js.map +1 -0
  38. package/dist/functions/ProofUtil.d.ts +2 -1
  39. package/dist/functions/ProofUtil.d.ts.map +1 -1
  40. package/dist/functions/ProofUtil.js +6 -4
  41. package/dist/functions/ProofUtil.js.map +1 -1
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/types/index.js +2 -0
  49. package/dist/types/index.js.map +1 -0
  50. package/lib/AccessTokenClient.ts +16 -20
  51. package/lib/AuthorizationCodeClient.ts +163 -0
  52. package/lib/AuthorizationDetailsBuilder.ts +2 -2
  53. package/lib/CredentialOfferClient.ts +4 -1
  54. package/lib/CredentialRequestClient.ts +116 -45
  55. package/lib/CredentialRequestClientBuilder.ts +53 -8
  56. package/lib/MetadataClient.ts +13 -1
  57. package/lib/OpenID4VCIClient.ts +348 -216
  58. package/lib/ProofOfPossessionBuilder.ts +8 -0
  59. package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
  60. package/lib/__tests__/CredentialRequestClient.spec.ts +28 -8
  61. package/lib/__tests__/EBSIE2E.spec.test.ts +145 -0
  62. package/lib/__tests__/MetadataClient.spec.ts +4 -1
  63. package/lib/__tests__/OpenID4VCIClient.spec.ts +117 -76
  64. package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
  65. package/lib/__tests__/SdJwt.spec.ts +163 -0
  66. package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
  67. package/lib/__tests__/data/VciDataFixtures.ts +14 -13
  68. package/lib/functions/AuthorizationUtil.ts +18 -0
  69. package/lib/functions/ProofUtil.ts +18 -4
  70. package/lib/index.ts +1 -0
  71. package/lib/types/index.ts +0 -0
  72. package/package.json +8 -6
@@ -1,4 +1,6 @@
1
- import { CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common';
1
+ import { PARMode, WellKnownEndpoints } from '@sphereon/oid4vci-common';
2
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
+ // @ts-ignore
2
4
  import nock from 'nock';
3
5
 
4
6
  import { OpenID4VCIClient } from '../OpenID4VCIClient';
@@ -13,6 +15,7 @@ describe('OpenID4VCIClient', () => {
13
15
  nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
14
16
  nock(`${MOCK_URL}`).post('/v1/auth/par').reply(201, { request_uri: 'test_uri', expires_in: 90 });
15
17
  client = await OpenID4VCIClient.fromURI({
18
+ createAuthorizationRequestURL: false,
16
19
  clientId: 'test-client',
17
20
  uri: 'openid-initiate-issuance://?issuer=https://server.example.com&credential_type=TestCredential',
18
21
  });
@@ -25,53 +28,58 @@ describe('OpenID4VCIClient', () => {
25
28
  it('should successfully retrieve the authorization code using PAR', async () => {
26
29
  client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`;
27
30
  client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
28
- const actual = await client.acquirePushedAuthorizationRequestURI({
29
- codeChallengeMethod: CodeChallengeMethod.SHA256,
30
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
31
- scope: 'openid TestCredential',
32
- redirectUri: 'http://localhost:8881/cb',
31
+ const actual = await client.createAuthorizationRequestUrl({
32
+ authorizationRequest: {
33
+ parMode: PARMode.REQUIRE,
34
+ scope: 'openid TestCredential',
35
+ redirectUri: 'http://localhost:8881/cb',
36
+ },
33
37
  });
34
38
  expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri');
35
39
  });
36
40
 
37
41
  it('should fail when pushed_authorization_request_endpoint is not present', async () => {
42
+ client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
38
43
  await expect(() =>
39
- client.acquirePushedAuthorizationRequestURI({
40
- codeChallengeMethod: CodeChallengeMethod.SHA256,
41
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
42
- scope: 'openid TestCredential',
43
- redirectUri: 'http://localhost:8881/cb',
44
+ client.createAuthorizationRequestUrl({
45
+ authorizationRequest: {
46
+ parMode: PARMode.REQUIRE,
47
+ scope: 'openid TestCredential',
48
+ redirectUri: 'http://localhost:8881/cb',
49
+ },
44
50
  }),
45
- ).rejects.toThrow(Error('Server metadata does not contain pushed authorization request endpoint'));
51
+ ).rejects.toThrow(Error('PAR mode is set to required by Authorization Server does not support PAR!'));
46
52
  });
47
53
 
48
54
  it('should fail when authorization_details and scope are not present', async () => {
49
55
  await expect(() =>
50
- client.acquirePushedAuthorizationRequestURI({
51
- codeChallengeMethod: CodeChallengeMethod.SHA256,
52
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
53
- redirectUri: 'http://localhost:8881/cb',
56
+ client.createAuthorizationRequestUrl({
57
+ authorizationRequest: {
58
+ parMode: PARMode.REQUIRE,
59
+ redirectUri: 'http://localhost:8881/cb',
60
+ },
54
61
  }),
55
- ).rejects.toThrow(Error('Please provide a scope or authorization_details'));
62
+ ).rejects.toThrow(Error('Could not create authorization details from credential offer. Please pass in explicit details'));
56
63
  });
57
64
 
58
65
  it('should not fail when only authorization_details is present', async () => {
59
66
  client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`;
60
67
  client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
61
- const actual = await client.acquirePushedAuthorizationRequestURI({
62
- codeChallengeMethod: CodeChallengeMethod.SHA256,
63
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
64
- authorizationDetails: [
65
- {
66
- type: 'openid_credential',
67
- format: 'ldp_vc',
68
- credential_definition: {
69
- '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
70
- types: ['VerifiableCredential', 'UniversityDegreeCredential'],
68
+ const actual = await client.createAuthorizationRequestUrl({
69
+ authorizationRequest: {
70
+ parMode: PARMode.REQUIRE,
71
+ authorizationDetails: [
72
+ {
73
+ type: 'openid_credential',
74
+ format: 'ldp_vc',
75
+ credential_definition: {
76
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
77
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
78
+ },
71
79
  },
72
- },
73
- ],
74
- redirectUri: 'http://localhost:8881/cb',
80
+ ],
81
+ redirectUri: 'http://localhost:8881/cb',
82
+ },
75
83
  });
76
84
  expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri');
77
85
  });
@@ -79,11 +87,12 @@ describe('OpenID4VCIClient', () => {
79
87
  it('should not fail when only scope is present', async () => {
80
88
  client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`;
81
89
  client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
82
- const actual = await client.acquirePushedAuthorizationRequestURI({
83
- codeChallengeMethod: CodeChallengeMethod.SHA256,
84
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
85
- scope: 'openid TestCredential',
86
- redirectUri: 'http://localhost:8881/cb',
90
+ const actual = await client.createAuthorizationRequestUrl({
91
+ authorizationRequest: {
92
+ parMode: PARMode.REQUIRE,
93
+ scope: 'openid TestCredential',
94
+ redirectUri: 'http://localhost:8881/cb',
95
+ },
87
96
  });
88
97
  expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri');
89
98
  });
@@ -91,21 +100,22 @@ describe('OpenID4VCIClient', () => {
91
100
  it('should not fail when both authorization_details and scope are present', async () => {
92
101
  client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`;
93
102
  client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
94
- const actual = await client.acquirePushedAuthorizationRequestURI({
95
- codeChallengeMethod: CodeChallengeMethod.SHA256,
96
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
97
- authorizationDetails: [
98
- {
99
- type: 'openid_credential',
100
- format: 'ldp_vc',
101
- credential_definition: {
102
- '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
103
- types: ['VerifiableCredential', 'UniversityDegreeCredential'],
103
+ const actual = await client.createAuthorizationRequestUrl({
104
+ authorizationRequest: {
105
+ parMode: PARMode.REQUIRE,
106
+ authorizationDetails: [
107
+ {
108
+ type: 'openid_credential',
109
+ format: 'ldp_vc',
110
+ credential_definition: {
111
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
112
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
113
+ },
104
114
  },
105
- },
106
- ],
107
- scope: 'openid TestCredential',
108
- redirectUri: 'http://localhost:8881/cb',
115
+ ],
116
+ scope: 'openid TestCredential',
117
+ redirectUri: 'http://localhost:8881/cb',
118
+ },
109
119
  });
110
120
  expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri');
111
121
  });
@@ -0,0 +1,163 @@
1
+ import { AccessTokenRequest, CredentialRequestV1_0_11, CredentialSupportedSdJwtVc } 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 { OpenID4VCIClient } from '..';
7
+ import { createAccessTokenResponse, IssuerMetadataBuilderV1_11, VcIssuerBuilder } from '../../../issuer';
8
+
9
+ export const UNIT_TEST_TIMEOUT = 30000;
10
+
11
+ const alg = 'ES256';
12
+ const jwk = { kty: 'EC', crv: 'P-256', x: 'zQOowIC1gWJtdddB5GAt4lau6Lt8Ihy771iAfam-1pc', y: 'cjD_7o3gdQ1vgiQy3_sMGs7WrwCMU9FQYimA3HxnMlw' };
13
+
14
+ const issuerMetadata = new IssuerMetadataBuilderV1_11()
15
+ .withCredentialIssuer('https://example.com')
16
+ .withCredentialEndpoint('https://credenital-endpoint.example.com')
17
+ .withTokenEndpoint('https://token-endpoint.example.com')
18
+ .addSupportedCredential({
19
+ format: 'vc+sd-jwt',
20
+ vct: 'SdJwtCredential',
21
+ id: 'SdJwtCredentialId',
22
+ })
23
+ .build();
24
+
25
+ const vcIssuer = new VcIssuerBuilder()
26
+ .withIssuerMetadata(issuerMetadata)
27
+ .withInMemoryCNonceState()
28
+ .withInMemoryCredentialOfferState()
29
+ .withInMemoryCredentialOfferURIState()
30
+ // TODO: see if we can construct an sd-jwt vc based on the input
31
+ .withCredentialSignerCallback(async () => {
32
+ return 'sd-jwt';
33
+ })
34
+ .withJWTVerifyCallback(() =>
35
+ Promise.resolve({
36
+ alg,
37
+ jwk,
38
+ jwt: {
39
+ header: {
40
+ typ: 'openid4vci-proof+jwt',
41
+ alg,
42
+ jwk,
43
+ },
44
+ payload: {
45
+ aud: issuerMetadata.credential_issuer,
46
+ iat: +new Date(),
47
+ nonce: 'a-c-nonce',
48
+ },
49
+ },
50
+ }),
51
+ )
52
+ .build();
53
+
54
+ describe('sd-jwt vc', () => {
55
+ beforeEach(() => {
56
+ nock.cleanAll();
57
+ });
58
+ afterEach(() => {
59
+ nock.cleanAll();
60
+ });
61
+
62
+ it(
63
+ 'succeed with a full flow',
64
+ async () => {
65
+ const offerUri = await vcIssuer.createCredentialOfferURI({
66
+ grants: {
67
+ 'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
68
+ 'pre-authorized_code': '123',
69
+ user_pin_required: false,
70
+ },
71
+ },
72
+ credentials: ['SdJwtCredentialId'],
73
+ });
74
+
75
+ nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(issuerMetadata));
76
+ nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/openid-configuration').reply(404);
77
+ nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/oauth-authorization-server').reply(404);
78
+
79
+ expect(offerUri.uri).toEqual(
80
+ '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%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22SdJwtCredentialId%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%7D',
81
+ );
82
+
83
+ const client = await OpenID4VCIClient.fromURI({
84
+ uri: offerUri.uri,
85
+ });
86
+
87
+ expect(client.credentialOffer?.credential_offer).toEqual({
88
+ credential_issuer: 'https://example.com',
89
+ credentials: ['SdJwtCredentialId'],
90
+ grants: {
91
+ 'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
92
+ 'pre-authorized_code': '123',
93
+ user_pin_required: false,
94
+ },
95
+ },
96
+ });
97
+
98
+ const supported = client.getCredentialsSupported(true, 'vc+sd-jwt');
99
+ expect(supported).toEqual([
100
+ {
101
+ vct: 'SdJwtCredential',
102
+ format: 'vc+sd-jwt',
103
+ id: 'SdJwtCredentialId',
104
+ },
105
+ ]);
106
+
107
+ const offered = supported[0] as CredentialSupportedSdJwtVc;
108
+
109
+ nock(issuerMetadata.token_endpoint as string)
110
+ .post('/')
111
+ .reply(200, async (_, body: string) => {
112
+ const parsedBody = Object.fromEntries(body.split('&').map((x) => x.split('=')));
113
+ return createAccessTokenResponse(parsedBody as AccessTokenRequest, {
114
+ credentialOfferSessions: vcIssuer.credentialOfferSessions,
115
+ accessTokenIssuer: 'https://issuer.example.com',
116
+ cNonces: vcIssuer.cNonces,
117
+ cNonce: 'a-c-nonce',
118
+ accessTokenSignerCallback: async () => 'ey.val.ue',
119
+ tokenExpiresIn: 500,
120
+ });
121
+ });
122
+
123
+ await client.acquireAccessToken({});
124
+
125
+ nock(issuerMetadata.credential_endpoint as string)
126
+ .post('/')
127
+ .reply(200, async (_, body) =>
128
+ vcIssuer.issueCredential({
129
+ credentialRequest: body as CredentialRequestV1_0_11,
130
+ credential: {
131
+ vct: 'Hello',
132
+ iss: 'did:example:123',
133
+ iat: 123,
134
+ // Defines what can be disclosed (optional)
135
+ __disclosureFrame: {
136
+ name: true,
137
+ },
138
+ },
139
+ newCNonce: 'new-c-nonce',
140
+ }),
141
+ );
142
+
143
+ const credentials = await client.acquireCredentials({
144
+ credentialTypes: [offered.vct],
145
+ format: 'vc+sd-jwt',
146
+ alg,
147
+ jwk,
148
+ proofCallbacks: {
149
+ // When using sd-jwt for real, this jwt should include a jwk
150
+ signCallback: async () => 'ey.ja.ja',
151
+ },
152
+ });
153
+
154
+ expect(credentials).toEqual({
155
+ c_nonce: 'new-c-nonce',
156
+ c_nonce_expires_in: 300000,
157
+ credential: 'sd-jwt',
158
+ format: 'vc+sd-jwt',
159
+ });
160
+ },
161
+ UNIT_TEST_TIMEOUT,
162
+ );
163
+ });
@@ -10,7 +10,7 @@ import { v4 } from 'uuid';
10
10
 
11
11
  import { OpenID4VCIClient } from '..';
12
12
 
13
- export const UNIT_TEST_TIMEOUT = 30000;
13
+ export const UNIT_TEST_TIMEOUT = 60000;
14
14
 
15
15
  const ISSUER_URL = 'https://ssi.sphereon.com/pf3';
16
16
 
@@ -66,7 +66,7 @@ describe('OID4VCI-Client using Sphereon issuer should', () => {
66
66
  },
67
67
  UNIT_TEST_TIMEOUT,
68
68
  );
69
- it(
69
+ xit(
70
70
  'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json',
71
71
  async () => {
72
72
  await test('jwt_vc_json');
@@ -1,4 +1,4 @@
1
- import { CredentialSupportedBrief, IssuerCredentialSubjectDisplay, IssuerMetadataV1_0_08 } from '@sphereon/oid4vci-common';
1
+ import { CredentialSupportedFormatV1_0_08, IssuerCredentialSubjectDisplay, IssuerMetadataV1_0_08 } from '@sphereon/oid4vci-common';
2
2
  import { ICredentialStatus, W3CVerifiableCredential } from '@sphereon/ssi-types';
3
3
 
4
4
  export function getMockData(issuerName: string): IssuerMockData | null {
@@ -42,7 +42,8 @@ export interface IssuerMockData {
42
42
  url: string;
43
43
  deeplink: string;
44
44
  request: {
45
- types: [string];
45
+ types?: [string];
46
+ type?: string;
46
47
  format: 'jwt_vc' | 'ldp_vc' | 'jwt_vc_json-ld' | string;
47
48
  proof: {
48
49
  proof_type: 'jwt' | string;
@@ -110,8 +111,8 @@ const mockData: VciMockDataStructure = {
110
111
  deeplink:
111
112
  'openid-initiate-issuance://?issuer=https%3A%2F%2Fngi%2Doidc4vci%2Dtest%2Espruceid%2Exyz&credential_type=OpenBadgeCredential&pre-authorized_code=eyJhbGciOiJFUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOlsiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJleHAiOiIyMDIzLTA0LTIwVDA5OjA0OjM2WiIsIm5vbmNlIjoibWFibmVpT0VSZVB3V3BuRFFweEt3UnRsVVRFRlhGUEwifQ.qOZRPN8sTv_knhp7WaWte2-aDULaPZX--2i9unF6QDQNUllqDhvxgIHMDCYHCV8O2_Gj-T2x1J84fDMajE3asg&user_pin_required=false',
112
113
  request: {
113
- types: ['OpenBadgeCredential'],
114
- format: 'jwt_vc_json-ld',
114
+ type: 'OpenBadgeCredential',
115
+ format: 'jwt_vc',
115
116
  proof: {
116
117
  proof_type: 'jwt',
117
118
  jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOa3NpTENKMWMyVWlPaUp6YVdjaUxDSnJkSGtpT2lKRlF5SXNJbU55ZGlJNkluTmxZM0F5TlRack1TSXNJbmdpT2lKclpuVmpTa0V0VEhKck9VWjBPRmx5TFVkMlQzSmpia3N3YjNkc2RqUlhNblUwU3pJeFNHZHZTVlIzSWl3aWVTSTZJalozY0ZCUE1rOUNRVXBTU0ZFMVRXdEtXVlJaV0dsQlJFUXdOMU5OTlV0amVXcDNYMkUzVUUxWmVGa2lmUSMwIn0.eyJhdWQiOiJodHRwczovL25naS1vaWRjNHZjaS10ZXN0LnNwcnVjZWlkLnh5eiIsImlhdCI6MTY4MTkxMTA2MC45NDIsImV4cCI6MTY4MTkxMTcyMC45NDIsImlzcyI6InNwaGVyZW9uOnNzaS13YWxsZXQiLCJqdGkiOiJhNjA4MzMxZi02ZmE0LTQ0ZjAtYWNkZWY5NmFjMjdmNmQ3MCJ9.NwF3_41gwnlIdd_6Uk9CczeQHzIQt6UcvTT5Cxv72j9S1vNwiY9annA2kLsjsTiR5-WMBdUhJCO7wYCtZ15mxw',
@@ -357,7 +358,7 @@ const mockData: VciMockDataStructure = {
357
358
  url: 'https://jff.walt.id/issuer-api/default/oidc/credential',
358
359
  request: {
359
360
  types: ['OpenBadgeCredential'],
360
- format: 'jwt_vc_json',
361
+ format: 'jwt_vc',
361
362
  proof: {
362
363
  proof_type: 'jwt',
363
364
  jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOa3NpTENKMWMyVWlPaUp6YVdjaUxDSnJkSGtpT2lKRlF5SXNJbU55ZGlJNkluTmxZM0F5TlRack1TSXNJbmdpT2lKclpuVmpTa0V0VEhKck9VWjBPRmx5TFVkMlQzSmpia3N3YjNkc2RqUlhNblUwU3pJeFNHZHZTVlIzSWl3aWVTSTZJalozY0ZCUE1rOUNRVXBTU0ZFMVRXdEtXVlJaV0dsQlJFUXdOMU5OTlV0amVXcDNYMkUzVUUxWmVGa2lmUSMwIn0.eyJhdWQiOiJodHRwczovL2pmZi53YWx0LmlkL2lzc3Vlci1hcGkvZGVmYXVsdC9vaWRjLyIsImlhdCI6MTY4MTkxMTk0Mi4yMzgsImV4cCI6MTY4MTkxMjYwMi4yMzgsIm5vbmNlIjoiZjA2YTMxMDUtYTJlZC00NGZjLTk1NGItNGEyNTk3MDM0OTNiIiwiaXNzIjoic3BoZXJlb246c3NpLXdhbGxldCIsImp0aSI6IjA1OWM3ODA5LTlmOGYtNGE3ZS1hZDI4YTNhMTNhMGIzNmViIn0.RfiWyybxpe3nkx3b0yIsqDHQtvB1WwhDW4t0X-kijy2dsSfv2cYhSEmAzs1shg7OV4EW8fSzt_Te79xiVl6jCw',
@@ -514,7 +515,7 @@ const mockData: VciMockDataStructure = {
514
515
  types: ['PermanentResidentCard'],
515
516
  binding_methods_supported: ['did'],
516
517
  cryptographic_suites_supported: ['Ed25519Signature2018'],
517
- } as CredentialSupportedBrief,
518
+ } as CredentialSupportedFormatV1_0_08,
518
519
  },
519
520
  },
520
521
  AcademicAward: {
@@ -525,7 +526,7 @@ const mockData: VciMockDataStructure = {
525
526
  types: ['AcademicAward'],
526
527
  binding_methods_supported: ['did'],
527
528
  cryptographic_suites_supported: ['Ed25519Signature2018'],
528
- } as CredentialSupportedBrief,
529
+ } as CredentialSupportedFormatV1_0_08,
529
530
  },
530
531
  },
531
532
  LearnerProfile: {
@@ -536,7 +537,7 @@ const mockData: VciMockDataStructure = {
536
537
  types: ['LearnerProfile'],
537
538
  binding_methods_supported: ['did'],
538
539
  cryptographic_suites_supported: ['Ed25519Signature2018'],
539
- } as CredentialSupportedBrief,
540
+ } as CredentialSupportedFormatV1_0_08,
540
541
  },
541
542
  },
542
543
  OpenBadgeCredential: {
@@ -547,7 +548,7 @@ const mockData: VciMockDataStructure = {
547
548
  types: ['OpenBadgeCredential'],
548
549
  binding_methods_supported: ['did'],
549
550
  cryptographic_suites_supported: ['Ed25519Signature2018'],
550
- } as CredentialSupportedBrief,
551
+ } as CredentialSupportedFormatV1_0_08,
551
552
  },
552
553
  },
553
554
  },
@@ -573,8 +574,8 @@ const mockData: VciMockDataStructure = {
573
574
  'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=g0UCOj6RAN5AwHU6gczm_GzB4_lH6GW39Z0Dl2DOOiO',
574
575
  url: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential',
575
576
  request: {
576
- types: ['OpenBadgeCredential'],
577
- format: 'jwt_vc_json-ld',
577
+ type: 'OpenBadgeCredential',
578
+ format: 'ldp_vc',
578
579
  proof: {
579
580
  proof_type: 'jwt',
580
581
  jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3AxM3N6QUFMVFN0cDV1OGtMcnl5YW5vYWtrVWtFUGZXazdvOHY3dms0RW1KI3o2TWtwMTNzekFBTFRTdHA1dThrTHJ5eWFub2Fra1VrRVBmV2s3bzh2N3ZrNEVtSiJ9.eyJhdWQiOiJodHRwczovL2xhdW5jaHBhZC5tYXR0cmxhYnMuY29tIiwiaWF0IjoxNjgxOTE0NDgyLjUxOSwiZXhwIjoxNjgxOTE1MTQyLjUxOSwiaXNzIjoic3BoZXJlb246c3NpLXdhbGxldCIsImp0aSI6ImI5NDY1ZGE5LTY4OGYtNDdjNi04MjUwNDA0ZGNiOWI5Y2E5In0.uQ8ewOfIjy_1p_Gk6PjeEWccBJnjOca1pwbTWiCAFMQX9wlIsfeUdGtXUoHjH5_PQtpwytodx7WU456_CT9iBQ',
@@ -687,8 +688,8 @@ const mockData: VciMockDataStructure = {
687
688
  'openid-initiate-issuance://?issuer=https://oidc4vc.diwala.io&credential_type=OpenBadgeCredential&pre-authorized_code=eyJhbGciOiJIUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZXhwIjoxNjgxOTg0NDY3fQ.fEAHKz2nuWfiYHw406iNxr-81pWkNkbi31bWsYSf6Ng',
688
689
  url: 'https://oidc4vc.diwala.io/credential',
689
690
  request: {
690
- types: ['OpenBadgeCredential'],
691
- format: 'jwt_vc_json-ld',
691
+ type: 'OpenBadgeCredential',
692
+ format: 'ldp_vc',
692
693
  proof: {
693
694
  proof_type: 'jwt',
694
695
  jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3AxM3N6QUFMVFN0cDV1OGtMcnl5YW5vYWtrVWtFUGZXazdvOHY3dms0RW1KI3o2TWtwMTNzekFBTFRTdHA1dThrTHJ5eWFub2Fra1VrRVBmV2s3bzh2N3ZrNEVtSiJ9.eyJhdWQiOiJodHRwczovL29pZGM0dmMuZGl3YWxhLmlvIiwiaWF0IjoxNjgxOTE1MDk1LjIwMiwiZXhwIjoxNjgxOTE1NzU1LjIwMiwiaXNzIjoic3BoZXJlb246c3NpLXdhbGxldCIsImp0aSI6IjYxN2MwM2EzLTM3MTUtNGJlMy1hYjkxNzM4MTlmYzYxNTYzIn0.KA-cHjecaYp9FSaWHkz5cqtNyhBIVT_0I7cJnpHn03T4UWFvdhjhn8Hpe-BU247enFyWOWJ6v3NQZyZgle7xBA',
@@ -0,0 +1,18 @@
1
+ import { assertValidCodeVerifier, CodeChallengeMethod, createCodeChallenge, generateCodeVerifier, PKCEOpts } from '@sphereon/oid4vci-common';
2
+
3
+ export const generateMissingPKCEOpts = (pkce: PKCEOpts) => {
4
+ if (pkce.disabled) {
5
+ return pkce;
6
+ }
7
+ if (!pkce.codeChallengeMethod) {
8
+ pkce.codeChallengeMethod = CodeChallengeMethod.S256;
9
+ }
10
+ if (!pkce.codeVerifier) {
11
+ pkce.codeVerifier = generateCodeVerifier();
12
+ }
13
+ assertValidCodeVerifier(pkce.codeVerifier);
14
+ if (!pkce.codeChallenge) {
15
+ pkce.codeChallenge = createCodeChallenge(pkce.codeVerifier, pkce.codeChallengeMethod);
16
+ }
17
+ return pkce;
18
+ };
@@ -1,4 +1,15 @@
1
- import { BAD_PARAMS, JWS_NOT_VALID, Jwt, JWTHeader, JWTPayload, ProofOfPossession, ProofOfPossessionCallbacks, Typ } from '@sphereon/oid4vci-common';
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';
2
13
  import Debug from 'debug';
3
14
 
4
15
  const debug = Debug('sphereon:openid4vci:token');
@@ -61,6 +72,7 @@ const partiallyValidateJWS = (jws: string): void => {
61
72
  export interface JwtProps {
62
73
  typ?: Typ;
63
74
  kid?: string;
75
+ jwk?: JWK;
64
76
  issuer?: string;
65
77
  clientId?: string;
66
78
  alg?: string;
@@ -76,7 +88,8 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => {
76
88
  const nonce = getJwtProperty<string>('nonce', false, jwtProps?.nonce, existingJwt?.payload?.nonce); // Officially this is required, but some implementations don't have it
77
89
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
78
90
  const alg = getJwtProperty<string>('alg', false, jwtProps?.alg, existingJwt?.header?.alg, 'ES256')!;
79
- const kid = getJwtProperty<string>('kid', true, jwtProps?.kid, existingJwt?.header?.kid);
91
+ const kid = getJwtProperty<string>('kid', false, jwtProps?.kid, existingJwt?.header?.kid);
92
+ const jwk = getJwtProperty<BaseJWK>('jwk', false, jwtProps?.jwk, existingJwt?.header?.jwk);
80
93
  const jwt: Partial<Jwt> = existingJwt ? existingJwt : {};
81
94
  const now = +new Date();
82
95
  const jwtPayload: Partial<JWTPayload> = {
@@ -92,6 +105,7 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => {
92
105
  typ,
93
106
  alg,
94
107
  kid,
108
+ jwk,
95
109
  };
96
110
  return {
97
111
  payload: { ...jwt.payload, ...jwtPayload },
@@ -99,8 +113,8 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => {
99
113
  };
100
114
  };
101
115
 
102
- const getJwtProperty = <T>(propertyName: string, required: boolean, option?: string, jwtProperty?: T, defaultValue?: T): T | undefined => {
103
- if (option && jwtProperty && option !== jwtProperty) {
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) {
104
118
  throw Error(`Cannot have a property '${propertyName}' with value '${option}' and different JWT value '${jwtProperty}' at the same time`);
105
119
  }
106
120
  let result = (jwtProperty ? jwtProperty : option) as T | undefined;
package/lib/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './AccessTokenClient';
2
+ export * from './CredentialRequestClient';
2
3
  export * from './CredentialOfferClient';
3
4
  export * from './CredentialRequestClient';
4
5
  export * from './CredentialRequestClientBuilder';
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/oid4vci-client",
3
- "version": "0.8.2-next.6+094ebfc",
3
+ "version": "0.8.2-next.88+78abccf",
4
4
  "description": "OpenID for Verifiable Credential Issuance (OpenID4VCI) client",
5
5
  "source": "lib/index.ts",
6
6
  "main": "dist/index.js",
@@ -15,13 +15,15 @@
15
15
  "build": "tsc"
16
16
  },
17
17
  "dependencies": {
18
- "@sphereon/oid4vci-common": "0.8.2-next.6+094ebfc",
19
- "@sphereon/ssi-types": "0.17.2",
18
+ "@sphereon/oid4vci-common": "0.8.2-next.88+78abccf",
19
+ "@sphereon/ssi-types": "^0.18.1",
20
20
  "cross-fetch": "^3.1.8",
21
21
  "debug": "^4.3.4"
22
22
  },
23
23
  "devDependencies": {
24
+ "@sphereon/ssi-sdk-ext.key-utils": "^0.16.0",
24
25
  "@transmute/did-key.js": "^0.3.0-unstable.10",
26
+ "@trust/keyto": "^2.0.0-alpha1",
25
27
  "@types/jest": "^29.5.3",
26
28
  "@types/node": "^18.17.4",
27
29
  "@types/uuid": "^9.0.6",
@@ -41,12 +43,12 @@
41
43
  "open-cli": "^7.2.0",
42
44
  "ts-jest": "^29.1.1",
43
45
  "ts-node": "^10.9.1",
44
- "typescript": "4.9.5",
46
+ "typescript": "5.3.3",
45
47
  "uint8arrays": "3.1.1",
46
48
  "uuid": "^9.0.1"
47
49
  },
48
50
  "engines": {
49
- "node": ">=16"
51
+ "node": ">=18"
50
52
  },
51
53
  "files": [
52
54
  "lib/**/*",
@@ -67,5 +69,5 @@
67
69
  "OIDC4VCI",
68
70
  "OID4VCI"
69
71
  ],
70
- "gitHead": "094ebfc61e1650e3c7bb32e4a22ec576d7049f18"
72
+ "gitHead": "78abccf8164feb7c3183932fed01a1d52e71cd61"
71
73
  }