@sphereon/oid4vci-client 0.10.3 → 0.10.4-unstable.15
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 +45 -25
- 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 +5 -5
- package/dist/AuthorizationCodeClient.d.ts.map +1 -1
- package/dist/AuthorizationCodeClient.js +19 -8
- 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 +11 -8
- 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 +4 -4
- 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 +19 -90
- 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/ProofOfPossessionBuilder.js +1 -1
- package/dist/ProofOfPossessionBuilder.js.map +1 -1
- 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/functions/ProofUtil.d.ts.map +1 -1
- 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 +277 -245
- package/lib/AccessTokenClientV1_0_11.ts +255 -0
- package/lib/AuthorizationCodeClient.ts +183 -163
- package/lib/AuthorizationCodeClientV1_0_11.ts +167 -0
- package/lib/CredentialOfferClient.ts +99 -111
- package/lib/CredentialOfferClientV1_0_11.ts +112 -0
- package/lib/CredentialRequestClient.ts +212 -208
- package/lib/CredentialRequestClientBuilder.ts +156 -155
- package/lib/CredentialRequestClientBuilderV1_0_11.ts +156 -0
- package/lib/CredentialRequestClientV1_0_11.ts +190 -0
- package/lib/MetadataClient.ts +186 -208
- package/lib/MetadataClientV1_0_11.ts +186 -0
- package/lib/OpenID4VCIClient.ts +547 -629
- package/lib/OpenID4VCIClientV1_0_11.ts +644 -0
- package/lib/ProofOfPossessionBuilder.ts +204 -204
- package/lib/__tests__/AccessTokenClient.spec.ts +239 -211
- package/lib/__tests__/CredentialRequestClient.spec.ts +328 -311
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +131 -131
- package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +316 -0
- package/lib/__tests__/EBSIE2E.spec.test.ts +145 -145
- package/lib/__tests__/IT.spec.ts +382 -171
- package/lib/__tests__/IssuanceInitiation.spec.ts +64 -61
- package/lib/__tests__/IssuanceInitiationV1_0_11.spec.ts +62 -0
- package/lib/__tests__/JsonURIConversions.spec.ts +146 -146
- package/lib/__tests__/MattrE2E.spec.test.ts +104 -104
- package/lib/__tests__/MetadataClient.spec.ts +324 -260
- package/lib/__tests__/MetadataMocks.ts +483 -444
- package/lib/__tests__/OpenID4VCIClient.spec.ts +202 -202
- package/lib/__tests__/{OpenID4VCIClientPAR.spec.ts → OpenID4VCIClientPARV1_0_11.spec.ts} +122 -122
- package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +202 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +110 -110
- package/lib/__tests__/SdJwt.spec.ts +170 -163
- package/lib/__tests__/SphereonE2E.spec.test.ts +169 -169
- package/lib/__tests__/data/VciDataFixtures.ts +1382 -745
- package/lib/functions/AuthorizationUtil.ts +18 -18
- package/lib/functions/OpenIDUtils.ts +25 -0
- package/lib/functions/ProofUtil.ts +128 -128
- package/lib/index.ts +16 -9
- package/package.json +3 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthorizationDetails,
|
|
3
|
+
AuthorizationRequestOpts,
|
|
4
|
+
CodeChallengeMethod,
|
|
5
|
+
convertJsonToURI,
|
|
6
|
+
CredentialSupported,
|
|
7
|
+
CredentialOfferFormat,
|
|
8
|
+
CredentialOfferPayloadV1_0_11,
|
|
9
|
+
CredentialOfferRequestWithBaseUrl,
|
|
10
|
+
EndpointMetadataResultV1_0_11,
|
|
11
|
+
formPost,
|
|
12
|
+
JsonURIMode,
|
|
13
|
+
PARMode,
|
|
14
|
+
PKCEOpts,
|
|
15
|
+
PushedAuthorizationResponse,
|
|
16
|
+
ResponseType,
|
|
17
|
+
} from '@sphereon/oid4vci-common';
|
|
18
|
+
import Debug from 'debug';
|
|
19
|
+
|
|
20
|
+
const debug = Debug('sphereon:oid4vci');
|
|
21
|
+
|
|
22
|
+
export const createAuthorizationRequestUrlV1_0_11 = async ({
|
|
23
|
+
pkce,
|
|
24
|
+
endpointMetadata,
|
|
25
|
+
authorizationRequest,
|
|
26
|
+
credentialOffer,
|
|
27
|
+
credentialsSupported,
|
|
28
|
+
}: {
|
|
29
|
+
pkce: PKCEOpts;
|
|
30
|
+
endpointMetadata: EndpointMetadataResultV1_0_11;
|
|
31
|
+
authorizationRequest: AuthorizationRequestOpts;
|
|
32
|
+
credentialOffer?: CredentialOfferRequestWithBaseUrl;
|
|
33
|
+
credentialsSupported?: CredentialSupported[];
|
|
34
|
+
}): Promise<string> => {
|
|
35
|
+
const { redirectUri, clientId } = authorizationRequest;
|
|
36
|
+
let { scope, authorizationDetails } = authorizationRequest;
|
|
37
|
+
const parMode = endpointMetadata?.credentialIssuerMetadata?.require_pushed_authorization_requests
|
|
38
|
+
? PARMode.REQUIRE
|
|
39
|
+
: authorizationRequest.parMode ?? PARMode.AUTO;
|
|
40
|
+
// Scope and authorization_details can be used in the same authorization request
|
|
41
|
+
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param
|
|
42
|
+
if (!scope && !authorizationDetails) {
|
|
43
|
+
if (!credentialOffer) {
|
|
44
|
+
throw Error('Please provide a scope or authorization_details if no credential offer is present');
|
|
45
|
+
}
|
|
46
|
+
const creds: (CredentialOfferFormat | string)[] = (credentialOffer.credential_offer as CredentialOfferPayloadV1_0_11).credentials;
|
|
47
|
+
|
|
48
|
+
// FIXME: complains about VCT for sd-jwt
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
authorizationDetails = creds
|
|
52
|
+
.flatMap((cred) =>
|
|
53
|
+
typeof cred === 'string' && credentialsSupported ? Object.values(credentialsSupported) : (cred as CredentialSupported),
|
|
54
|
+
)
|
|
55
|
+
.filter((cred) => !!cred)
|
|
56
|
+
.map((cred) => {
|
|
57
|
+
return {
|
|
58
|
+
...cred,
|
|
59
|
+
type: 'openid_credential',
|
|
60
|
+
locations: [endpointMetadata.issuer],
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
format: cred!.format,
|
|
65
|
+
} satisfies AuthorizationDetails;
|
|
66
|
+
});
|
|
67
|
+
if (!authorizationDetails || authorizationDetails.length === 0) {
|
|
68
|
+
throw Error(`Could not create authorization details from credential offer. Please pass in explicit details`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!endpointMetadata?.authorization_endpoint) {
|
|
72
|
+
throw Error('Server metadata does not contain authorization endpoint');
|
|
73
|
+
}
|
|
74
|
+
const parEndpoint = endpointMetadata.credentialIssuerMetadata?.pushed_authorization_request_endpoint;
|
|
75
|
+
|
|
76
|
+
// add 'openid' scope if not present
|
|
77
|
+
if (!scope?.includes('openid')) {
|
|
78
|
+
scope = ['openid', scope].filter((s) => !!s).join(' ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let queryObj: { [key: string]: string } | PushedAuthorizationResponse = {
|
|
82
|
+
response_type: ResponseType.AUTH_CODE,
|
|
83
|
+
...(!pkce.disabled && {
|
|
84
|
+
code_challenge_method: pkce.codeChallengeMethod ?? CodeChallengeMethod.S256,
|
|
85
|
+
code_challenge: pkce.codeChallenge,
|
|
86
|
+
}),
|
|
87
|
+
authorization_details: JSON.stringify(handleAuthorizationDetailsV1_0_11(endpointMetadata, authorizationDetails)),
|
|
88
|
+
...(redirectUri && { redirect_uri: redirectUri }),
|
|
89
|
+
...(clientId && { client_id: clientId }),
|
|
90
|
+
...(credentialOffer?.issuerState && { issuer_state: credentialOffer.issuerState }),
|
|
91
|
+
scope,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (!parEndpoint && parMode === PARMode.REQUIRE) {
|
|
95
|
+
throw Error(`PAR mode is set to required by Authorization Server does not support PAR!`);
|
|
96
|
+
} else if (parEndpoint && parMode !== PARMode.NEVER) {
|
|
97
|
+
debug(`USING PAR with endpoint ${parEndpoint}`);
|
|
98
|
+
const parResponse = await formPost<PushedAuthorizationResponse>(
|
|
99
|
+
parEndpoint,
|
|
100
|
+
convertJsonToURI(queryObj, {
|
|
101
|
+
mode: JsonURIMode.X_FORM_WWW_URLENCODED,
|
|
102
|
+
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state'],
|
|
103
|
+
}),
|
|
104
|
+
{ contentType: 'application/x-www-form-urlencoded', accept: 'application/json' },
|
|
105
|
+
);
|
|
106
|
+
if (parResponse.errorBody || !parResponse.successBody) {
|
|
107
|
+
console.log(JSON.stringify(parResponse.errorBody));
|
|
108
|
+
console.log('Falling back to regular request URI, since PAR failed');
|
|
109
|
+
if (parMode === PARMode.REQUIRE) {
|
|
110
|
+
throw Error(`PAR error: ${parResponse.origResponse.statusText}`);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
debug(`PAR response: ${(parResponse.successBody, null, 2)}`);
|
|
114
|
+
queryObj = { request_uri: parResponse.successBody.request_uri };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
debug(`Object that will become query params: ` + JSON.stringify(queryObj, null, 2));
|
|
119
|
+
const url = convertJsonToURI(queryObj, {
|
|
120
|
+
baseUrl: endpointMetadata.authorization_endpoint,
|
|
121
|
+
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state'],
|
|
122
|
+
// arrayTypeProperties: ['authorization_details'],
|
|
123
|
+
mode: JsonURIMode.X_FORM_WWW_URLENCODED,
|
|
124
|
+
// We do not add the version here, as this always needs to be form encoded
|
|
125
|
+
});
|
|
126
|
+
debug(`Authorization Request URL: ${url}`);
|
|
127
|
+
return url;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const handleAuthorizationDetailsV1_0_11 = (
|
|
131
|
+
endpointMetadata: EndpointMetadataResultV1_0_11,
|
|
132
|
+
authorizationDetails?: AuthorizationDetails | AuthorizationDetails[],
|
|
133
|
+
): AuthorizationDetails | AuthorizationDetails[] | undefined => {
|
|
134
|
+
if (authorizationDetails) {
|
|
135
|
+
if (typeof authorizationDetails === 'string') {
|
|
136
|
+
// backwards compat for older versions of the lib
|
|
137
|
+
return authorizationDetails;
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(authorizationDetails)) {
|
|
140
|
+
return authorizationDetails
|
|
141
|
+
.filter((value) => typeof value !== 'string')
|
|
142
|
+
.map((value) => handleLocations(endpointMetadata, typeof value === 'string' ? value : { ...value }));
|
|
143
|
+
} else {
|
|
144
|
+
return handleLocations(endpointMetadata, { ...authorizationDetails });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return authorizationDetails;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const handleLocations = (endpointMetadata: EndpointMetadataResultV1_0_11, authorizationDetails: AuthorizationDetails) => {
|
|
151
|
+
if (typeof authorizationDetails === 'string') {
|
|
152
|
+
// backwards compat for older versions of the lib
|
|
153
|
+
return authorizationDetails;
|
|
154
|
+
}
|
|
155
|
+
if (authorizationDetails && (endpointMetadata.credentialIssuerMetadata?.authorization_server || endpointMetadata.authorization_endpoint)) {
|
|
156
|
+
if (authorizationDetails.locations) {
|
|
157
|
+
if (Array.isArray(authorizationDetails.locations)) {
|
|
158
|
+
authorizationDetails.locations.push(endpointMetadata.issuer);
|
|
159
|
+
} else {
|
|
160
|
+
authorizationDetails.locations = [authorizationDetails.locations as string, endpointMetadata.issuer];
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
authorizationDetails.locations = [endpointMetadata.issuer];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return authorizationDetails;
|
|
167
|
+
};
|
|
@@ -1,111 +1,99 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
requestWithBaseUrl
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
baseUrl,
|
|
101
|
-
arrayTypeProperties: isUri ? [] : ['credential_type'],
|
|
102
|
-
uriTypeProperties: isUri
|
|
103
|
-
? ['credential_offer_uri']
|
|
104
|
-
: version >= OpenId4VCIVersion.VER_1_0_11
|
|
105
|
-
? ['credential_issuer', 'credential_type']
|
|
106
|
-
: ['issuer', 'credential_type'],
|
|
107
|
-
param,
|
|
108
|
-
version,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
CredentialOfferRequestWithBaseUrl,
|
|
3
|
+
CredentialOfferV1_0_13,
|
|
4
|
+
determineSpecVersionFromURI,
|
|
5
|
+
getClientIdFromCredentialOfferPayload,
|
|
6
|
+
OpenId4VCIVersion,
|
|
7
|
+
toUniformCredentialOfferRequest,
|
|
8
|
+
} from '@sphereon/oid4vci-common';
|
|
9
|
+
import Debug from 'debug';
|
|
10
|
+
|
|
11
|
+
import { convertJsonToURI, convertURIToJsonObject } from './functions';
|
|
12
|
+
|
|
13
|
+
const debug = Debug('sphereon:oid4vci:offer');
|
|
14
|
+
|
|
15
|
+
export class CredentialOfferClient {
|
|
16
|
+
public static async fromURI(uri: string, opts?: { resolve?: boolean }): Promise<CredentialOfferRequestWithBaseUrl> {
|
|
17
|
+
debug(`Credential Offer URI: ${uri}`);
|
|
18
|
+
if (!uri.includes('?') || !uri.includes('://')) {
|
|
19
|
+
debug(`Invalid Credential Offer URI: ${uri}`);
|
|
20
|
+
throw Error(`Invalid Credential Offer Request`);
|
|
21
|
+
}
|
|
22
|
+
const scheme = uri.split('://')[0];
|
|
23
|
+
const baseUrl = uri.split('?')[0];
|
|
24
|
+
const version = determineSpecVersionFromURI(uri);
|
|
25
|
+
const credentialOffer: CredentialOfferV1_0_13 = convertURIToJsonObject(uri, {
|
|
26
|
+
arrayTypeProperties: ['credential_configuration_ids'],
|
|
27
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['credential_offer'],
|
|
28
|
+
}) as CredentialOfferV1_0_13;
|
|
29
|
+
if (credentialOffer?.credential_offer_uri === undefined && !credentialOffer?.credential_offer) {
|
|
30
|
+
throw Error('Either a credential_offer or credential_offer_uri should be present in ' + uri);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const request = await toUniformCredentialOfferRequest(credentialOffer, {
|
|
34
|
+
...opts,
|
|
35
|
+
version,
|
|
36
|
+
});
|
|
37
|
+
const clientId = getClientIdFromCredentialOfferPayload(request.credential_offer);
|
|
38
|
+
const grants = request.credential_offer?.grants;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
scheme,
|
|
42
|
+
baseUrl,
|
|
43
|
+
...(clientId && { clientId }),
|
|
44
|
+
...request,
|
|
45
|
+
...(grants?.authorization_code?.issuer_state && { issuerState: grants.authorization_code.issuer_state }),
|
|
46
|
+
...(grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] && {
|
|
47
|
+
preAuthorizedCode: grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'],
|
|
48
|
+
}),
|
|
49
|
+
...(request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.tx_code &&
|
|
50
|
+
{
|
|
51
|
+
// txCode: request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.tx_code,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public static toURI(
|
|
57
|
+
requestWithBaseUrl: CredentialOfferRequestWithBaseUrl,
|
|
58
|
+
opts?: {
|
|
59
|
+
version?: OpenId4VCIVersion;
|
|
60
|
+
},
|
|
61
|
+
): string {
|
|
62
|
+
debug(`Credential Offer Request with base URL: ${JSON.stringify(requestWithBaseUrl)}`);
|
|
63
|
+
const version = opts?.version ?? requestWithBaseUrl.version;
|
|
64
|
+
let baseUrl = requestWithBaseUrl.baseUrl.includes(requestWithBaseUrl.scheme)
|
|
65
|
+
? requestWithBaseUrl.baseUrl
|
|
66
|
+
: `${requestWithBaseUrl.scheme.replace('://', '')}://${requestWithBaseUrl.baseUrl}`;
|
|
67
|
+
let param: string | undefined;
|
|
68
|
+
|
|
69
|
+
const isUri = requestWithBaseUrl.credential_offer_uri !== undefined;
|
|
70
|
+
|
|
71
|
+
if (version.valueOf() >= OpenId4VCIVersion.VER_1_0_11.valueOf()) {
|
|
72
|
+
// v11 changed from encoding every param to a encoded json object with a credential_offer param key
|
|
73
|
+
if (!baseUrl.includes('?')) {
|
|
74
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
75
|
+
} else {
|
|
76
|
+
const split = baseUrl.split('?');
|
|
77
|
+
if (split.length > 1 && split[1] !== '') {
|
|
78
|
+
if (baseUrl.endsWith('&')) {
|
|
79
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
80
|
+
} else if (!baseUrl.endsWith('=')) {
|
|
81
|
+
baseUrl += `&`;
|
|
82
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return convertJsonToURI(requestWithBaseUrl.credential_offer_uri ?? requestWithBaseUrl.original_credential_offer, {
|
|
88
|
+
baseUrl,
|
|
89
|
+
arrayTypeProperties: isUri ? [] : ['credential_type'],
|
|
90
|
+
uriTypeProperties: isUri
|
|
91
|
+
? ['credential_offer_uri']
|
|
92
|
+
: version >= OpenId4VCIVersion.VER_1_0_13
|
|
93
|
+
? ['credential_issuer', 'credential_type']
|
|
94
|
+
: ['issuer', 'credential_type'],
|
|
95
|
+
param,
|
|
96
|
+
version,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialOffer,
|
|
3
|
+
CredentialOfferPayload,
|
|
4
|
+
CredentialOfferPayloadV1_0_09,
|
|
5
|
+
CredentialOfferRequestWithBaseUrl,
|
|
6
|
+
CredentialOfferRequestWithBaseUrlV1_0_11,
|
|
7
|
+
CredentialOfferV1_0_11,
|
|
8
|
+
determineSpecVersionFromURI,
|
|
9
|
+
getClientIdFromCredentialOfferPayload,
|
|
10
|
+
OpenId4VCIVersion,
|
|
11
|
+
toUniformCredentialOfferRequestV1_0_11,
|
|
12
|
+
} from '@sphereon/oid4vci-common';
|
|
13
|
+
import Debug from 'debug';
|
|
14
|
+
|
|
15
|
+
import { convertJsonToURI, convertURIToJsonObject } from './functions';
|
|
16
|
+
|
|
17
|
+
const debug = Debug('sphereon:oid4vci:offer');
|
|
18
|
+
|
|
19
|
+
export class CredentialOfferClientV1_0_11 {
|
|
20
|
+
public static async fromURI(uri: string, opts?: { resolve?: boolean }): Promise<CredentialOfferRequestWithBaseUrlV1_0_11> {
|
|
21
|
+
debug(`Credential Offer URI: ${uri}`);
|
|
22
|
+
if (!uri.includes('?') || !uri.includes('://')) {
|
|
23
|
+
debug(`Invalid Credential Offer URI: ${uri}`);
|
|
24
|
+
throw Error(`Invalid Credential Offer Request`);
|
|
25
|
+
}
|
|
26
|
+
const scheme = uri.split('://')[0];
|
|
27
|
+
const baseUrl = uri.split('?')[0];
|
|
28
|
+
const version = determineSpecVersionFromURI(uri);
|
|
29
|
+
let credentialOffer: CredentialOffer;
|
|
30
|
+
let credentialOfferPayload: CredentialOfferPayload;
|
|
31
|
+
if (version < OpenId4VCIVersion.VER_1_0_11) {
|
|
32
|
+
credentialOfferPayload = convertURIToJsonObject(uri, {
|
|
33
|
+
arrayTypeProperties: ['credential_type'],
|
|
34
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['issuer', 'credential_type'],
|
|
35
|
+
}) as CredentialOfferPayloadV1_0_09;
|
|
36
|
+
credentialOffer = {
|
|
37
|
+
credential_offer: credentialOfferPayload,
|
|
38
|
+
};
|
|
39
|
+
} else {
|
|
40
|
+
credentialOffer = convertURIToJsonObject(uri, {
|
|
41
|
+
arrayTypeProperties: ['credentials'],
|
|
42
|
+
requiredProperties: uri.includes('credential_offer_uri=') ? ['credential_offer_uri'] : ['credential_offer'],
|
|
43
|
+
}) as CredentialOfferV1_0_11;
|
|
44
|
+
if (credentialOffer?.credential_offer_uri === undefined && !credentialOffer?.credential_offer) {
|
|
45
|
+
throw Error('Either a credential_offer or credential_offer_uri should be present in ' + uri);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const request = await toUniformCredentialOfferRequestV1_0_11(credentialOffer, {
|
|
50
|
+
...opts,
|
|
51
|
+
version,
|
|
52
|
+
});
|
|
53
|
+
const clientId = getClientIdFromCredentialOfferPayload(request.credential_offer);
|
|
54
|
+
const grants = request.credential_offer?.grants;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
scheme,
|
|
58
|
+
baseUrl,
|
|
59
|
+
clientId,
|
|
60
|
+
...request,
|
|
61
|
+
...(grants?.authorization_code?.issuer_state && { issuerState: grants.authorization_code.issuer_state }),
|
|
62
|
+
...(grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] && {
|
|
63
|
+
preAuthorizedCode: grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code'],
|
|
64
|
+
}),
|
|
65
|
+
userPinRequired: request.credential_offer?.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.user_pin_required ?? false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static toURI(
|
|
70
|
+
requestWithBaseUrl: CredentialOfferRequestWithBaseUrl,
|
|
71
|
+
opts?: {
|
|
72
|
+
version?: OpenId4VCIVersion;
|
|
73
|
+
},
|
|
74
|
+
): string {
|
|
75
|
+
debug(`Credential Offer Request with base URL: ${JSON.stringify(requestWithBaseUrl)}`);
|
|
76
|
+
const version = opts?.version ?? requestWithBaseUrl.version;
|
|
77
|
+
let baseUrl = requestWithBaseUrl.baseUrl.includes(requestWithBaseUrl.scheme)
|
|
78
|
+
? requestWithBaseUrl.baseUrl
|
|
79
|
+
: `${requestWithBaseUrl.scheme.replace('://', '')}://${requestWithBaseUrl.baseUrl}`;
|
|
80
|
+
let param: string | undefined;
|
|
81
|
+
|
|
82
|
+
const isUri = requestWithBaseUrl.credential_offer_uri !== undefined;
|
|
83
|
+
|
|
84
|
+
if (version.valueOf() >= OpenId4VCIVersion.VER_1_0_11.valueOf()) {
|
|
85
|
+
// v11 changed from encoding every param to a encoded json object with a credential_offer param key
|
|
86
|
+
if (!baseUrl.includes('?')) {
|
|
87
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
88
|
+
} else {
|
|
89
|
+
const split = baseUrl.split('?');
|
|
90
|
+
if (split.length > 1 && split[1] !== '') {
|
|
91
|
+
if (baseUrl.endsWith('&')) {
|
|
92
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
93
|
+
} else if (!baseUrl.endsWith('=')) {
|
|
94
|
+
baseUrl += `&`;
|
|
95
|
+
param = isUri ? 'credential_offer_uri' : 'credential_offer';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return convertJsonToURI(requestWithBaseUrl.credential_offer_uri ?? requestWithBaseUrl.original_credential_offer, {
|
|
101
|
+
baseUrl,
|
|
102
|
+
arrayTypeProperties: isUri ? [] : ['credential_type'],
|
|
103
|
+
uriTypeProperties: isUri
|
|
104
|
+
? ['credential_offer_uri']
|
|
105
|
+
: version >= OpenId4VCIVersion.VER_1_0_11
|
|
106
|
+
? ['credential_issuer', 'credential_type']
|
|
107
|
+
: ['issuer', 'credential_type'],
|
|
108
|
+
param,
|
|
109
|
+
version,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|