@sphereon/oid4vci-client 0.8.2-next.48 → 0.8.2-next.87

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 (47) hide show
  1. package/dist/AccessTokenClient.d.ts +0 -1
  2. package/dist/AccessTokenClient.d.ts.map +1 -1
  3. package/dist/AccessTokenClient.js +2 -9
  4. package/dist/AccessTokenClient.js.map +1 -1
  5. package/dist/AuthorizationCodeClient.d.ts +9 -0
  6. package/dist/AuthorizationCodeClient.d.ts.map +1 -0
  7. package/dist/AuthorizationCodeClient.js +124 -0
  8. package/dist/AuthorizationCodeClient.js.map +1 -0
  9. package/dist/CredentialOfferClient.d.ts.map +1 -1
  10. package/dist/CredentialOfferClient.js +3 -1
  11. package/dist/CredentialOfferClient.js.map +1 -1
  12. package/dist/CredentialRequestClient.d.ts +2 -0
  13. package/dist/CredentialRequestClient.d.ts.map +1 -1
  14. package/dist/CredentialRequestClient.js +9 -7
  15. package/dist/CredentialRequestClient.js.map +1 -1
  16. package/dist/OpenID4VCIClient.d.ts +50 -29
  17. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  18. package/dist/OpenID4VCIClient.js +191 -190
  19. package/dist/OpenID4VCIClient.js.map +1 -1
  20. package/dist/functions/AuthorizationUtil.d.ts +3 -0
  21. package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
  22. package/dist/functions/AuthorizationUtil.js +22 -0
  23. package/dist/functions/AuthorizationUtil.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/types/index.d.ts +1 -0
  29. package/dist/types/index.d.ts.map +1 -0
  30. package/dist/types/index.js +2 -0
  31. package/dist/types/index.js.map +1 -0
  32. package/lib/AccessTokenClient.ts +5 -11
  33. package/lib/AuthorizationCodeClient.ts +151 -0
  34. package/lib/CredentialOfferClient.ts +4 -1
  35. package/lib/CredentialRequestClient.ts +13 -4
  36. package/lib/OpenID4VCIClient.ts +250 -228
  37. package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
  38. package/lib/__tests__/CredentialRequestClient.spec.ts +10 -2
  39. package/lib/__tests__/EBSIE2E.spec.test.ts +8 -6
  40. package/lib/__tests__/OpenID4VCIClient.spec.ts +115 -79
  41. package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
  42. package/lib/__tests__/SdJwt.spec.ts +2 -0
  43. package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
  44. package/lib/functions/AuthorizationUtil.ts +18 -0
  45. package/lib/index.ts +1 -0
  46. package/lib/types/index.ts +0 -0
  47. package/package.json +3 -3
@@ -1,4 +1,6 @@
1
1
  import { AccessTokenRequest, AccessTokenResponse, GrantTypes, OpenIDResponse, 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 { AccessTokenClient } from '../AccessTokenClient';
@@ -157,7 +157,7 @@ describe('Credential Request Client ', () => {
157
157
  });
158
158
 
159
159
  describe('Credential Request Client with Walt.id ', () => {
160
- beforeAll(() => {
160
+ beforeEach(() => {
161
161
  nock.cleanAll();
162
162
  });
163
163
 
@@ -165,7 +165,7 @@ describe('Credential Request Client with Walt.id ', () => {
165
165
  nock.cleanAll();
166
166
  });
167
167
  it('should have correct metadata endpoints', async function () {
168
- // nock.cleanAll();
168
+ nock.cleanAll();
169
169
  const WALT_IRR_URI =
170
170
  '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';
171
171
  const credentialOffer = await CredentialOfferClient.fromURI(WALT_IRR_URI);
@@ -184,6 +184,13 @@ describe('Credential Request Client with Walt.id ', () => {
184
184
  });
185
185
 
186
186
  describe('Credential Request Client with different issuers ', () => {
187
+ beforeEach(() => {
188
+ nock.cleanAll();
189
+ });
190
+
191
+ afterEach(() => {
192
+ nock.cleanAll();
193
+ });
187
194
  it('should create correct CredentialRequest for Spruce', async () => {
188
195
  const IRR_URI =
189
196
  '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';
@@ -208,6 +215,7 @@ describe('Credential Request Client with different issuers ', () => {
208
215
  });
209
216
 
210
217
  it('should create correct CredentialRequest for Walt', async () => {
218
+ nock.cleanAll();
211
219
  const IRR_URI =
212
220
  'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&credential_type=OpenBadgeCredential&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMTc4OTNjYy04ZTY3LTQxNzItYWZlOS1lODcyYmYxNDBlNWMiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.ODfq2AIhOcB61dAb3zMrXBJjPJaf53zkeHh_AssYyYA&user_pin_required=false';
213
221
  const credentialOffer = await (
@@ -1,4 +1,4 @@
1
- import { Alg, CodeChallengeMethod, Jwt } from '@sphereon/oid4vci-common';
1
+ import { Alg, Jwt } from '@sphereon/oid4vci-common';
2
2
  import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils';
3
3
  import { CredentialMapper } from '@sphereon/ssi-types';
4
4
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -52,7 +52,9 @@ const DID_URL_ENCODED =
52
52
  const kid = `${DID}#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE`;
53
53
 
54
54
  // const jw = jose.importKey()
55
- describe('OID4VCI-Client using Sphereon issuer should', () => {
55
+
56
+ // EBSI returning a 500 in credential endpoint all of a sudden
57
+ describe.skip('OID4VCI-Client using Sphereon issuer should', () => {
56
58
  async function test(credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossPreAuthorisedDeferred' | 'CTWalletCrossAuthorisedInTime') {
57
59
  debug.enable('*');
58
60
  const offer = await getCredentialOffer(credentialType);
@@ -68,10 +70,10 @@ describe('OID4VCI-Client using Sphereon issuer should', () => {
68
70
  expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`);
69
71
 
70
72
  if (credentialType !== 'CTWalletCrossPreAuthorisedInTime') {
71
- const url = client.createAuthorizationRequestUrl({
72
- redirectUri: 'openid4vc%3A',
73
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
74
- codeChallengeMethod: CodeChallengeMethod.SHA256,
73
+ const url = await client.createAuthorizationRequestUrl({
74
+ authorizationRequest: {
75
+ redirectUri: 'openid4vc%3A',
76
+ },
75
77
  });
76
78
  const result = await fetch(url);
77
79
  console.log(result.text());
@@ -17,6 +17,7 @@ describe('OpenID4VCIClient should', () => {
17
17
  client = await OpenID4VCIClient.fromURI({
18
18
  clientId: 'test-client',
19
19
  uri: 'openid-initiate-issuance://?issuer=https://server.example.com&credential_type=TestCredential',
20
+ createAuthorizationRequestURL: false,
20
21
  });
21
22
  });
22
23
 
@@ -24,15 +25,15 @@ describe('OpenID4VCIClient should', () => {
24
25
  nock.cleanAll();
25
26
  });
26
27
 
27
- it('should create successfully construct an authorization request url', async () => {
28
+ it('should successfully construct an authorization request url', async () => {
28
29
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
29
30
  // @ts-ignore
30
- client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
31
- const url = client.createAuthorizationRequestUrl({
32
- codeChallengeMethod: CodeChallengeMethod.SHA256,
33
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
34
- scope: 'openid TestCredential',
35
- redirectUri: 'http://localhost:8881/cb',
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
+ },
36
37
  });
37
38
 
38
39
  const urlSearchParams = new URLSearchParams(url.split('?')[1]);
@@ -41,25 +42,29 @@ describe('OpenID4VCIClient should', () => {
41
42
  expect(scope?.[0]).toBe('openid');
42
43
  });
43
44
  it('throw an error if authorization endpoint is not set in server metadata', async () => {
44
- expect(() => {
45
+ await expect(
45
46
  client.createAuthorizationRequestUrl({
46
- codeChallengeMethod: CodeChallengeMethod.SHA256,
47
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
48
- scope: 'openid TestCredential',
49
- redirectUri: 'http://localhost:8881/cb',
50
- });
51
- }).toThrow(Error('Server metadata does not contain authorization endpoint'));
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'));
52
53
  });
53
54
  it("injects 'openid' as the first scope if not provided", async () => {
54
55
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
55
56
  // @ts-ignore
56
- client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
57
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
57
58
 
58
- const url = client.createAuthorizationRequestUrl({
59
- codeChallengeMethod: CodeChallengeMethod.SHA256,
60
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
61
- scope: 'TestCredential',
62
- redirectUri: 'http://localhost:8881/cb',
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
+ },
63
68
  });
64
69
 
65
70
  const urlSearchParams = new URLSearchParams(url.split('?')[1]);
@@ -69,98 +74,129 @@ describe('OpenID4VCIClient should', () => {
69
74
  });
70
75
  it('throw an error if no scope and no authorization_details is provided', async () => {
71
76
  nock(MOCK_URL).get(/.*/).reply(200, {});
72
- nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
73
- nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
77
+ nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, {});
78
+ nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(200, {});
74
79
  // Use a client with issuer only to trigger the error
75
- client = await OpenID4VCIClient.fromCredentialIssuer({ credentialIssuer: 'https://server.example.com' });
80
+ client = await OpenID4VCIClient.fromCredentialIssuer({
81
+ credentialIssuer: MOCK_URL,
82
+ createAuthorizationRequestURL: false,
83
+ retrieveServerMetadata: false,
84
+ });
85
+
76
86
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
77
87
  // @ts-ignore
78
- client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
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`;
79
97
 
80
- expect(() => {
98
+ await expect(
81
99
  client.createAuthorizationRequestUrl({
82
- codeChallengeMethod: CodeChallengeMethod.SHA256,
83
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
84
- redirectUri: 'http://localhost:8881/cb',
85
- });
86
- }).toThrow(Error('Please provide a scope or authorization_details'));
100
+ pkce: {
101
+ codeChallengeMethod: CodeChallengeMethod.S256,
102
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
103
+ },
104
+ authorizationRequest: {
105
+ redirectUri: 'http://localhost:8881/cb',
106
+ },
107
+ }),
108
+ ).rejects.toThrow(Error('Please provide a scope or authorization_details if no credential offer is present'));
87
109
  });
88
110
  it('create an authorization request url with authorization_details array property', async () => {
89
111
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
90
112
  // @ts-ignore
91
- client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
113
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
92
114
 
93
- expect(
115
+ await expect(
94
116
  client.createAuthorizationRequestUrl({
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'],
117
+ pkce: {
118
+ codeChallengeMethod: CodeChallengeMethod.S256,
119
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
120
+ },
121
+ authorizationRequest: {
122
+ authorizationDetails: [
123
+ {
124
+ type: 'openid_credential',
125
+ format: 'ldp_vc',
126
+ credential_definition: {
127
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
128
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
129
+ },
104
130
  },
105
- },
106
- {
107
- type: 'openid_credential',
108
- format: 'mso_mdoc',
109
- doctype: 'org.iso.18013.5.1.mDL',
110
- },
111
- ],
112
- redirectUri: 'http://localhost:8881/cb',
131
+ {
132
+ type: 'openid_credential',
133
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
134
+ // @ts-ignore
135
+ format: 'mso_mdoc',
136
+ doctype: 'org.iso.18013.5.1.mDL',
137
+ },
138
+ ],
139
+ redirectUri: 'http://localhost:8881/cb',
140
+ },
113
141
  }),
114
- ).toEqual(
115
- '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%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%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%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid&client_id=test-client',
142
+ ).resolves.toEqual(
143
+ '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',
116
144
  );
117
145
  });
118
146
  it('create an authorization request url with authorization_details object property', async () => {
119
147
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
120
148
  // @ts-ignore
121
- client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
149
+ client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
122
150
 
123
- expect(
151
+ await expect(
124
152
  client.createAuthorizationRequestUrl({
125
- codeChallengeMethod: CodeChallengeMethod.SHA256,
126
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
127
- authorizationDetails: {
128
- type: 'openid_credential',
129
- format: 'ldp_vc',
130
- credential_definition: {
131
- '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
132
- types: ['VerifiableCredential', 'UniversityDegreeCredential'],
153
+ pkce: {
154
+ codeChallengeMethod: CodeChallengeMethod.S256,
155
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
156
+ },
157
+ authorizationRequest: {
158
+ authorizationDetails: {
159
+ type: 'openid_credential',
160
+ format: 'ldp_vc',
161
+ credential_definition: {
162
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
163
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
164
+ },
133
165
  },
166
+ redirectUri: 'http://localhost:8881/cb',
134
167
  },
135
- redirectUri: 'http://localhost:8881/cb',
136
168
  }),
137
- ).toEqual(
138
- '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%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid&client_id=test-client',
169
+ ).resolves.toEqual(
170
+ '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',
139
171
  );
140
172
  });
141
173
  it('create an authorization request url with authorization_details and scope', async () => {
142
174
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
143
175
  // @ts-ignore
144
- client._endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
176
+ client._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
145
177
 
146
- expect(
178
+ await expect(
147
179
  client.createAuthorizationRequestUrl({
148
- codeChallengeMethod: CodeChallengeMethod.SHA256,
149
- codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
150
- authorizationDetails: {
151
- type: 'openid_credential',
152
- format: 'ldp_vc',
153
- locations: ['https://test.com'],
154
- credential_definition: {
155
- '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
156
- types: ['VerifiableCredential', 'UniversityDegreeCredential'],
180
+ pkce: {
181
+ codeChallengeMethod: CodeChallengeMethod.S256,
182
+ codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
183
+ },
184
+ authorizationRequest: {
185
+ authorizationDetails: {
186
+ type: 'openid_credential',
187
+ format: 'ldp_vc',
188
+ locations: ['https://test.com'],
189
+ credential_definition: {
190
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
191
+ types: ['VerifiableCredential', 'UniversityDegreeCredential'],
192
+ },
157
193
  },
194
+ scope: 'openid',
195
+ redirectUri: 'http://localhost:8881/cb',
158
196
  },
159
- scope: 'openid',
160
- redirectUri: 'http://localhost:8881/cb',
161
197
  }),
162
- ).toEqual(
163
- '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&scope=openid&client_id=test-client',
198
+ ).resolves.toEqual(
199
+ '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',
164
200
  );
165
201
  });
166
202
  });
@@ -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
  });
@@ -1,4 +1,6 @@
1
1
  import { AccessTokenRequest, CredentialRequestV1_0_11, CredentialSupportedSdJwtVc } 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 '..';
@@ -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');
@@ -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
+ };
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.48+81adb47",
3
+ "version": "0.8.2-next.87+9f0679e",
4
4
  "description": "OpenID for Verifiable Credential Issuance (OpenID4VCI) client",
5
5
  "source": "lib/index.ts",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "build": "tsc"
16
16
  },
17
17
  "dependencies": {
18
- "@sphereon/oid4vci-common": "0.8.2-next.48+81adb47",
18
+ "@sphereon/oid4vci-common": "0.8.2-next.87+9f0679e",
19
19
  "@sphereon/ssi-types": "^0.18.1",
20
20
  "cross-fetch": "^3.1.8",
21
21
  "debug": "^4.3.4"
@@ -69,5 +69,5 @@
69
69
  "OIDC4VCI",
70
70
  "OID4VCI"
71
71
  ],
72
- "gitHead": "81adb47112f687715737f17c5e8518739b8a6a58"
72
+ "gitHead": "9f0679e6cbf0b0ba7f7d4f8ecc2263981d4ef80e"
73
73
  }