@sphereon/oid4vci-client 0.10.4-unstable.2 → 0.10.4-unstable.21
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 +5 -5
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +42 -22
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AccessTokenClientV1_0_11.d.ts +29 -0
- package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -0
- package/dist/AccessTokenClientV1_0_11.js +212 -0
- package/dist/AccessTokenClientV1_0_11.js.map +1 -0
- package/dist/AuthorizationCodeClient.d.ts +4 -4
- package/dist/AuthorizationCodeClient.d.ts.map +1 -1
- package/dist/AuthorizationCodeClient.js +14 -3
- package/dist/AuthorizationCodeClient.js.map +1 -1
- package/dist/AuthorizationCodeClientV1_0_11.d.ts +9 -0
- package/dist/AuthorizationCodeClientV1_0_11.d.ts.map +1 -0
- package/dist/AuthorizationCodeClientV1_0_11.js +132 -0
- package/dist/AuthorizationCodeClientV1_0_11.js.map +1 -0
- package/dist/CredentialOfferClient.d.ts.map +1 -1
- package/dist/CredentialOfferClient.js +14 -25
- package/dist/CredentialOfferClient.js.map +1 -1
- package/dist/CredentialOfferClientV1_0_11.d.ts +10 -0
- package/dist/CredentialOfferClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialOfferClientV1_0_11.js +103 -0
- package/dist/CredentialOfferClientV1_0_11.js.map +1 -0
- package/dist/CredentialRequestClient.d.ts +1 -1
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +9 -6
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/CredentialRequestClientBuilder.d.ts +3 -3
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js +2 -2
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts +46 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js +117 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts +44 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.js +151 -0
- package/dist/CredentialRequestClientV1_0_11.js.map +1 -0
- package/dist/MetadataClient.d.ts +5 -15
- package/dist/MetadataClient.d.ts.map +1 -1
- package/dist/MetadataClient.js +13 -33
- package/dist/MetadataClient.js.map +1 -1
- package/dist/MetadataClientV1_0_11.d.ts +31 -0
- package/dist/MetadataClientV1_0_11.d.ts.map +1 -0
- package/dist/MetadataClientV1_0_11.js +182 -0
- package/dist/MetadataClientV1_0_11.js.map +1 -0
- package/dist/OpenID4VCIClient.d.ts +5 -17
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +3 -74
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/OpenID4VCIClientV1_0_11.d.ts +107 -0
- package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -0
- package/dist/OpenID4VCIClientV1_0_11.js +462 -0
- package/dist/OpenID4VCIClientV1_0_11.js.map +1 -0
- package/dist/functions/OpenIDUtils.d.ts +12 -0
- package/dist/functions/OpenIDUtils.d.ts.map +1 -0
- package/dist/functions/OpenIDUtils.js +37 -0
- package/dist/functions/OpenIDUtils.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/lib/AccessTokenClient.ts +54 -22
- package/lib/AccessTokenClientV1_0_11.ts +255 -0
- package/lib/AuthorizationCodeClient.ts +31 -11
- package/lib/AuthorizationCodeClientV1_0_11.ts +167 -0
- package/lib/CredentialOfferClient.ts +13 -25
- package/lib/CredentialOfferClientV1_0_11.ts +112 -0
- package/lib/CredentialRequestClient.ts +11 -7
- package/lib/CredentialRequestClientBuilder.ts +9 -8
- package/lib/CredentialRequestClientBuilderV1_0_11.ts +156 -0
- package/lib/CredentialRequestClientV1_0_11.ts +190 -0
- package/lib/MetadataClient.ts +26 -48
- package/lib/MetadataClientV1_0_11.ts +186 -0
- package/lib/OpenID4VCIClient.ts +10 -92
- package/lib/OpenID4VCIClientV1_0_11.ts +644 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +34 -6
- package/lib/__tests__/CredentialRequestClient.spec.ts +56 -39
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +4 -4
- package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +316 -0
- package/lib/__tests__/EBSIE2E.spec.test.ts +2 -2
- package/lib/__tests__/IT.spec.ts +222 -11
- package/lib/__tests__/IssuanceInitiation.spec.ts +32 -51
- package/lib/__tests__/IssuanceInitiationV1_0_11.spec.ts +62 -0
- package/lib/__tests__/MattrE2E.spec.test.ts +2 -2
- package/lib/__tests__/MetadataClient.spec.ts +70 -6
- package/lib/__tests__/MetadataMocks.ts +41 -2
- package/lib/__tests__/OpenID4VCIClient.spec.ts +1 -1
- package/lib/__tests__/{OpenID4VCIClientPAR.spec.ts → OpenID4VCIClientPARV1_0_11.spec.ts} +5 -5
- package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +202 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +1 -1
- package/lib/__tests__/SdJwt.spec.ts +31 -21
- package/lib/__tests__/SphereonE2E.spec.test.ts +3 -3
- package/lib/__tests__/data/VciDataFixtures.ts +664 -27
- package/lib/functions/OpenIDUtils.ts +25 -0
- package/lib/index.ts +8 -1
- package/package.json +3 -3
package/lib/MetadataClient.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AuthorizationServerMetadata,
|
|
3
3
|
AuthorizationServerType,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
CredentialIssuerMetadataV1_0_13,
|
|
5
|
+
CredentialOfferPayloadV1_0_13,
|
|
6
6
|
CredentialOfferRequestWithBaseUrl,
|
|
7
|
-
|
|
7
|
+
EndpointMetadataResultV1_0_13,
|
|
8
8
|
getIssuerFromCredentialOfferPayload,
|
|
9
|
+
IssuerMetadataV1_0_13,
|
|
9
10
|
OpenIDResponse,
|
|
10
|
-
WellKnownEndpoints
|
|
11
|
-
} from '@sphereon/oid4vci-common'
|
|
11
|
+
WellKnownEndpoints
|
|
12
|
+
} from '@sphereon/oid4vci-common'
|
|
12
13
|
import Debug from 'debug';
|
|
13
14
|
|
|
14
|
-
import {
|
|
15
|
+
import { retrieveWellknown } from './functions/OpenIDUtils'
|
|
15
16
|
|
|
16
17
|
const debug = Debug('sphereon:oid4vci:metadata');
|
|
17
18
|
|
|
@@ -21,15 +22,15 @@ export class MetadataClient {
|
|
|
21
22
|
*
|
|
22
23
|
* @param credentialOffer
|
|
23
24
|
*/
|
|
24
|
-
public static async retrieveAllMetadataFromCredentialOffer(credentialOffer: CredentialOfferRequestWithBaseUrl): Promise<
|
|
25
|
-
return MetadataClient.retrieveAllMetadataFromCredentialOfferRequest(credentialOffer.credential_offer);
|
|
25
|
+
public static async retrieveAllMetadataFromCredentialOffer(credentialOffer: CredentialOfferRequestWithBaseUrl): Promise<EndpointMetadataResultV1_0_13> {
|
|
26
|
+
return MetadataClient.retrieveAllMetadataFromCredentialOfferRequest(credentialOffer.credential_offer as CredentialOfferPayloadV1_0_13);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Retrieve the metada using the initiation request obtained from a previous step
|
|
30
31
|
* @param request
|
|
31
32
|
*/
|
|
32
|
-
public static async retrieveAllMetadataFromCredentialOfferRequest(request:
|
|
33
|
+
public static async retrieveAllMetadataFromCredentialOfferRequest(request: CredentialOfferPayloadV1_0_13): Promise<EndpointMetadataResultV1_0_13> {
|
|
33
34
|
const issuer = getIssuerFromCredentialOfferPayload(request);
|
|
34
35
|
if (issuer) {
|
|
35
36
|
return MetadataClient.retrieveAllMetadata(issuer);
|
|
@@ -42,13 +43,13 @@ export class MetadataClient {
|
|
|
42
43
|
* @param issuer The issuer URL
|
|
43
44
|
* @param opts
|
|
44
45
|
*/
|
|
45
|
-
public static async retrieveAllMetadata(issuer: string, opts?: { errorOnNotFound: boolean }): Promise<
|
|
46
|
+
public static async retrieveAllMetadata(issuer: string, opts?: { errorOnNotFound: boolean }): Promise<EndpointMetadataResultV1_0_13> {
|
|
46
47
|
let token_endpoint: string | undefined;
|
|
47
48
|
let credential_endpoint: string | undefined;
|
|
48
49
|
let deferred_credential_endpoint: string | undefined;
|
|
49
50
|
let authorization_endpoint: string | undefined;
|
|
50
51
|
let authorizationServerType: AuthorizationServerType = 'OID4VCI';
|
|
51
|
-
let
|
|
52
|
+
let authorization_servers: string[] = [issuer];
|
|
52
53
|
const oid4vciResponse = await MetadataClient.retrieveOpenID4VCIServerMetadata(issuer, { errorOnNotFound: false }); // We will handle errors later, given we will also try other metadata locations
|
|
53
54
|
let credentialIssuerMetadata = oid4vciResponse?.successBody;
|
|
54
55
|
if (credentialIssuerMetadata) {
|
|
@@ -58,16 +59,14 @@ export class MetadataClient {
|
|
|
58
59
|
if (credentialIssuerMetadata.token_endpoint) {
|
|
59
60
|
token_endpoint = credentialIssuerMetadata.token_endpoint;
|
|
60
61
|
}
|
|
61
|
-
if (credentialIssuerMetadata.
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
if (credentialIssuerMetadata.authorization_endpoint) {
|
|
65
|
-
authorization_endpoint = credentialIssuerMetadata.authorization_endpoint;
|
|
62
|
+
if (credentialIssuerMetadata.authorization_servers) {
|
|
63
|
+
authorization_servers = credentialIssuerMetadata.authorization_servers;
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
// No specific OID4VCI endpoint. Either can be an OAuth2 AS or an OIDC IDP. Let's start with OIDC first
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
// TODO: for now we're taking just the first one
|
|
68
|
+
let response: OpenIDResponse<AuthorizationServerMetadata> = await retrieveWellknown(
|
|
69
|
+
authorization_servers[0],
|
|
71
70
|
WellKnownEndpoints.OPENID_CONFIGURATION,
|
|
72
71
|
{
|
|
73
72
|
errorOnNotFound: false,
|
|
@@ -79,13 +78,14 @@ export class MetadataClient {
|
|
|
79
78
|
authorizationServerType = 'OIDC';
|
|
80
79
|
} else {
|
|
81
80
|
// Now let's do OAuth2
|
|
82
|
-
|
|
81
|
+
// TODO: for now we're taking just the first one
|
|
82
|
+
response = await retrieveWellknown(authorization_servers[0], WellKnownEndpoints.OAUTH_AS, { errorOnNotFound: false });
|
|
83
83
|
authMetadata = response.successBody;
|
|
84
84
|
}
|
|
85
85
|
if (!authMetadata) {
|
|
86
86
|
// We will always throw an error, no matter whether the user provided the option not to, because this is bad.
|
|
87
|
-
if (issuer
|
|
88
|
-
throw Error(`Issuer ${issuer} provided a separate authorization server ${
|
|
87
|
+
if (!authorization_servers.includes(issuer)) {
|
|
88
|
+
throw Error(`Issuer ${issuer} provided a separate authorization server ${authorization_servers}, but that server did not provide metadata`);
|
|
89
89
|
}
|
|
90
90
|
} else {
|
|
91
91
|
if (!authorizationServerType) {
|
|
@@ -103,7 +103,7 @@ export class MetadataClient {
|
|
|
103
103
|
}
|
|
104
104
|
authorization_endpoint = authMetadata.authorization_endpoint;
|
|
105
105
|
if (!authMetadata.token_endpoint) {
|
|
106
|
-
throw Error(`Authorization Sever ${
|
|
106
|
+
throw Error(`Authorization Sever ${authorization_servers} did not provide a token_endpoint`);
|
|
107
107
|
} else if (token_endpoint && authMetadata.token_endpoint !== token_endpoint) {
|
|
108
108
|
throw Error(
|
|
109
109
|
`Credential issuer has a different token_endpoint (${token_endpoint}) from the Authorization Server (${authMetadata.token_endpoint})`,
|
|
@@ -152,7 +152,7 @@ export class MetadataClient {
|
|
|
152
152
|
|
|
153
153
|
if (!credentialIssuerMetadata && authMetadata) {
|
|
154
154
|
// Apparently everything worked out and the issuer is exposing everything in oAuth2/OIDC well-knowns. Spec is vague about this situation, but we can support it
|
|
155
|
-
credentialIssuerMetadata = authMetadata as
|
|
155
|
+
credentialIssuerMetadata = authMetadata as CredentialIssuerMetadataV1_0_13;
|
|
156
156
|
}
|
|
157
157
|
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);
|
|
158
158
|
return {
|
|
@@ -160,7 +160,7 @@ export class MetadataClient {
|
|
|
160
160
|
token_endpoint,
|
|
161
161
|
credential_endpoint,
|
|
162
162
|
deferred_credential_endpoint,
|
|
163
|
-
authorization_server,
|
|
163
|
+
authorization_server: authorization_servers[0],
|
|
164
164
|
authorization_endpoint,
|
|
165
165
|
authorizationServerType,
|
|
166
166
|
credentialIssuerMetadata: credentialIssuerMetadata,
|
|
@@ -178,31 +178,9 @@ export class MetadataClient {
|
|
|
178
178
|
opts?: {
|
|
179
179
|
errorOnNotFound?: boolean;
|
|
180
180
|
},
|
|
181
|
-
): Promise<OpenIDResponse<
|
|
182
|
-
return
|
|
181
|
+
): Promise<OpenIDResponse<IssuerMetadataV1_0_13> | undefined> {
|
|
182
|
+
return retrieveWellknown(issuerHost, WellKnownEndpoints.OPENID4VCI_ISSUER, {
|
|
183
183
|
errorOnNotFound: opts?.errorOnNotFound === undefined ? true : opts.errorOnNotFound,
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Allows to retrieve information from a well-known location
|
|
189
|
-
*
|
|
190
|
-
* @param host The host
|
|
191
|
-
* @param endpointType The endpoint type, currently supports OID4VCI, OIDC and OAuth2 endpoint types
|
|
192
|
-
* @param opts Options, like for instance whether an error should be thrown in case the endpoint doesn't exist
|
|
193
|
-
*/
|
|
194
|
-
public static async retrieveWellknown<T>(
|
|
195
|
-
host: string,
|
|
196
|
-
endpointType: WellKnownEndpoints,
|
|
197
|
-
opts?: { errorOnNotFound?: boolean },
|
|
198
|
-
): Promise<OpenIDResponse<T>> {
|
|
199
|
-
const result: OpenIDResponse<T> = await getJson(`${host.endsWith('/') ? host.slice(0, -1) : host}${endpointType}`, {
|
|
200
|
-
exceptionOnHttpErrorStatus: opts?.errorOnNotFound,
|
|
201
|
-
});
|
|
202
|
-
if (result.origResponse.status >= 400) {
|
|
203
|
-
// We only get here when error on not found is false
|
|
204
|
-
debug(`host ${host} with endpoint type ${endpointType} status: ${result.origResponse.status}, ${result.origResponse.statusText}`);
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
186
|
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthorizationServerMetadata,
|
|
3
|
+
AuthorizationServerType,
|
|
4
|
+
CredentialIssuerMetadataV1_0_11,
|
|
5
|
+
CredentialOfferPayload,
|
|
6
|
+
CredentialOfferRequestWithBaseUrl,
|
|
7
|
+
EndpointMetadataResultV1_0_11,
|
|
8
|
+
getIssuerFromCredentialOfferPayload, IssuerMetadataV1_0_08,
|
|
9
|
+
OpenIDResponse,
|
|
10
|
+
WellKnownEndpoints
|
|
11
|
+
} from '@sphereon/oid4vci-common'
|
|
12
|
+
import Debug from 'debug';
|
|
13
|
+
|
|
14
|
+
import { retrieveWellknown } from './functions/OpenIDUtils'
|
|
15
|
+
|
|
16
|
+
const debug = Debug('sphereon:oid4vci:metadata');
|
|
17
|
+
|
|
18
|
+
export class MetadataClientV1_0_11 {
|
|
19
|
+
/**
|
|
20
|
+
* Retrieve metadata using the Initiation obtained from a previous step
|
|
21
|
+
*
|
|
22
|
+
* @param credentialOffer
|
|
23
|
+
*/
|
|
24
|
+
public static async retrieveAllMetadataFromCredentialOffer(credentialOffer: CredentialOfferRequestWithBaseUrl): Promise<EndpointMetadataResultV1_0_11> {
|
|
25
|
+
return MetadataClientV1_0_11.retrieveAllMetadataFromCredentialOfferRequest(credentialOffer.credential_offer);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Retrieve the metada using the initiation request obtained from a previous step
|
|
30
|
+
* @param request
|
|
31
|
+
*/
|
|
32
|
+
public static async retrieveAllMetadataFromCredentialOfferRequest(request: CredentialOfferPayload): Promise<EndpointMetadataResultV1_0_11> {
|
|
33
|
+
const issuer = getIssuerFromCredentialOfferPayload(request);
|
|
34
|
+
if (issuer) {
|
|
35
|
+
return MetadataClientV1_0_11.retrieveAllMetadata(issuer);
|
|
36
|
+
}
|
|
37
|
+
throw new Error("can't retrieve metadata from CredentialOfferRequest. No issuer field is present");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Retrieve all metadata from an issuer
|
|
42
|
+
* @param issuer The issuer URL
|
|
43
|
+
* @param opts
|
|
44
|
+
*/
|
|
45
|
+
public static async retrieveAllMetadata(issuer: string, opts?: { errorOnNotFound: boolean }): Promise<EndpointMetadataResultV1_0_11> {
|
|
46
|
+
let token_endpoint: string | undefined;
|
|
47
|
+
let credential_endpoint: string | undefined;
|
|
48
|
+
let deferred_credential_endpoint: string | undefined;
|
|
49
|
+
let authorization_endpoint: string | undefined;
|
|
50
|
+
let authorizationServerType: AuthorizationServerType = 'OID4VCI';
|
|
51
|
+
let authorization_server: string = issuer;
|
|
52
|
+
const oid4vciResponse = await MetadataClientV1_0_11.retrieveOpenID4VCIServerMetadata(issuer, { errorOnNotFound: false }); // We will handle errors later, given we will also try other metadata locations
|
|
53
|
+
let credentialIssuerMetadata = oid4vciResponse?.successBody;
|
|
54
|
+
if (credentialIssuerMetadata) {
|
|
55
|
+
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${JSON.stringify(credentialIssuerMetadata)}`);
|
|
56
|
+
credential_endpoint = credentialIssuerMetadata.credential_endpoint;
|
|
57
|
+
deferred_credential_endpoint = credentialIssuerMetadata.deferred_credential_endpoint;
|
|
58
|
+
if (credentialIssuerMetadata.token_endpoint) {
|
|
59
|
+
token_endpoint = credentialIssuerMetadata.token_endpoint;
|
|
60
|
+
}
|
|
61
|
+
if (credentialIssuerMetadata.authorization_server) {
|
|
62
|
+
authorization_server = credentialIssuerMetadata.authorization_server;
|
|
63
|
+
}
|
|
64
|
+
if (credentialIssuerMetadata.authorization_endpoint) {
|
|
65
|
+
authorization_endpoint = credentialIssuerMetadata.authorization_endpoint;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// No specific OID4VCI endpoint. Either can be an OAuth2 AS or an OIDC IDP. Let's start with OIDC first
|
|
69
|
+
let response: OpenIDResponse<AuthorizationServerMetadata> = await retrieveWellknown(
|
|
70
|
+
authorization_server,
|
|
71
|
+
WellKnownEndpoints.OPENID_CONFIGURATION,
|
|
72
|
+
{
|
|
73
|
+
errorOnNotFound: false,
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
let authMetadata = response.successBody;
|
|
77
|
+
if (authMetadata) {
|
|
78
|
+
debug(`Issuer ${issuer} has OpenID Connect Server metadata in well-known location`);
|
|
79
|
+
authorizationServerType = 'OIDC';
|
|
80
|
+
} else {
|
|
81
|
+
// Now let's do OAuth2
|
|
82
|
+
response = await retrieveWellknown(authorization_server, WellKnownEndpoints.OAUTH_AS, { errorOnNotFound: false });
|
|
83
|
+
authMetadata = response.successBody;
|
|
84
|
+
}
|
|
85
|
+
if (!authMetadata) {
|
|
86
|
+
// We will always throw an error, no matter whether the user provided the option not to, because this is bad.
|
|
87
|
+
if (issuer !== authorization_server) {
|
|
88
|
+
throw Error(`Issuer ${issuer} provided a separate authorization server ${authorization_server}, but that server did not provide metadata`);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
if (!authorizationServerType) {
|
|
92
|
+
authorizationServerType = 'OAuth 2.0';
|
|
93
|
+
}
|
|
94
|
+
debug(`Issuer ${issuer} has ${authorizationServerType} Server metadata in well-known location`);
|
|
95
|
+
if (!authMetadata.authorization_endpoint) {
|
|
96
|
+
console.warn(
|
|
97
|
+
`Issuer ${issuer} of type ${authorizationServerType} has no authorization_endpoint! Will use ${authorization_endpoint}. This only works for pre-authorized flows`,
|
|
98
|
+
);
|
|
99
|
+
} else if (authorization_endpoint && authMetadata.authorization_endpoint !== authorization_endpoint) {
|
|
100
|
+
throw Error(
|
|
101
|
+
`Credential issuer has a different authorization_endpoint (${authorization_endpoint}) from the Authorization Server (${authMetadata.authorization_endpoint})`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
authorization_endpoint = authMetadata.authorization_endpoint;
|
|
105
|
+
if (!authMetadata.token_endpoint) {
|
|
106
|
+
throw Error(`Authorization Sever ${authorization_server} did not provide a token_endpoint`);
|
|
107
|
+
} else if (token_endpoint && authMetadata.token_endpoint !== token_endpoint) {
|
|
108
|
+
throw Error(
|
|
109
|
+
`Credential issuer has a different token_endpoint (${token_endpoint}) from the Authorization Server (${authMetadata.token_endpoint})`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
token_endpoint = authMetadata.token_endpoint;
|
|
113
|
+
if (authMetadata.credential_endpoint) {
|
|
114
|
+
if (credential_endpoint && authMetadata.credential_endpoint !== credential_endpoint) {
|
|
115
|
+
debug(
|
|
116
|
+
`Credential issuer has a different credential_endpoint (${credential_endpoint}) from the Authorization Server (${authMetadata.credential_endpoint}). Will use the issuer value`,
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
credential_endpoint = authMetadata.credential_endpoint;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (authMetadata.deferred_credential_endpoint) {
|
|
123
|
+
if (deferred_credential_endpoint && authMetadata.deferred_credential_endpoint !== deferred_credential_endpoint) {
|
|
124
|
+
debug(
|
|
125
|
+
`Credential issuer has a different deferred_credential_endpoint (${deferred_credential_endpoint}) from the Authorization Server (${authMetadata.deferred_credential_endpoint}). Will use the issuer value`,
|
|
126
|
+
);
|
|
127
|
+
} else {
|
|
128
|
+
deferred_credential_endpoint = authMetadata.deferred_credential_endpoint;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!authorization_endpoint) {
|
|
134
|
+
debug(`Issuer ${issuer} does not expose authorization_endpoint, so only pre-auth will be supported`);
|
|
135
|
+
}
|
|
136
|
+
if (!token_endpoint) {
|
|
137
|
+
debug(`Issuer ${issuer} does not have a token_endpoint listed in well-known locations!`);
|
|
138
|
+
if (opts?.errorOnNotFound) {
|
|
139
|
+
throw Error(`Could not deduce the token_endpoint for ${issuer}`);
|
|
140
|
+
} else {
|
|
141
|
+
token_endpoint = `${issuer}${issuer.endsWith('/') ? 'token' : '/token'}`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (!credential_endpoint) {
|
|
145
|
+
debug(`Issuer ${issuer} does not have a credential_endpoint listed in well-known locations!`);
|
|
146
|
+
if (opts?.errorOnNotFound) {
|
|
147
|
+
throw Error(`Could not deduce the credential endpoint for ${issuer}`);
|
|
148
|
+
} else {
|
|
149
|
+
credential_endpoint = `${issuer}${issuer.endsWith('/') ? 'credential' : '/credential'}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!credentialIssuerMetadata && authMetadata) {
|
|
154
|
+
// Apparently everything worked out and the issuer is exposing everything in oAuth2/OIDC well-knowns. Spec is vague about this situation, but we can support it
|
|
155
|
+
credentialIssuerMetadata = authMetadata as CredentialIssuerMetadataV1_0_11;
|
|
156
|
+
}
|
|
157
|
+
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);
|
|
158
|
+
return {
|
|
159
|
+
issuer,
|
|
160
|
+
token_endpoint,
|
|
161
|
+
credential_endpoint,
|
|
162
|
+
deferred_credential_endpoint,
|
|
163
|
+
authorization_server,
|
|
164
|
+
authorization_endpoint,
|
|
165
|
+
authorizationServerType,
|
|
166
|
+
credentialIssuerMetadata: credentialIssuerMetadata as unknown as (Partial<AuthorizationServerMetadata> & IssuerMetadataV1_0_08),
|
|
167
|
+
authorizationServerMetadata: authMetadata,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Retrieve only the OID4VCI metadata for the issuer. So no OIDC/OAuth2 metadata
|
|
173
|
+
*
|
|
174
|
+
* @param issuerHost The issuer hostname
|
|
175
|
+
*/
|
|
176
|
+
public static async retrieveOpenID4VCIServerMetadata(
|
|
177
|
+
issuerHost: string,
|
|
178
|
+
opts?: {
|
|
179
|
+
errorOnNotFound?: boolean;
|
|
180
|
+
},
|
|
181
|
+
): Promise<OpenIDResponse<CredentialIssuerMetadataV1_0_11> | undefined> {
|
|
182
|
+
return retrieveWellknown(issuerHost, WellKnownEndpoints.OPENID4VCI_ISSUER, {
|
|
183
|
+
errorOnNotFound: opts?.errorOnNotFound === undefined ? true : opts.errorOnNotFound,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
package/lib/OpenID4VCIClient.ts
CHANGED
|
@@ -5,12 +5,11 @@ import {
|
|
|
5
5
|
AuthorizationResponse,
|
|
6
6
|
AuthzFlowType,
|
|
7
7
|
CodeChallengeMethod,
|
|
8
|
-
CredentialOfferPayloadV1_0_08,
|
|
9
8
|
CredentialOfferRequestWithBaseUrl,
|
|
10
9
|
CredentialResponse,
|
|
11
10
|
CredentialSupported,
|
|
12
11
|
DefaultURISchemes,
|
|
13
|
-
|
|
12
|
+
EndpointMetadataResultV1_0_13,
|
|
14
13
|
getClientIdFromCredentialOfferPayload,
|
|
15
14
|
getIssuerFromCredentialOfferPayload,
|
|
16
15
|
getSupportedCredentials,
|
|
@@ -20,9 +19,8 @@ import {
|
|
|
20
19
|
OID4VCICredentialFormat,
|
|
21
20
|
OpenId4VCIVersion,
|
|
22
21
|
PKCEOpts,
|
|
23
|
-
ProofOfPossessionCallbacks,
|
|
24
|
-
|
|
25
|
-
} from '@sphereon/oid4vci-common';
|
|
22
|
+
ProofOfPossessionCallbacks, toAuthorizationResponsePayload
|
|
23
|
+
} from '@sphereon/oid4vci-common'
|
|
26
24
|
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
27
25
|
import Debug from 'debug';
|
|
28
26
|
|
|
@@ -43,7 +41,7 @@ export interface OpenID4VCIClientState {
|
|
|
43
41
|
kid?: string;
|
|
44
42
|
jwk?: JWK;
|
|
45
43
|
alg?: Alg | string;
|
|
46
|
-
endpointMetadata?:
|
|
44
|
+
endpointMetadata?: EndpointMetadataResultV1_0_13;
|
|
47
45
|
accessTokenResponse?: AccessTokenResponse;
|
|
48
46
|
authorizationRequestOpts?: AuthorizationRequestOpts;
|
|
49
47
|
authorizationCodeResponse?: AuthorizationResponse;
|
|
@@ -77,7 +75,7 @@ export class OpenID4VCIClient {
|
|
|
77
75
|
pkce?: PKCEOpts;
|
|
78
76
|
authorizationRequest?: AuthorizationRequestOpts; // Can be provided here, or when manually calling createAuthorizationUrl
|
|
79
77
|
jwk?: JWK;
|
|
80
|
-
endpointMetadata?:
|
|
78
|
+
endpointMetadata?: EndpointMetadataResultV1_0_13;
|
|
81
79
|
accessTokenResponse?: AccessTokenResponse;
|
|
82
80
|
authorizationRequestOpts?: AuthorizationRequestOpts;
|
|
83
81
|
authorizationCodeResponse?: AuthorizationResponse;
|
|
@@ -223,13 +221,13 @@ export class OpenID4VCIClient {
|
|
|
223
221
|
endpointMetadata: this.endpointMetadata,
|
|
224
222
|
authorizationRequest: this._state.authorizationRequestOpts,
|
|
225
223
|
credentialOffer: this.credentialOffer,
|
|
226
|
-
|
|
224
|
+
credentialConfigurationSupported: this.getCredentialsSupported(),
|
|
227
225
|
});
|
|
228
226
|
}
|
|
229
227
|
return this._state.authorizationURL;
|
|
230
228
|
}
|
|
231
229
|
|
|
232
|
-
public async retrieveServerMetadata(): Promise<
|
|
230
|
+
public async retrieveServerMetadata(): Promise<EndpointMetadataResultV1_0_13> {
|
|
233
231
|
this.assertIssuerData();
|
|
234
232
|
if (!this._state.endpointMetadata) {
|
|
235
233
|
if (this.credentialOffer) {
|
|
@@ -284,7 +282,6 @@ export class OpenID4VCIClient {
|
|
|
284
282
|
if (this._state.authorizationRequestOpts?.redirectUri && !redirectUri) {
|
|
285
283
|
redirectUri = this._state.authorizationRequestOpts.redirectUri;
|
|
286
284
|
}
|
|
287
|
-
|
|
288
285
|
const response = await accessTokenClient.acquireAccessToken({
|
|
289
286
|
credentialOffer: this.credentialOffer,
|
|
290
287
|
metadata: this.endpointMetadata,
|
|
@@ -441,46 +438,17 @@ export class OpenID4VCIClient {
|
|
|
441
438
|
return JSON.stringify(this._state);
|
|
442
439
|
}
|
|
443
440
|
|
|
444
|
-
// FIXME: We really should convert <v11 to v12 objects first. Right now the logic doesn't map nicely and is brittle.
|
|
445
|
-
// We should resolve IDs to objects first in case of strings.
|
|
446
|
-
// When < v11 convert into a v12 object. When v12 object retain it.
|
|
447
|
-
// Then match the object array on server metadata
|
|
448
441
|
getCredentialsSupported(
|
|
449
|
-
restrictToInitiationTypes: boolean,
|
|
450
442
|
format?: (OID4VCICredentialFormat | string) | (OID4VCICredentialFormat | string)[],
|
|
451
|
-
): CredentialSupported
|
|
443
|
+
): Record<string, CredentialSupported> {
|
|
452
444
|
return getSupportedCredentials({
|
|
453
445
|
issuerMetadata: this.endpointMetadata.credentialIssuerMetadata,
|
|
454
446
|
version: this.version(),
|
|
455
447
|
format: format,
|
|
456
|
-
types:
|
|
448
|
+
types: undefined,
|
|
457
449
|
});
|
|
458
450
|
}
|
|
459
451
|
|
|
460
|
-
getCredentialOfferTypes(): string[][] {
|
|
461
|
-
if (!this.credentialOffer) {
|
|
462
|
-
return [];
|
|
463
|
-
} else if (this.credentialOffer.version < OpenId4VCIVersion.VER_1_0_11) {
|
|
464
|
-
const orig = this.credentialOffer.original_credential_offer as CredentialOfferPayloadV1_0_08;
|
|
465
|
-
const types: string[] = typeof orig.credential_type === 'string' ? [orig.credential_type] : orig.credential_type;
|
|
466
|
-
const result: string[][] = [];
|
|
467
|
-
result[0] = types;
|
|
468
|
-
return result;
|
|
469
|
-
} else {
|
|
470
|
-
return this.credentialOffer.credential_offer.credentials.map((c) => {
|
|
471
|
-
if (typeof c === 'string') {
|
|
472
|
-
return [c];
|
|
473
|
-
} else if ('types' in c) {
|
|
474
|
-
return c.types;
|
|
475
|
-
} else if ('vct' in c) {
|
|
476
|
-
return [c.vct];
|
|
477
|
-
} else {
|
|
478
|
-
return c.credential_definition.types;
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
452
|
issuerSupportedFlowTypes(): AuthzFlowType[] {
|
|
485
453
|
return (
|
|
486
454
|
this.credentialOffer?.supportedFlows ??
|
|
@@ -488,18 +456,6 @@ export class OpenID4VCIClient {
|
|
|
488
456
|
);
|
|
489
457
|
}
|
|
490
458
|
|
|
491
|
-
isFlowTypeSupported(flowType: AuthzFlowType): boolean {
|
|
492
|
-
return this.issuerSupportedFlowTypes().includes(flowType);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
get authorizationURL(): string | undefined {
|
|
496
|
-
return this._state.authorizationURL;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
public hasAuthorizationURL(): boolean {
|
|
500
|
-
return !!this.authorizationURL;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
459
|
get credentialOffer(): CredentialOfferRequestWithBaseUrl | undefined {
|
|
504
460
|
return this._state.credentialOffer;
|
|
505
461
|
}
|
|
@@ -508,7 +464,7 @@ export class OpenID4VCIClient {
|
|
|
508
464
|
return this.credentialOffer?.version ?? OpenId4VCIVersion.VER_1_0_11;
|
|
509
465
|
}
|
|
510
466
|
|
|
511
|
-
public get endpointMetadata():
|
|
467
|
+
public get endpointMetadata(): EndpointMetadataResultV1_0_13 {
|
|
512
468
|
this.assertServerMetadata();
|
|
513
469
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
514
470
|
return this._state.endpointMetadata!;
|
|
@@ -538,10 +494,6 @@ export class OpenID4VCIClient {
|
|
|
538
494
|
return this._state.clientId;
|
|
539
495
|
}
|
|
540
496
|
|
|
541
|
-
public hasAccessTokenResponse(): boolean {
|
|
542
|
-
return !!this._state.accessTokenResponse;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
497
|
get accessTokenResponse(): AccessTokenResponse {
|
|
546
498
|
this.assertAccessToken();
|
|
547
499
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -553,45 +505,11 @@ export class OpenID4VCIClient {
|
|
|
553
505
|
return this._state.credentialIssuer;
|
|
554
506
|
}
|
|
555
507
|
|
|
556
|
-
public getAccessTokenEndpoint(): string {
|
|
557
|
-
this.assertIssuerData();
|
|
558
|
-
return this.endpointMetadata
|
|
559
|
-
? this.endpointMetadata.token_endpoint
|
|
560
|
-
: AccessTokenClient.determineTokenURL({ issuerOpts: { issuer: this.getIssuer() } });
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
public getCredentialEndpoint(): string {
|
|
564
|
-
this.assertIssuerData();
|
|
565
|
-
return this.endpointMetadata ? this.endpointMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
public hasDeferredCredentialEndpoint(): boolean {
|
|
569
|
-
return !!this.getAccessTokenEndpoint();
|
|
570
|
-
}
|
|
571
|
-
|
|
572
508
|
public getDeferredCredentialEndpoint(): string {
|
|
573
509
|
this.assertIssuerData();
|
|
574
510
|
return this.endpointMetadata ? this.endpointMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
|
|
575
511
|
}
|
|
576
512
|
|
|
577
|
-
/**
|
|
578
|
-
* Too bad we need a method like this, but EBSI is not exposing metadata
|
|
579
|
-
*/
|
|
580
|
-
public isEBSI() {
|
|
581
|
-
if (
|
|
582
|
-
this.credentialOffer?.credential_offer.credentials.find(
|
|
583
|
-
(cred) =>
|
|
584
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
585
|
-
// @ts-ignore
|
|
586
|
-
typeof cred !== 'string' && 'trust_framework' in cred && 'name' in cred.trust_framework && cred.trust_framework.name.includes('ebsi'),
|
|
587
|
-
)
|
|
588
|
-
) {
|
|
589
|
-
return true;
|
|
590
|
-
}
|
|
591
|
-
this.assertIssuerData();
|
|
592
|
-
return this.endpointMetadata.credentialIssuerMetadata?.authorization_endpoint?.includes('ebsi.eu');
|
|
593
|
-
}
|
|
594
|
-
|
|
595
513
|
private assertIssuerData(): void {
|
|
596
514
|
if (!this._state.credentialIssuer) {
|
|
597
515
|
throw Error(`No credential issuer value present`);
|