@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.
- package/dist/AccessTokenClient.d.ts +0 -1
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +2 -9
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AuthorizationCodeClient.d.ts +9 -0
- package/dist/AuthorizationCodeClient.d.ts.map +1 -0
- package/dist/AuthorizationCodeClient.js +124 -0
- package/dist/AuthorizationCodeClient.js.map +1 -0
- package/dist/CredentialOfferClient.d.ts.map +1 -1
- package/dist/CredentialOfferClient.js +3 -1
- package/dist/CredentialOfferClient.js.map +1 -1
- package/dist/CredentialRequestClient.d.ts +2 -0
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +9 -7
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/OpenID4VCIClient.d.ts +50 -29
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +191 -190
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/functions/AuthorizationUtil.d.ts +3 -0
- package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
- package/dist/functions/AuthorizationUtil.js +22 -0
- package/dist/functions/AuthorizationUtil.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/lib/AccessTokenClient.ts +5 -11
- package/lib/AuthorizationCodeClient.ts +151 -0
- package/lib/CredentialOfferClient.ts +4 -1
- package/lib/CredentialRequestClient.ts +13 -4
- package/lib/OpenID4VCIClient.ts +250 -228
- package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
- package/lib/__tests__/CredentialRequestClient.spec.ts +10 -2
- package/lib/__tests__/EBSIE2E.spec.test.ts +8 -6
- package/lib/__tests__/OpenID4VCIClient.spec.ts +115 -79
- package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
- package/lib/__tests__/SdJwt.spec.ts +2 -0
- package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
- package/lib/functions/AuthorizationUtil.ts +18 -0
- package/lib/index.ts +1 -0
- package/lib/types/index.ts +0 -0
- 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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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.
|
|
31
|
-
const url = client.createAuthorizationRequestUrl({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
|
|
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.
|
|
57
|
+
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
57
58
|
|
|
58
|
-
const url = client.createAuthorizationRequestUrl({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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(
|
|
73
|
-
nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(
|
|
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({
|
|
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.
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
113
|
+
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
92
114
|
|
|
93
|
-
expect(
|
|
115
|
+
await expect(
|
|
94
116
|
client.createAuthorizationRequestUrl({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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&
|
|
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.
|
|
149
|
+
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
122
150
|
|
|
123
|
-
expect(
|
|
151
|
+
await expect(
|
|
124
152
|
client.createAuthorizationRequestUrl({
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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&
|
|
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.
|
|
176
|
+
client._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
145
177
|
|
|
146
|
-
expect(
|
|
178
|
+
await expect(
|
|
147
179
|
client.createAuthorizationRequestUrl({
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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&
|
|
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 {
|
|
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.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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('
|
|
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.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
client.createAuthorizationRequestUrl({
|
|
57
|
+
authorizationRequest: {
|
|
58
|
+
parMode: PARMode.REQUIRE,
|
|
59
|
+
redirectUri: 'http://localhost:8881/cb',
|
|
60
|
+
},
|
|
54
61
|
}),
|
|
55
|
-
).rejects.toThrow(Error('Please
|
|
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.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
});
|
|
@@ -10,7 +10,7 @@ import { v4 } from 'uuid';
|
|
|
10
10
|
|
|
11
11
|
import { OpenID4VCIClient } from '..';
|
|
12
12
|
|
|
13
|
-
export const UNIT_TEST_TIMEOUT =
|
|
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
|
-
|
|
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
|
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.
|
|
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.
|
|
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": "
|
|
72
|
+
"gitHead": "9f0679e6cbf0b0ba7f7d4f8ecc2263981d4ef80e"
|
|
73
73
|
}
|