@sphereon/oid4vci-client 0.12.1-unstable.9 → 0.13.0
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.map +1 -1
- package/dist/AccessTokenClient.js +11 -5
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -1
- package/dist/AccessTokenClientV1_0_11.js +12 -5
- package/dist/AccessTokenClientV1_0_11.js.map +1 -1
- package/dist/AuthorizationCodeClient.js +2 -2
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/OpenID4VCIClient.d.ts +3 -1
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +30 -14
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/OpenID4VCIClientV1_0_11.d.ts +4 -2
- package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -1
- package/dist/OpenID4VCIClientV1_0_11.js +32 -16
- package/dist/OpenID4VCIClientV1_0_11.js.map +1 -1
- package/dist/OpenID4VCIClientV1_0_13.d.ts +3 -1
- package/dist/OpenID4VCIClientV1_0_13.d.ts.map +1 -1
- package/dist/OpenID4VCIClientV1_0_13.js +29 -15
- package/dist/OpenID4VCIClientV1_0_13.js.map +1 -1
- package/dist/ProofOfPossessionBuilder.js +6 -6
- package/dist/ProofOfPossessionBuilder.js.map +1 -1
- package/dist/functions/AccessTokenUtil.d.ts +5 -0
- package/dist/functions/AccessTokenUtil.d.ts.map +1 -0
- package/dist/functions/AccessTokenUtil.js +63 -0
- package/dist/functions/AccessTokenUtil.js.map +1 -0
- package/dist/functions/index.d.ts +2 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +2 -0
- package/dist/functions/index.js.map +1 -1
- package/lib/AccessTokenClient.ts +9 -4
- package/lib/AccessTokenClientV1_0_11.ts +11 -3
- package/lib/AuthorizationCodeClient.ts +2 -2
- package/lib/CredentialRequestClientBuilder.ts +14 -14
- package/lib/OpenID4VCIClient.ts +31 -3
- package/lib/OpenID4VCIClientV1_0_11.ts +35 -6
- package/lib/OpenID4VCIClientV1_0_13.ts +33 -6
- package/lib/ProofOfPossessionBuilder.ts +6 -6
- package/lib/__tests__/CredentialRequestClient.spec.ts +1 -1
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +1 -1
- package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +2 -2
- package/lib/__tests__/IT.spec.ts +23 -29
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +23 -23
- package/lib/__tests__/SdJwt.spec.ts +26 -26
- package/lib/functions/AccessTokenUtil.ts +52 -0
- package/lib/functions/index.ts +2 -0
- package/package.json +4 -4
package/lib/AccessTokenClient.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { ObjectUtils } from '@sphereon/ssi-types';
|
|
23
23
|
|
|
24
24
|
import { MetadataClientV1_0_13 } from './MetadataClientV1_0_13';
|
|
25
|
+
import { createJwtBearerClientAssertion } from './functions';
|
|
25
26
|
import { LOG } from './types';
|
|
26
27
|
|
|
27
28
|
export class AccessTokenClient {
|
|
@@ -48,6 +49,9 @@ export class AccessTokenClient {
|
|
|
48
49
|
code,
|
|
49
50
|
redirectUri,
|
|
50
51
|
pin,
|
|
52
|
+
credentialIssuer: issuer,
|
|
53
|
+
metadata,
|
|
54
|
+
additionalParams: opts.additionalParams,
|
|
51
55
|
pinMetadata,
|
|
52
56
|
}),
|
|
53
57
|
pinMetadata,
|
|
@@ -90,11 +94,12 @@ export class AccessTokenClient {
|
|
|
90
94
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
91
95
|
// @ts-ignore
|
|
92
96
|
const credentialOfferRequest = opts.credentialOffer ? await toUniformCredentialOfferRequest(opts.credentialOffer) : undefined;
|
|
93
|
-
const request: Partial<AccessTokenRequest> = {};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
request.client_id = asOpts.clientId;
|
|
97
|
+
const request: Partial<AccessTokenRequest> = { ...opts.additionalParams };
|
|
98
|
+
if (asOpts?.clientOpts?.clientId) {
|
|
99
|
+
request.client_id = asOpts.clientOpts.clientId;
|
|
97
100
|
}
|
|
101
|
+
const credentialIssuer = opts.credentialIssuer ?? credentialOfferRequest?.credential_offer?.credential_issuer ?? opts.metadata?.issuer;
|
|
102
|
+
await createJwtBearerClientAssertion(request, { ...opts, credentialIssuer });
|
|
98
103
|
|
|
99
104
|
if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
|
|
100
105
|
this.assertAlphanumericPin(opts.pinMetadata, pin);
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
GrantTypes,
|
|
15
15
|
IssuerOpts,
|
|
16
16
|
JsonURIMode,
|
|
17
|
+
OpenId4VCIVersion,
|
|
17
18
|
OpenIDResponse,
|
|
18
19
|
PRE_AUTH_CODE_LITERAL,
|
|
19
20
|
TokenErrorResponse,
|
|
@@ -24,6 +25,7 @@ import { ObjectUtils } from '@sphereon/ssi-types';
|
|
|
24
25
|
import Debug from 'debug';
|
|
25
26
|
|
|
26
27
|
import { MetadataClientV1_0_13 } from './MetadataClientV1_0_13';
|
|
28
|
+
import { createJwtBearerClientAssertion } from './functions';
|
|
27
29
|
|
|
28
30
|
const debug = Debug('sphereon:oid4vci:token');
|
|
29
31
|
|
|
@@ -51,6 +53,10 @@ export class AccessTokenClientV1_0_11 {
|
|
|
51
53
|
code,
|
|
52
54
|
redirectUri,
|
|
53
55
|
pin,
|
|
56
|
+
credentialIssuer: issuer,
|
|
57
|
+
metadata,
|
|
58
|
+
additionalParams: opts.additionalParams,
|
|
59
|
+
pinMetadata: opts.pinMetadata,
|
|
54
60
|
}),
|
|
55
61
|
isPinRequired,
|
|
56
62
|
metadata,
|
|
@@ -92,11 +98,13 @@ export class AccessTokenClientV1_0_11 {
|
|
|
92
98
|
const credentialOfferRequest = opts.credentialOffer
|
|
93
99
|
? await toUniformCredentialOfferRequest(opts.credentialOffer as CredentialOfferV1_0_11 | CredentialOfferV1_0_13)
|
|
94
100
|
: undefined;
|
|
95
|
-
const request: Partial<AccessTokenRequest> = {};
|
|
101
|
+
const request: Partial<AccessTokenRequest> = { ...opts.additionalParams };
|
|
102
|
+
const credentialIssuer = opts.credentialIssuer ?? credentialOfferRequest?.credential_offer?.credential_issuer ?? opts.metadata?.issuer;
|
|
96
103
|
|
|
97
|
-
if (asOpts?.clientId) {
|
|
98
|
-
request.client_id = asOpts.clientId;
|
|
104
|
+
if (asOpts?.clientOpts?.clientId) {
|
|
105
|
+
request.client_id = asOpts.clientOpts.clientId;
|
|
99
106
|
}
|
|
107
|
+
await createJwtBearerClientAssertion(request, { ...opts, version: OpenId4VCIVersion.VER_1_0_11, credentialIssuer });
|
|
100
108
|
|
|
101
109
|
if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
|
|
102
110
|
this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin);
|
|
@@ -52,14 +52,14 @@ export async function createSignedAuthRequestWhenNeeded(requestObject: Record<st
|
|
|
52
52
|
const iss = requestObject.iss ?? opts.iss ?? requestObject.client_id;
|
|
53
53
|
|
|
54
54
|
const jwt: Jwt = {
|
|
55
|
-
header: { alg: 'ES256', kid: opts.kid, typ: '
|
|
55
|
+
header: { alg: 'ES256', kid: opts.kid, typ: 'JWT' },
|
|
56
56
|
payload: { ...requestObject, iss, authorization_details, ...(client_metadata && { client_metadata }) },
|
|
57
57
|
};
|
|
58
58
|
const pop = await ProofOfPossessionBuilder.fromJwt({
|
|
59
59
|
jwt,
|
|
60
60
|
callbacks: opts.signCallbacks,
|
|
61
61
|
version: OpenId4VCIVersion.VER_1_0_11,
|
|
62
|
-
mode: '
|
|
62
|
+
mode: 'JWT',
|
|
63
63
|
}).build();
|
|
64
64
|
requestObject['request'] = pop.jwt;
|
|
65
65
|
}
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
ExperimentalSubjectIssuance,
|
|
9
9
|
OID4VCICredentialFormat,
|
|
10
10
|
OpenId4VCIVersion,
|
|
11
|
-
UniformCredentialOfferRequest
|
|
12
|
-
} from '@sphereon/oid4vci-common'
|
|
13
|
-
import { CredentialFormat } from '@sphereon/ssi-types'
|
|
11
|
+
UniformCredentialOfferRequest,
|
|
12
|
+
} from '@sphereon/oid4vci-common';
|
|
13
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
14
14
|
|
|
15
15
|
import { CredentialOfferClient } from './CredentialOfferClient';
|
|
16
16
|
import { CredentialRequestClientBuilderV1_0_11 } from './CredentialRequestClientBuilderV1_0_11';
|
|
@@ -30,12 +30,12 @@ export class CredentialRequestClientBuilder {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
public static fromCredentialIssuer({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
credentialIssuer,
|
|
34
|
+
metadata,
|
|
35
|
+
version,
|
|
36
|
+
credentialIdentifier,
|
|
37
|
+
credentialTypes,
|
|
38
|
+
}: {
|
|
39
39
|
credentialIssuer: string;
|
|
40
40
|
metadata?: EndpointMetadata;
|
|
41
41
|
version?: OpenId4VCIVersion;
|
|
@@ -99,9 +99,9 @@ export class CredentialRequestClientBuilder {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
public static fromCredentialOffer({
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
credentialOffer,
|
|
103
|
+
metadata,
|
|
104
|
+
}: {
|
|
105
105
|
credentialOffer: CredentialOfferRequestWithBaseUrl;
|
|
106
106
|
metadata?: EndpointMetadata;
|
|
107
107
|
}): CredentialRequestClientBuilder {
|
|
@@ -129,9 +129,9 @@ export class CredentialRequestClientBuilder {
|
|
|
129
129
|
|
|
130
130
|
public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata | CredentialIssuerMetadataV1_0_13): this {
|
|
131
131
|
if (isV1_0_13(this._builder)) {
|
|
132
|
-
this._builder.withCredentialEndpointFromMetadata(metadata as CredentialIssuerMetadataV1_0_13)
|
|
132
|
+
this._builder.withCredentialEndpointFromMetadata(metadata as CredentialIssuerMetadataV1_0_13);
|
|
133
133
|
} else {
|
|
134
|
-
this._builder.withCredentialEndpointFromMetadata(metadata as CredentialIssuerMetadata)
|
|
134
|
+
this._builder.withCredentialEndpointFromMetadata(metadata as CredentialIssuerMetadata);
|
|
135
135
|
}
|
|
136
136
|
return this;
|
|
137
137
|
}
|
package/lib/OpenID4VCIClient.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Alg,
|
|
4
4
|
AuthorizationRequestOpts,
|
|
5
5
|
AuthorizationResponse,
|
|
6
|
+
AuthorizationServerOpts,
|
|
6
7
|
AuthzFlowType,
|
|
7
8
|
CodeChallengeMethod,
|
|
8
9
|
CredentialConfigurationSupported,
|
|
@@ -41,8 +42,8 @@ import { createAuthorizationRequestUrl } from './AuthorizationCodeClient';
|
|
|
41
42
|
import { createAuthorizationRequestUrlV1_0_11 } from './AuthorizationCodeClientV1_0_11';
|
|
42
43
|
import { CredentialOfferClient } from './CredentialOfferClient';
|
|
43
44
|
import { CredentialRequestOpts } from './CredentialRequestClient';
|
|
44
|
-
import { CredentialRequestClientBuilderV1_0_13 } from './CredentialRequestClientBuilderV1_0_13';
|
|
45
45
|
import { CredentialRequestClientBuilderV1_0_11 } from './CredentialRequestClientBuilderV1_0_11';
|
|
46
|
+
import { CredentialRequestClientBuilderV1_0_13 } from './CredentialRequestClientBuilderV1_0_13';
|
|
46
47
|
import { MetadataClient } from './MetadataClient';
|
|
47
48
|
import { OpenID4VCIClientStateV1_0_11 } from './OpenID4VCIClientV1_0_11';
|
|
48
49
|
import { OpenID4VCIClientStateV1_0_13 } from './OpenID4VCIClientV1_0_13';
|
|
@@ -103,6 +104,7 @@ export class OpenID4VCIClient {
|
|
|
103
104
|
pkce: { disabled: false, codeChallengeMethod: CodeChallengeMethod.S256, ...pkce },
|
|
104
105
|
authorizationRequestOpts,
|
|
105
106
|
authorizationCodeResponse,
|
|
107
|
+
accessToken,
|
|
106
108
|
jwk,
|
|
107
109
|
endpointMetadata: endpointMetadata?.credentialIssuerMetadata?.authorization_server
|
|
108
110
|
? (endpointMetadata as EndpointMetadataResultV1_0_11)
|
|
@@ -273,8 +275,10 @@ export class OpenID4VCIClient {
|
|
|
273
275
|
authorizationResponse?: string | AuthorizationResponse; // Pass in an auth response, either as URI/redirect, or object
|
|
274
276
|
code?: string; // Directly pass in a code from an auth response
|
|
275
277
|
redirectUri?: string;
|
|
278
|
+
additionalRequestParams?: Record<string, any>;
|
|
279
|
+
asOpts?: AuthorizationServerOpts;
|
|
276
280
|
}): Promise<AccessTokenResponse> {
|
|
277
|
-
const { pin, clientId } = opts ?? {};
|
|
281
|
+
const { pin, clientId = this._state.clientId ?? this._state.authorizationRequestOpts?.clientId } = opts ?? {};
|
|
278
282
|
let { redirectUri } = opts ?? {};
|
|
279
283
|
if (opts?.authorizationResponse) {
|
|
280
284
|
this._state.authorizationCodeResponse = { ...toAuthorizationResponsePayload(opts.authorizationResponse) };
|
|
@@ -288,6 +292,26 @@ export class OpenID4VCIClient {
|
|
|
288
292
|
}
|
|
289
293
|
this.assertIssuerData();
|
|
290
294
|
|
|
295
|
+
const asOpts: AuthorizationServerOpts = { ...opts?.asOpts };
|
|
296
|
+
const kid = asOpts.clientOpts?.kid ?? this._state.kid ?? this._state.authorizationRequestOpts?.requestObjectOpts?.kid;
|
|
297
|
+
const clientAssertionType =
|
|
298
|
+
asOpts.clientOpts?.clientAssertionType ??
|
|
299
|
+
(kid && clientId && typeof asOpts.clientOpts?.signCallbacks?.signCallback === 'function'
|
|
300
|
+
? 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
301
|
+
: undefined);
|
|
302
|
+
if (this.isEBSI() || (clientId && kid)) {
|
|
303
|
+
if (!clientId) {
|
|
304
|
+
throw Error(`Client id expected for EBSI`);
|
|
305
|
+
}
|
|
306
|
+
asOpts.clientOpts = {
|
|
307
|
+
...asOpts.clientOpts,
|
|
308
|
+
clientId,
|
|
309
|
+
...(kid && { kid }),
|
|
310
|
+
...(clientAssertionType && { clientAssertionType }),
|
|
311
|
+
signCallbacks: asOpts.clientOpts?.signCallbacks ?? this._state.authorizationRequestOpts?.requestObjectOpts?.signCallbacks,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
291
315
|
if (clientId) {
|
|
292
316
|
this._state.clientId = clientId;
|
|
293
317
|
}
|
|
@@ -311,7 +335,8 @@ export class OpenID4VCIClient {
|
|
|
311
335
|
...(!this._state.pkce.disabled && { codeVerifier: this._state.pkce.codeVerifier }),
|
|
312
336
|
code,
|
|
313
337
|
redirectUri,
|
|
314
|
-
asOpts
|
|
338
|
+
asOpts,
|
|
339
|
+
...(opts?.additionalRequestParams && { additionalParams: opts.additionalRequestParams }),
|
|
315
340
|
});
|
|
316
341
|
|
|
317
342
|
if (response.errorBody) {
|
|
@@ -646,6 +671,9 @@ export class OpenID4VCIClient {
|
|
|
646
671
|
}
|
|
647
672
|
// this.assertIssuerData();
|
|
648
673
|
return (
|
|
674
|
+
this.clientId?.includes('ebsi') ||
|
|
675
|
+
this._state.kid?.includes('did:ebsi:') ||
|
|
676
|
+
this.getIssuer().includes('ebsi') ||
|
|
649
677
|
this.endpointMetadata.credentialIssuerMetadata?.authorization_endpoint?.includes('ebsi.eu') ||
|
|
650
678
|
this.endpointMetadata.credentialIssuerMetadata?.authorization_server?.includes('ebsi.eu')
|
|
651
679
|
);
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Alg,
|
|
4
4
|
AuthorizationRequestOpts,
|
|
5
5
|
AuthorizationResponse,
|
|
6
|
+
AuthorizationServerOpts,
|
|
6
7
|
AuthzFlowType,
|
|
7
8
|
CodeChallengeMethod,
|
|
8
9
|
CredentialConfigurationSupported,
|
|
@@ -259,8 +260,10 @@ export class OpenID4VCIClientV1_0_11 {
|
|
|
259
260
|
authorizationResponse?: string | AuthorizationResponse; // Pass in an auth response, either as URI/redirect, or object
|
|
260
261
|
code?: string; // Directly pass in a code from an auth response
|
|
261
262
|
redirectUri?: string;
|
|
263
|
+
additionalRequestParams?: Record<string, any>;
|
|
264
|
+
asOpts?: AuthorizationServerOpts;
|
|
262
265
|
}): Promise<AccessTokenResponse> {
|
|
263
|
-
const { pin, clientId } = opts ?? {};
|
|
266
|
+
const { pin, clientId = this._state.clientId ?? this._state.authorizationRequestOpts?.clientId } = opts ?? {};
|
|
264
267
|
let { redirectUri } = opts ?? {};
|
|
265
268
|
if (opts?.authorizationResponse) {
|
|
266
269
|
this._state.authorizationCodeResponse = { ...toAuthorizationResponsePayload(opts.authorizationResponse) };
|
|
@@ -288,6 +291,25 @@ export class OpenID4VCIClientV1_0_11 {
|
|
|
288
291
|
if (this._state.authorizationRequestOpts?.redirectUri && !redirectUri) {
|
|
289
292
|
redirectUri = this._state.authorizationRequestOpts.redirectUri;
|
|
290
293
|
}
|
|
294
|
+
const asOpts: AuthorizationServerOpts = { ...opts?.asOpts };
|
|
295
|
+
const kid = asOpts.clientOpts?.kid ?? this._state.kid ?? this._state.authorizationRequestOpts?.requestObjectOpts?.kid;
|
|
296
|
+
const clientAssertionType =
|
|
297
|
+
asOpts.clientOpts?.clientAssertionType ??
|
|
298
|
+
(kid && clientId && typeof asOpts.clientOpts?.signCallbacks?.signCallback === 'function'
|
|
299
|
+
? 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
300
|
+
: undefined);
|
|
301
|
+
if (this.isEBSI() || (clientId && kid)) {
|
|
302
|
+
if (!clientId) {
|
|
303
|
+
throw Error(`Client id expected for EBSI`);
|
|
304
|
+
}
|
|
305
|
+
asOpts.clientOpts = {
|
|
306
|
+
...asOpts.clientOpts,
|
|
307
|
+
clientId,
|
|
308
|
+
...(kid && { kid }),
|
|
309
|
+
...(clientAssertionType && { clientAssertionType }),
|
|
310
|
+
signCallbacks: asOpts.clientOpts?.signCallbacks ?? this._state.authorizationRequestOpts?.requestObjectOpts?.signCallbacks,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
291
313
|
|
|
292
314
|
const response = await accessTokenClient.acquireAccessToken({
|
|
293
315
|
credentialOffer: this.credentialOffer,
|
|
@@ -297,7 +319,8 @@ export class OpenID4VCIClientV1_0_11 {
|
|
|
297
319
|
...(!this._state.pkce.disabled && { codeVerifier: this._state.pkce.codeVerifier }),
|
|
298
320
|
code,
|
|
299
321
|
redirectUri,
|
|
300
|
-
asOpts
|
|
322
|
+
asOpts,
|
|
323
|
+
...(opts?.additionalRequestParams && { additionalParams: opts.additionalRequestParams }),
|
|
301
324
|
});
|
|
302
325
|
|
|
303
326
|
if (response.errorBody) {
|
|
@@ -584,8 +607,8 @@ export class OpenID4VCIClientV1_0_11 {
|
|
|
584
607
|
*/
|
|
585
608
|
public isEBSI() {
|
|
586
609
|
if (
|
|
587
|
-
|
|
588
|
-
(this.credentialOffer?.credential_offer as CredentialOfferPayloadV1_0_11)
|
|
610
|
+
this.credentialOffer &&
|
|
611
|
+
(this.credentialOffer?.credential_offer as CredentialOfferPayloadV1_0_11)?.credentials?.find(
|
|
589
612
|
(cred) =>
|
|
590
613
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
591
614
|
// @ts-ignore
|
|
@@ -594,8 +617,14 @@ export class OpenID4VCIClientV1_0_11 {
|
|
|
594
617
|
) {
|
|
595
618
|
return true;
|
|
596
619
|
}
|
|
597
|
-
this.assertIssuerData();
|
|
598
|
-
return
|
|
620
|
+
// this.assertIssuerData();
|
|
621
|
+
return (
|
|
622
|
+
this.clientId?.includes('ebsi') ||
|
|
623
|
+
this._state.kid?.includes('did:ebsi:') ||
|
|
624
|
+
this.getIssuer().includes('ebsi') ||
|
|
625
|
+
this.endpointMetadata.credentialIssuerMetadata?.authorization_endpoint?.includes('ebsi.eu') ||
|
|
626
|
+
this.endpointMetadata.credentialIssuerMetadata?.authorization_server?.includes('ebsi.eu')
|
|
627
|
+
);
|
|
599
628
|
}
|
|
600
629
|
|
|
601
630
|
private assertIssuerData(): void {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Alg,
|
|
4
4
|
AuthorizationRequestOpts,
|
|
5
5
|
AuthorizationResponse,
|
|
6
|
+
AuthorizationServerOpts,
|
|
6
7
|
AuthzFlowType,
|
|
7
8
|
CodeChallengeMethod,
|
|
8
9
|
CredentialConfigurationSupportedV1_0_13,
|
|
@@ -36,8 +37,7 @@ import { CredentialRequestOpts } from './CredentialRequestClient';
|
|
|
36
37
|
import { CredentialRequestClientBuilder } from './CredentialRequestClientBuilder';
|
|
37
38
|
import { MetadataClientV1_0_13 } from './MetadataClientV1_0_13';
|
|
38
39
|
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
39
|
-
import { generateMissingPKCEOpts } from './functions';
|
|
40
|
-
import { sendNotification } from './functions';
|
|
40
|
+
import { generateMissingPKCEOpts, sendNotification } from './functions';
|
|
41
41
|
|
|
42
42
|
const debug = Debug('sphereon:oid4vci');
|
|
43
43
|
|
|
@@ -265,8 +265,10 @@ export class OpenID4VCIClientV1_0_13 {
|
|
|
265
265
|
authorizationResponse?: string | AuthorizationResponse; // Pass in an auth response, either as URI/redirect, or object
|
|
266
266
|
code?: string; // Directly pass in a code from an auth response
|
|
267
267
|
redirectUri?: string;
|
|
268
|
+
additionalRequestParams?: Record<string, any>;
|
|
269
|
+
asOpts?: AuthorizationServerOpts;
|
|
268
270
|
}): Promise<AccessTokenResponse> {
|
|
269
|
-
const { pin, clientId } = opts ?? {};
|
|
271
|
+
const { pin, clientId = this._state.clientId ?? this._state.authorizationRequestOpts?.clientId } = opts ?? {};
|
|
270
272
|
let { redirectUri } = opts ?? {};
|
|
271
273
|
if (opts?.authorizationResponse) {
|
|
272
274
|
this._state.authorizationCodeResponse = { ...toAuthorizationResponsePayload(opts.authorizationResponse) };
|
|
@@ -279,6 +281,25 @@ export class OpenID4VCIClientV1_0_13 {
|
|
|
279
281
|
this._state.pkce.codeVerifier = opts.codeVerifier;
|
|
280
282
|
}
|
|
281
283
|
this.assertIssuerData();
|
|
284
|
+
const asOpts: AuthorizationServerOpts = { ...opts?.asOpts };
|
|
285
|
+
const kid = asOpts.clientOpts?.kid ?? this._state.kid ?? this._state.authorizationRequestOpts?.requestObjectOpts?.kid;
|
|
286
|
+
const clientAssertionType =
|
|
287
|
+
asOpts.clientOpts?.clientAssertionType ??
|
|
288
|
+
(kid && clientId && typeof asOpts.clientOpts?.signCallbacks?.signCallback === 'function'
|
|
289
|
+
? 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
290
|
+
: undefined);
|
|
291
|
+
if (this.isEBSI() || (clientId && kid)) {
|
|
292
|
+
if (!clientId) {
|
|
293
|
+
throw Error(`Client id expected for EBSI`);
|
|
294
|
+
}
|
|
295
|
+
asOpts.clientOpts = {
|
|
296
|
+
...asOpts.clientOpts,
|
|
297
|
+
clientId,
|
|
298
|
+
...(kid && { kid }),
|
|
299
|
+
...(clientAssertionType && { clientAssertionType }),
|
|
300
|
+
signCallbacks: asOpts.clientOpts?.signCallbacks ?? this._state.authorizationRequestOpts?.requestObjectOpts?.signCallbacks,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
282
303
|
|
|
283
304
|
if (clientId) {
|
|
284
305
|
this._state.clientId = clientId;
|
|
@@ -302,7 +323,8 @@ export class OpenID4VCIClientV1_0_13 {
|
|
|
302
323
|
...(!this._state.pkce.disabled && { codeVerifier: this._state.pkce.codeVerifier }),
|
|
303
324
|
code,
|
|
304
325
|
redirectUri,
|
|
305
|
-
asOpts
|
|
326
|
+
asOpts,
|
|
327
|
+
...(opts?.additionalRequestParams && { additionalParams: opts.additionalRequestParams }),
|
|
306
328
|
});
|
|
307
329
|
|
|
308
330
|
if (response.errorBody) {
|
|
@@ -636,8 +658,13 @@ export class OpenID4VCIClientV1_0_13 {
|
|
|
636
658
|
}
|
|
637
659
|
}
|
|
638
660
|
|
|
639
|
-
|
|
640
|
-
|
|
661
|
+
return (
|
|
662
|
+
this.clientId?.includes('ebsi') ||
|
|
663
|
+
this._state.kid?.includes('did:ebsi:') ||
|
|
664
|
+
this.getIssuer().includes('ebsi') ||
|
|
665
|
+
this.endpointMetadata.credentialIssuerMetadata?.authorization_endpoint?.includes('ebsi.eu') ||
|
|
666
|
+
this.endpointMetadata.credentialIssuerMetadata?.authorization_server?.includes('ebsi.eu')
|
|
667
|
+
);
|
|
641
668
|
}
|
|
642
669
|
|
|
643
670
|
private assertIssuerData(): void {
|
|
@@ -53,7 +53,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
53
53
|
if (jwt) {
|
|
54
54
|
this.withJwt(jwt);
|
|
55
55
|
} else {
|
|
56
|
-
this.withTyp(version < OpenId4VCIVersion.VER_1_0_11 || mode === '
|
|
56
|
+
this.withTyp(version < OpenId4VCIVersion.VER_1_0_11 || mode === 'JWT' ? 'JWT' : 'openid4vci-proof+jwt');
|
|
57
57
|
}
|
|
58
58
|
if (accessTokenResponse) {
|
|
59
59
|
this.withAccessTokenResponse(accessTokenResponse);
|
|
@@ -64,7 +64,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
64
64
|
jwt,
|
|
65
65
|
callbacks,
|
|
66
66
|
version,
|
|
67
|
-
mode = '
|
|
67
|
+
mode = 'JWT',
|
|
68
68
|
}: {
|
|
69
69
|
jwt?: Jwt;
|
|
70
70
|
callbacks: ProofOfPossessionCallbacks<DIDDoc>;
|
|
@@ -144,11 +144,11 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
144
144
|
withTyp(typ: Typ): this {
|
|
145
145
|
if (this.mode === 'pop' && this.version >= OpenId4VCIVersion.VER_1_0_11) {
|
|
146
146
|
if (!!typ && typ !== 'openid4vci-proof+jwt') {
|
|
147
|
-
throw Error(
|
|
147
|
+
throw Error(`typ must be openid4vci-proof+jwt for version 1.0.11 and up. Provided: ${typ}`);
|
|
148
148
|
}
|
|
149
149
|
} else {
|
|
150
|
-
if (!!typ && typ !== '
|
|
151
|
-
throw Error(
|
|
150
|
+
if (!!typ && typ !== 'JWT') {
|
|
151
|
+
throw Error(`typ must be jwt for version 1.0.10 and below. Provided: ${typ}`);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
this.typ = typ;
|
|
@@ -216,7 +216,7 @@ export class ProofOfPossessionBuilder<DIDDoc> {
|
|
|
216
216
|
this.mode,
|
|
217
217
|
this.callbacks,
|
|
218
218
|
{
|
|
219
|
-
typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 || this.mode === '
|
|
219
|
+
typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 || this.mode === 'JWT' ? 'JWT' : 'openid4vci-proof+jwt'),
|
|
220
220
|
kid: this.kid,
|
|
221
221
|
jwk: this.jwk,
|
|
222
222
|
jti: this.jti,
|
|
@@ -29,7 +29,7 @@ import { getMockData } from './data/VciDataFixtures';
|
|
|
29
29
|
const partialJWT = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmN';
|
|
30
30
|
|
|
31
31
|
const jwt1_0_08: Jwt = {
|
|
32
|
-
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
32
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
33
33
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL },
|
|
34
34
|
};
|
|
35
35
|
|
|
@@ -20,7 +20,7 @@ const partialJWT = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmN';
|
|
|
20
20
|
const partialJWT_withoutDid = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJlYmZlYjFmNzEyZWJjNmYxYzI3N';
|
|
21
21
|
|
|
22
22
|
/*const jwtv1_0_08: Jwt = {
|
|
23
|
-
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
23
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
24
24
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL },
|
|
25
25
|
};*/
|
|
26
26
|
|
|
@@ -31,12 +31,12 @@ const partialJWT = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmN';
|
|
|
31
31
|
const partialJWT_withoutDid = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJlYmZlYjFmNzEyZWJjNmYxYzI3N';
|
|
32
32
|
|
|
33
33
|
const jwt: Jwt = {
|
|
34
|
-
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
34
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
35
35
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL },
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const jwt_withoutDid: Jwt = {
|
|
39
|
-
header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
39
|
+
header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
40
40
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL },
|
|
41
41
|
};
|
|
42
42
|
|
package/lib/__tests__/IT.spec.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AccessTokenResponse,
|
|
3
|
-
Alg,
|
|
3
|
+
Alg,
|
|
4
|
+
CredentialOfferPayloadV1_0_13,
|
|
4
5
|
CredentialOfferRequestWithBaseUrl,
|
|
5
6
|
Jwt,
|
|
6
7
|
OpenId4VCIVersion,
|
|
7
|
-
ProofOfPossession,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
ProofOfPossession,
|
|
9
|
+
resolveCredentialOfferURI,
|
|
10
|
+
WellKnownEndpoints,
|
|
11
|
+
} from '@sphereon/oid4vci-common';
|
|
10
12
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
11
13
|
// @ts-ignore
|
|
12
14
|
import nock from 'nock';
|
|
@@ -271,10 +273,6 @@ describe('OID4VCI-Client should', () => {
|
|
|
271
273
|
.withTokenFromResponse(accessTokenResponse.successBody!)
|
|
272
274
|
.build();
|
|
273
275
|
|
|
274
|
-
//TS2322: Type '(args: ProofOfPossessionCallbackArgs) => Promise<string>'
|
|
275
|
-
// is not assignable to type 'ProofOfPossessionCallback'.
|
|
276
|
-
// Types of parameters 'args' and 'args' are incompatible.
|
|
277
|
-
// Property 'kid' is missing in type '{ header: unknown; payload: unknown; }' but required in type 'ProofOfPossessionCallbackArgs'.
|
|
278
276
|
const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({
|
|
279
277
|
jwt: jwtDid,
|
|
280
278
|
callbacks: {
|
|
@@ -315,8 +313,8 @@ describe('OID4VCI-Client should', () => {
|
|
|
315
313
|
});
|
|
316
314
|
|
|
317
315
|
nock(ISSUER_URL)
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
.post(/token.*/)
|
|
317
|
+
.reply(200, JSON.stringify(mockedAccessTokenResponse));
|
|
320
318
|
|
|
321
319
|
/* The actual access token calls */
|
|
322
320
|
const accessTokenClient: AccessTokenClient = new AccessTokenClient();
|
|
@@ -324,21 +322,17 @@ describe('OID4VCI-Client should', () => {
|
|
|
324
322
|
expect(accessTokenResponse.successBody).toEqual(mockedAccessTokenResponse);
|
|
325
323
|
// Get the credential
|
|
326
324
|
nock(ISSUER_URL)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
325
|
+
.post(/credential/)
|
|
326
|
+
.reply(200, {
|
|
327
|
+
format: 'jwt-vc',
|
|
328
|
+
credential: mockedVC,
|
|
329
|
+
});
|
|
332
330
|
const credReqClient = CredentialRequestClientBuilder.fromCredentialOffer({ credentialOffer: credentialOffer })
|
|
333
|
-
|
|
331
|
+
.withFormat('jwt_vc')
|
|
334
332
|
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
.withTokenFromResponse(accessTokenResponse.successBody!)
|
|
334
|
+
.build();
|
|
337
335
|
|
|
338
|
-
//TS2322: Type '(args: ProofOfPossessionCallbackArgs) => Promise<string>'
|
|
339
|
-
// is not assignable to type 'ProofOfPossessionCallback'.
|
|
340
|
-
// Types of parameters 'args' and 'args' are incompatible.
|
|
341
|
-
// Property 'kid' is missing in type '{ header: unknown; payload: unknown; }' but required in type 'ProofOfPossessionCallbackArgs'.
|
|
342
336
|
const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({
|
|
343
337
|
jwt: jwtDid,
|
|
344
338
|
callbacks: {
|
|
@@ -346,13 +340,13 @@ describe('OID4VCI-Client should', () => {
|
|
|
346
340
|
},
|
|
347
341
|
version: OpenId4VCIVersion.VER_1_0_11,
|
|
348
342
|
})
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
343
|
+
.withEndpointMetadata({
|
|
344
|
+
issuer: 'https://issuer.research.identiproof.io',
|
|
345
|
+
credential_endpoint: 'https://issuer.research.identiproof.io/credential',
|
|
346
|
+
token_endpoint: 'https://issuer.research.identiproof.io/token',
|
|
347
|
+
})
|
|
348
|
+
.withKid('did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1')
|
|
349
|
+
.build();
|
|
356
350
|
const credResponse = await credReqClient.acquireCredentialsUsingProof({
|
|
357
351
|
proofInput: proof,
|
|
358
352
|
credentialTypes: credentialOffer.original_credential_offer.credential_configuration_ids[0],
|
|
@@ -8,12 +8,12 @@ import { ProofOfPossessionBuilder } from '..';
|
|
|
8
8
|
import { IDENTIPROOF_ISSUER_URL } from './MetadataMocks';
|
|
9
9
|
|
|
10
10
|
const jwt: Jwt = {
|
|
11
|
-
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
11
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
12
12
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now() / 1000 },
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const jwt_withoutDid: Jwt = {
|
|
16
|
-
header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: '
|
|
16
|
+
header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' },
|
|
17
17
|
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now() / 1000 },
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -62,10 +62,10 @@ describe('ProofOfPossession Builder ', () => {
|
|
|
62
62
|
it('should fail without supplied proof or callbacks and with kid without did', async function () {
|
|
63
63
|
await expect(
|
|
64
64
|
ProofOfPossessionBuilder.fromProof(undefined as never, OpenId4VCIVersion.VER_1_0_13)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
.withIssuer(IDENTIPROOF_ISSUER_URL)
|
|
66
|
+
.withClientId('sphereon:wallet')
|
|
67
|
+
.withKid(kid_withoutDid)
|
|
68
|
+
.build(),
|
|
69
69
|
).rejects.toThrow(Error(PROOF_CANT_BE_CONSTRUCTED));
|
|
70
70
|
});
|
|
71
71
|
|
|
@@ -87,11 +87,11 @@ describe('ProofOfPossession Builder ', () => {
|
|
|
87
87
|
callbacks: { signCallback: proofOfPossessionCallbackFunction },
|
|
88
88
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
89
89
|
})
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
.withJwt(undefined as never)
|
|
91
|
+
.withIssuer(IDENTIPROOF_ISSUER_URL)
|
|
92
|
+
.withClientId('sphereon:wallet')
|
|
93
|
+
.withKid(kid_withoutDid)
|
|
94
|
+
.build(),
|
|
95
95
|
).toThrow(Error(NO_JWT_PROVIDED));
|
|
96
96
|
});
|
|
97
97
|
|
|
@@ -118,10 +118,10 @@ describe('ProofOfPossession Builder ', () => {
|
|
|
118
118
|
},
|
|
119
119
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
120
120
|
})
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
.withIssuer(IDENTIPROOF_ISSUER_URL)
|
|
122
|
+
.withKid(kid_withoutDid)
|
|
123
|
+
.withClientId('sphereon:wallet')
|
|
124
|
+
.build();
|
|
125
125
|
expect(proof).toBeDefined();
|
|
126
126
|
});
|
|
127
127
|
|
|
@@ -152,10 +152,10 @@ describe('ProofOfPossession Builder ', () => {
|
|
|
152
152
|
callbacks: { signCallback: proofOfPossessionCallbackFunction },
|
|
153
153
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
154
154
|
})
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
.withIssuer(IDENTIPROOF_ISSUER_URL)
|
|
156
|
+
.withClientId('sphereon:wallet')
|
|
157
|
+
.withKid(kid_withoutDid)
|
|
158
|
+
.build(),
|
|
159
159
|
).rejects.toThrow(Error(JWS_NOT_VALID));
|
|
160
160
|
});
|
|
161
161
|
|
|
@@ -186,10 +186,10 @@ describe('ProofOfPossession Builder ', () => {
|
|
|
186
186
|
callbacks: { signCallback: proofOfPossessionCallbackFunction },
|
|
187
187
|
version: OpenId4VCIVersion.VER_1_0_08,
|
|
188
188
|
})
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
.withIssuer(IDENTIPROOF_ISSUER_URL)
|
|
190
|
+
.withClientId('sphereon:wallet')
|
|
191
|
+
.withKid(kid_withoutDid)
|
|
192
|
+
.build(),
|
|
193
193
|
).rejects.toThrow(Error(JWS_NOT_VALID));
|
|
194
194
|
});
|
|
195
195
|
});
|