@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.
- package/README.md +9 -8
- package/dist/AccessTokenClient.d.ts +0 -1
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +11 -18
- 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 +132 -0
- package/dist/AuthorizationCodeClient.js.map +1 -0
- package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -1
- package/dist/AuthorizationDetailsBuilder.js.map +1 -1
- 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 +15 -0
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +91 -43
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/CredentialRequestClientBuilder.d.ts +19 -7
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js +31 -1
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/MetadataClient.d.ts.map +1 -1
- package/dist/MetadataClient.js +12 -1
- package/dist/MetadataClient.js.map +1 -1
- package/dist/OpenID4VCIClient.d.ts +62 -27
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +255 -176
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/ProofOfPossessionBuilder.d.ts +3 -1
- package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
- package/dist/ProofOfPossessionBuilder.js +5 -0
- package/dist/ProofOfPossessionBuilder.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/functions/ProofUtil.d.ts +2 -1
- package/dist/functions/ProofUtil.d.ts.map +1 -1
- package/dist/functions/ProofUtil.js +6 -4
- package/dist/functions/ProofUtil.js.map +1 -1
- 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 +16 -20
- package/lib/AuthorizationCodeClient.ts +163 -0
- package/lib/AuthorizationDetailsBuilder.ts +2 -2
- package/lib/CredentialOfferClient.ts +4 -1
- package/lib/CredentialRequestClient.ts +116 -45
- package/lib/CredentialRequestClientBuilder.ts +53 -8
- package/lib/MetadataClient.ts +13 -1
- package/lib/OpenID4VCIClient.ts +348 -216
- package/lib/ProofOfPossessionBuilder.ts +8 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
- package/lib/__tests__/CredentialRequestClient.spec.ts +28 -8
- package/lib/__tests__/EBSIE2E.spec.test.ts +145 -0
- package/lib/__tests__/MetadataClient.spec.ts +4 -1
- package/lib/__tests__/OpenID4VCIClient.spec.ts +117 -76
- package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
- package/lib/__tests__/SdJwt.spec.ts +163 -0
- package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
- package/lib/__tests__/data/VciDataFixtures.ts +14 -13
- package/lib/functions/AuthorizationUtil.ts +18 -0
- package/lib/functions/ProofUtil.ts +18 -4
- package/lib/index.ts +1 -0
- package/lib/types/index.ts +0 -0
- package/package.json +8 -6
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
AccessTokenResponse,
|
|
3
3
|
Alg,
|
|
4
4
|
EndpointMetadata,
|
|
5
|
+
JWK,
|
|
5
6
|
Jwt,
|
|
6
7
|
NO_JWT_PROVIDED,
|
|
7
8
|
OpenId4VCIVersion,
|
|
@@ -19,6 +20,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
19
20
|
private readonly version: OpenId4VCIVersion;
|
|
20
21
|
|
|
21
22
|
private kid?: string;
|
|
23
|
+
private jwk?: JWK;
|
|
22
24
|
private clientId?: string;
|
|
23
25
|
private issuer?: string;
|
|
24
26
|
private jwt?: Jwt;
|
|
@@ -91,6 +93,11 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
91
93
|
return this;
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
withJWK(jwk: JWK): this {
|
|
97
|
+
this.jwk = jwk;
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
withIssuer(issuer: string): this {
|
|
95
102
|
this.issuer = issuer;
|
|
96
103
|
return this;
|
|
@@ -182,6 +189,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
182
189
|
{
|
|
183
190
|
typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 ? 'jwt' : 'openid4vci-proof+jwt'),
|
|
184
191
|
kid: this.kid,
|
|
192
|
+
jwk: this.jwk,
|
|
185
193
|
jti: this.jti,
|
|
186
194
|
alg: this.alg,
|
|
187
195
|
issuer: this.issuer,
|
|
@@ -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';
|
|
@@ -3,6 +3,7 @@ import { KeyObject } from 'crypto';
|
|
|
3
3
|
import {
|
|
4
4
|
Alg,
|
|
5
5
|
EndpointMetadata,
|
|
6
|
+
getCredentialRequestForVersion,
|
|
6
7
|
getIssuerFromCredentialOfferPayload,
|
|
7
8
|
Jwt,
|
|
8
9
|
OpenId4VCIVersion,
|
|
@@ -127,7 +128,7 @@ describe('Credential Request Client ', () => {
|
|
|
127
128
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
128
129
|
});
|
|
129
130
|
expect(credentialRequest.proof?.jwt?.includes(partialJWT)).toBeTruthy();
|
|
130
|
-
expect(credentialRequest.format).toEqual('
|
|
131
|
+
expect(credentialRequest.format).toEqual('jwt_vc');
|
|
131
132
|
const result = await credReqClient.acquireCredentialsUsingRequest(credentialRequest);
|
|
132
133
|
expect(result?.successBody?.credential).toEqual(mockedVC);
|
|
133
134
|
});
|
|
@@ -149,15 +150,20 @@ describe('Credential Request Client ', () => {
|
|
|
149
150
|
.withKid(kid)
|
|
150
151
|
.withClientId('sphereon:wallet')
|
|
151
152
|
.build();
|
|
152
|
-
|
|
153
|
-
// @ts-ignore
|
|
154
|
-
await expect(credReqClient.acquireCredentialsUsingRequest({ format: 'jwt_vc_json-ld', types: ['random'], proof })).rejects.toThrow(
|
|
153
|
+
await expect(credReqClient.acquireCredentialsUsingRequest({ format: 'jwt_vc_json', types: ['random'], proof })).rejects.toThrow(
|
|
155
154
|
Error(URL_NOT_VALID),
|
|
156
155
|
);
|
|
157
156
|
});
|
|
158
157
|
});
|
|
159
158
|
|
|
160
159
|
describe('Credential Request Client with Walt.id ', () => {
|
|
160
|
+
beforeEach(() => {
|
|
161
|
+
nock.cleanAll();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
afterEach(() => {
|
|
165
|
+
nock.cleanAll();
|
|
166
|
+
});
|
|
161
167
|
it('should have correct metadata endpoints', async function () {
|
|
162
168
|
nock.cleanAll();
|
|
163
169
|
const WALT_IRR_URI =
|
|
@@ -178,6 +184,13 @@ describe('Credential Request Client with Walt.id ', () => {
|
|
|
178
184
|
});
|
|
179
185
|
|
|
180
186
|
describe('Credential Request Client with different issuers ', () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
nock.cleanAll();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
afterEach(() => {
|
|
192
|
+
nock.cleanAll();
|
|
193
|
+
});
|
|
181
194
|
it('should create correct CredentialRequest for Spruce', async () => {
|
|
182
195
|
const IRR_URI =
|
|
183
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';
|
|
@@ -194,13 +207,15 @@ describe('Credential Request Client with different issuers ', () => {
|
|
|
194
207
|
jwt: getMockData('spruce')?.credential.request.proof.jwt as string,
|
|
195
208
|
},
|
|
196
209
|
credentialTypes: ['OpenBadgeCredential'],
|
|
197
|
-
format: '
|
|
210
|
+
format: 'jwt_vc',
|
|
198
211
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
199
212
|
});
|
|
200
|
-
|
|
213
|
+
const draft8CredentialRequest = getCredentialRequestForVersion(credentialRequest, OpenId4VCIVersion.VER_1_0_08);
|
|
214
|
+
expect(draft8CredentialRequest).toEqual(getMockData('spruce')?.credential.request);
|
|
201
215
|
});
|
|
202
216
|
|
|
203
217
|
it('should create correct CredentialRequest for Walt', async () => {
|
|
218
|
+
nock.cleanAll();
|
|
204
219
|
const IRR_URI =
|
|
205
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';
|
|
206
221
|
const credentialOffer = await (
|
|
@@ -264,7 +279,8 @@ describe('Credential Request Client with different issuers ', () => {
|
|
|
264
279
|
format: 'ldp_vc',
|
|
265
280
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
266
281
|
});
|
|
267
|
-
|
|
282
|
+
const credentialRequest = getCredentialRequestForVersion(credentialOffer, OpenId4VCIVersion.VER_1_0_08);
|
|
283
|
+
expect(credentialRequest).toEqual(getMockData('mattr')?.credential.request);
|
|
268
284
|
});
|
|
269
285
|
|
|
270
286
|
it('should create correct CredentialRequest for diwala', async () => {
|
|
@@ -286,6 +302,10 @@ describe('Credential Request Client with different issuers ', () => {
|
|
|
286
302
|
format: 'ldp_vc',
|
|
287
303
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
288
304
|
});
|
|
289
|
-
|
|
305
|
+
|
|
306
|
+
// createCredentialRequest returns uniform format in draft 11
|
|
307
|
+
const credentialRequest = getCredentialRequestForVersion(credentialOffer, OpenId4VCIVersion.VER_1_0_08);
|
|
308
|
+
|
|
309
|
+
expect(credentialRequest).toEqual(getMockData('diwala')?.credential.request);
|
|
290
310
|
});
|
|
291
311
|
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Alg, Jwt } from '@sphereon/oid4vci-common';
|
|
2
|
+
import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils';
|
|
3
|
+
import { CredentialMapper } from '@sphereon/ssi-types';
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
5
|
+
//@ts-ignore
|
|
6
|
+
import { from } from '@trust/keyto';
|
|
7
|
+
import { fetch } from 'cross-fetch';
|
|
8
|
+
import debug from 'debug';
|
|
9
|
+
import { base64url, importJWK, JWK, SignJWT } from 'jose';
|
|
10
|
+
import * as u8a from 'uint8arrays';
|
|
11
|
+
|
|
12
|
+
import { OpenID4VCIClient } from '..';
|
|
13
|
+
|
|
14
|
+
export const UNIT_TEST_TIMEOUT = 30000;
|
|
15
|
+
|
|
16
|
+
const ISSUER_URL = 'https://conformance-test.ebsi.eu/conformance/v3/issuer-mock';
|
|
17
|
+
const AUTH_URL = 'https://conformance-test.ebsi.eu/conformance/v3/auth-mock';
|
|
18
|
+
|
|
19
|
+
const jwk: JWK = {
|
|
20
|
+
alg: 'ES256',
|
|
21
|
+
use: 'sig',
|
|
22
|
+
kty: 'EC',
|
|
23
|
+
crv: 'P-256',
|
|
24
|
+
x: 'hUWYK06qFvdudydiqnEhVJhZ-73jcLtuzH8kIyNOSHE',
|
|
25
|
+
y: 'UZf7oUkJdo65SQekMD5ssiRclEimG2SmlsjXf3QwQJo',
|
|
26
|
+
d: 'zDeeo3K0Pk8dofeKcajvJYxMZ1vijx_cVDJQl1IpbAM',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
console.log(`JWK (private/orig): ${JSON.stringify(jwk, null, 2)}`);
|
|
30
|
+
|
|
31
|
+
const privateKey = from(jwk, 'jwk').toString('blk', 'private');
|
|
32
|
+
const publicKey = from(jwk, 'jwk').toString('blk', 'public');
|
|
33
|
+
console.log(`Private key: ${privateKey}`);
|
|
34
|
+
console.log(`Public key: ${publicKey}`);
|
|
35
|
+
console.log(`Private key (b64): ${base64url.encode(u8a.fromString(privateKey, 'base16'))}`);
|
|
36
|
+
console.log(`JWK (private 2) ${JSON.stringify(toJwk(privateKey, 'Secp256r1', { isPrivateKey: true }))}`);
|
|
37
|
+
console.log(`JWK (public 2) ${JSON.stringify(toJwk(publicKey, 'Secp256r1', { isPrivateKey: false }))}`);
|
|
38
|
+
|
|
39
|
+
// const DID_METHOD = 'did:key'
|
|
40
|
+
const DID =
|
|
41
|
+
'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE';
|
|
42
|
+
const DID_URL_ENCODED =
|
|
43
|
+
'did%3Akey%3Az2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE';
|
|
44
|
+
// const PRIVATE_KEY_HEX = '7dd923e40f4615ac496119f7e793cc2899e99b64b88ca8603db986700089532b'
|
|
45
|
+
|
|
46
|
+
// const PUBLIC_KEY_HEX =
|
|
47
|
+
// '04a23cb4c83901acc2eb0f852599610de0caeac260bf8ed05e7f902eaac0f9c8d74dd4841b94d13424d32af8ec0e9976db9abfa7e3a59e10d565c5d4d901b4be63'
|
|
48
|
+
|
|
49
|
+
// pub hex: 35e03477cb29f3ac518770dccd4e26e703cd21b9741c24b038170c377b0d99d9
|
|
50
|
+
// priv hex: 913466d1a38d1d8c0d3c0fb0fc3b633075085a31372bbd2a8022215a88d9d1e5
|
|
51
|
+
// const did = `did:key:z6Mki5ZwZKN1dBQprfJTikUvkDxrHijiiQngkWviMF5gw2Hv`;
|
|
52
|
+
const kid = `${DID}#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE`;
|
|
53
|
+
|
|
54
|
+
// const jw = jose.importKey()
|
|
55
|
+
|
|
56
|
+
// EBSI returning a 500 in credential endpoint all of a sudden
|
|
57
|
+
describe.skip('OID4VCI-Client using Sphereon issuer should', () => {
|
|
58
|
+
async function test(credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossPreAuthorisedDeferred' | 'CTWalletCrossAuthorisedInTime') {
|
|
59
|
+
debug.enable('*');
|
|
60
|
+
const offer = await getCredentialOffer(credentialType);
|
|
61
|
+
const client = await OpenID4VCIClient.fromURI({
|
|
62
|
+
uri: offer,
|
|
63
|
+
kid,
|
|
64
|
+
alg: Alg.ES256,
|
|
65
|
+
clientId: DID_URL_ENCODED,
|
|
66
|
+
});
|
|
67
|
+
expect(client.credentialOffer).toBeDefined();
|
|
68
|
+
expect(client.endpointMetadata).toBeDefined();
|
|
69
|
+
expect(client.getCredentialEndpoint()).toEqual(`${ISSUER_URL}/credential`);
|
|
70
|
+
expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`);
|
|
71
|
+
|
|
72
|
+
if (credentialType !== 'CTWalletCrossPreAuthorisedInTime') {
|
|
73
|
+
const url = await client.createAuthorizationRequestUrl({
|
|
74
|
+
authorizationRequest: {
|
|
75
|
+
redirectUri: 'openid4vc%3A',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
const result = await fetch(url);
|
|
79
|
+
console.log(result.text());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const accessToken = await client.acquireAccessToken({ pin: '0891' });
|
|
83
|
+
// console.log(accessToken);
|
|
84
|
+
expect(accessToken).toMatchObject({
|
|
85
|
+
expires_in: 86400,
|
|
86
|
+
// scope: 'GuestCredential',
|
|
87
|
+
token_type: 'Bearer',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const format = 'jwt_vc';
|
|
91
|
+
const credentialResponse = await client.acquireCredentials({
|
|
92
|
+
credentialTypes: client.getCredentialOfferTypes()[0],
|
|
93
|
+
format,
|
|
94
|
+
proofCallbacks: {
|
|
95
|
+
signCallback: proofOfPossessionCallbackFunction,
|
|
96
|
+
},
|
|
97
|
+
kid,
|
|
98
|
+
deferredCredentialAwait: true,
|
|
99
|
+
deferredCredentialIntervalInMS: 5000,
|
|
100
|
+
});
|
|
101
|
+
console.log(JSON.stringify(credentialResponse, null, 2));
|
|
102
|
+
expect(credentialResponse.credential).toBeDefined();
|
|
103
|
+
const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credentialResponse.credential!);
|
|
104
|
+
expect(format.startsWith(wrappedVC.format)).toEqual(true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Current conformance tests is not stable as changes are being applied it seems
|
|
108
|
+
|
|
109
|
+
it(
|
|
110
|
+
'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json',
|
|
111
|
+
async () => {
|
|
112
|
+
await test('CTWalletCrossPreAuthorisedInTime');
|
|
113
|
+
await test('CTWalletCrossPreAuthorisedDeferred');
|
|
114
|
+
// await test('CTWalletCrossAuthorisedInTime');
|
|
115
|
+
},
|
|
116
|
+
UNIT_TEST_TIMEOUT,
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
async function getCredentialOffer(
|
|
121
|
+
credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossAuthorisedInTime' | 'CTWalletCrossPreAuthorisedDeferred',
|
|
122
|
+
): Promise<string> {
|
|
123
|
+
const credentialOffer = await fetch(
|
|
124
|
+
`https://conformance-test.ebsi.eu/conformance/v3/issuer-mock/initiate-credential-offer?credential_type=${credentialType}&client_id=${DID_URL_ENCODED}&credential_offer_endpoint=openid-credential-offer%3A%2F%2F`,
|
|
125
|
+
{
|
|
126
|
+
method: 'GET',
|
|
127
|
+
headers: {
|
|
128
|
+
Accept: 'application/json',
|
|
129
|
+
'Content-Type': 'application/json',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return await credentialOffer.text();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promise<string> {
|
|
138
|
+
const importedJwk = await importJWK(jwk);
|
|
139
|
+
return await new SignJWT({ ...args.payload })
|
|
140
|
+
.setProtectedHeader({ ...args.header, kid: kid! })
|
|
141
|
+
.setIssuer(DID)
|
|
142
|
+
.setIssuedAt()
|
|
143
|
+
.setExpirationTime('2m')
|
|
144
|
+
.sign(importedJwk);
|
|
145
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getIssuerFromCredentialOfferPayload, 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 { CredentialOfferClient } from '../CredentialOfferClient';
|
|
@@ -211,7 +213,8 @@ describe('Metadataclient with Walt-id should', () => {
|
|
|
211
213
|
});
|
|
212
214
|
});
|
|
213
215
|
|
|
214
|
-
|
|
216
|
+
// Spruce gives back 404's these days, so test is disabled
|
|
217
|
+
describe.skip('Metadataclient with SpruceId should', () => {
|
|
215
218
|
beforeAll(() => {
|
|
216
219
|
nock.cleanAll();
|
|
217
220
|
});
|
|
@@ -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]);
|
|
@@ -68,94 +73,130 @@ describe('OpenID4VCIClient should', () => {
|
|
|
68
73
|
expect(scope?.[0]).toBe('openid');
|
|
69
74
|
});
|
|
70
75
|
it('throw an error if no scope and no authorization_details is provided', async () => {
|
|
76
|
+
nock(MOCK_URL).get(/.*/).reply(200, {});
|
|
77
|
+
nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, {});
|
|
78
|
+
nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(200, {});
|
|
79
|
+
// Use a client with issuer only to trigger the error
|
|
80
|
+
client = await OpenID4VCIClient.fromCredentialIssuer({
|
|
81
|
+
credentialIssuer: MOCK_URL,
|
|
82
|
+
createAuthorizationRequestURL: false,
|
|
83
|
+
retrieveServerMetadata: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
71
86
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
72
87
|
// @ts-ignore
|
|
73
|
-
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`;
|
|
74
97
|
|
|
75
|
-
expect(
|
|
98
|
+
await expect(
|
|
76
99
|
client.createAuthorizationRequestUrl({
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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'));
|
|
82
109
|
});
|
|
83
110
|
it('create an authorization request url with authorization_details array property', async () => {
|
|
84
111
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
85
112
|
// @ts-ignore
|
|
86
|
-
client.
|
|
113
|
+
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
87
114
|
|
|
88
|
-
expect(
|
|
115
|
+
await expect(
|
|
89
116
|
client.createAuthorizationRequestUrl({
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
},
|
|
99
130
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
},
|
|
108
141
|
}),
|
|
109
|
-
).toEqual(
|
|
110
|
-
'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',
|
|
111
144
|
);
|
|
112
145
|
});
|
|
113
146
|
it('create an authorization request url with authorization_details object property', async () => {
|
|
114
147
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
115
148
|
// @ts-ignore
|
|
116
|
-
client.
|
|
149
|
+
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
117
150
|
|
|
118
|
-
expect(
|
|
151
|
+
await expect(
|
|
119
152
|
client.createAuthorizationRequestUrl({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
},
|
|
128
165
|
},
|
|
166
|
+
redirectUri: 'http://localhost:8881/cb',
|
|
129
167
|
},
|
|
130
|
-
redirectUri: 'http://localhost:8881/cb',
|
|
131
168
|
}),
|
|
132
|
-
).toEqual(
|
|
133
|
-
'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',
|
|
134
171
|
);
|
|
135
172
|
});
|
|
136
173
|
it('create an authorization request url with authorization_details and scope', async () => {
|
|
137
174
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
138
175
|
// @ts-ignore
|
|
139
|
-
client.
|
|
176
|
+
client._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
|
|
140
177
|
|
|
141
|
-
expect(
|
|
178
|
+
await expect(
|
|
142
179
|
client.createAuthorizationRequestUrl({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
},
|
|
152
193
|
},
|
|
194
|
+
scope: 'openid',
|
|
195
|
+
redirectUri: 'http://localhost:8881/cb',
|
|
153
196
|
},
|
|
154
|
-
scope: 'openid',
|
|
155
|
-
redirectUri: 'http://localhost:8881/cb',
|
|
156
197
|
}),
|
|
157
|
-
).toEqual(
|
|
158
|
-
'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',
|
|
159
200
|
);
|
|
160
201
|
});
|
|
161
202
|
});
|