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