@sphereon/oid4vci-client 0.8.2-next.6 → 0.8.2-next.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -8
- package/dist/AccessTokenClient.d.ts +0 -1
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +11 -18
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AuthorizationCodeClient.d.ts +9 -0
- package/dist/AuthorizationCodeClient.d.ts.map +1 -0
- package/dist/AuthorizationCodeClient.js +132 -0
- package/dist/AuthorizationCodeClient.js.map +1 -0
- package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -1
- package/dist/AuthorizationDetailsBuilder.js.map +1 -1
- package/dist/CredentialOfferClient.d.ts.map +1 -1
- package/dist/CredentialOfferClient.js +3 -1
- package/dist/CredentialOfferClient.js.map +1 -1
- package/dist/CredentialRequestClient.d.ts +15 -0
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +91 -43
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/CredentialRequestClientBuilder.d.ts +19 -7
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js +31 -1
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/MetadataClient.d.ts.map +1 -1
- package/dist/MetadataClient.js +12 -1
- package/dist/MetadataClient.js.map +1 -1
- package/dist/OpenID4VCIClient.d.ts +62 -27
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +255 -176
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/ProofOfPossessionBuilder.d.ts +3 -1
- package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
- package/dist/ProofOfPossessionBuilder.js +5 -0
- package/dist/ProofOfPossessionBuilder.js.map +1 -1
- package/dist/functions/AuthorizationUtil.d.ts +3 -0
- package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
- package/dist/functions/AuthorizationUtil.js +22 -0
- package/dist/functions/AuthorizationUtil.js.map +1 -0
- package/dist/functions/ProofUtil.d.ts +2 -1
- package/dist/functions/ProofUtil.d.ts.map +1 -1
- package/dist/functions/ProofUtil.js +6 -4
- package/dist/functions/ProofUtil.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/lib/AccessTokenClient.ts +16 -20
- package/lib/AuthorizationCodeClient.ts +163 -0
- package/lib/AuthorizationDetailsBuilder.ts +2 -2
- package/lib/CredentialOfferClient.ts +4 -1
- package/lib/CredentialRequestClient.ts +116 -45
- package/lib/CredentialRequestClientBuilder.ts +53 -8
- package/lib/MetadataClient.ts +13 -1
- package/lib/OpenID4VCIClient.ts +348 -216
- package/lib/ProofOfPossessionBuilder.ts +8 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
- package/lib/__tests__/CredentialRequestClient.spec.ts +28 -8
- package/lib/__tests__/EBSIE2E.spec.test.ts +145 -0
- package/lib/__tests__/MetadataClient.spec.ts +4 -1
- package/lib/__tests__/OpenID4VCIClient.spec.ts +117 -76
- package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
- package/lib/__tests__/SdJwt.spec.ts +163 -0
- package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
- package/lib/__tests__/data/VciDataFixtures.ts +14 -13
- package/lib/functions/AuthorizationUtil.ts +18 -0
- package/lib/functions/ProofUtil.ts +18 -4
- package/lib/index.ts +1 -0
- package/lib/types/index.ts +0 -0
- package/package.json +8 -6
package/dist/OpenID4VCIClient.js
CHANGED
|
@@ -14,217 +14,218 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.OpenID4VCIClient = void 0;
|
|
16
16
|
const oid4vci_common_1 = require("@sphereon/oid4vci-common");
|
|
17
|
-
const IssuerMetadataUtils_1 = require("@sphereon/oid4vci-common/dist/functions/IssuerMetadataUtils");
|
|
18
17
|
const debug_1 = __importDefault(require("debug"));
|
|
19
18
|
const AccessTokenClient_1 = require("./AccessTokenClient");
|
|
19
|
+
const AuthorizationCodeClient_1 = require("./AuthorizationCodeClient");
|
|
20
20
|
const CredentialOfferClient_1 = require("./CredentialOfferClient");
|
|
21
21
|
const CredentialRequestClientBuilder_1 = require("./CredentialRequestClientBuilder");
|
|
22
22
|
const MetadataClient_1 = require("./MetadataClient");
|
|
23
23
|
const ProofOfPossessionBuilder_1 = require("./ProofOfPossessionBuilder");
|
|
24
|
-
const
|
|
24
|
+
const AuthorizationUtil_1 = require("./functions/AuthorizationUtil");
|
|
25
25
|
const debug = (0, debug_1.default)('sphereon:oid4vci');
|
|
26
26
|
class OpenID4VCIClient {
|
|
27
|
-
constructor(credentialOffer, kid, alg,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
constructor({ credentialOffer, clientId, kid, alg, credentialIssuer, pkce, authorizationRequest, jwk, endpointMetadata, accessTokenResponse, authorizationRequestOpts, authorizationURL, }) {
|
|
28
|
+
var _a;
|
|
29
|
+
const issuer = credentialIssuer !== null && credentialIssuer !== void 0 ? credentialIssuer : (credentialOffer ? (0, oid4vci_common_1.getIssuerFromCredentialOfferPayload)(credentialOffer.credential_offer) : undefined);
|
|
30
|
+
if (!issuer) {
|
|
31
|
+
throw Error('No credential issuer supplied or deduced from offer');
|
|
32
|
+
}
|
|
33
|
+
this._state = {
|
|
34
|
+
credentialOffer,
|
|
35
|
+
credentialIssuer: issuer,
|
|
36
|
+
kid,
|
|
37
|
+
alg,
|
|
38
|
+
// TODO: We need to refactor this and always explicitly call createAuthorizationRequestUrl, so we can have a credential selection first and use the kid as a default for the client id
|
|
39
|
+
clientId: (_a = clientId !== null && clientId !== void 0 ? clientId : (credentialOffer && (0, oid4vci_common_1.getClientIdFromCredentialOfferPayload)(credentialOffer.credential_offer))) !== null && _a !== void 0 ? _a : kid === null || kid === void 0 ? void 0 : kid.split('#')[0],
|
|
40
|
+
pkce: Object.assign({ disabled: false, codeChallengeMethod: oid4vci_common_1.CodeChallengeMethod.S256 }, pkce),
|
|
41
|
+
authorizationRequestOpts,
|
|
42
|
+
jwk,
|
|
43
|
+
endpointMetadata,
|
|
44
|
+
accessTokenResponse,
|
|
45
|
+
authorizationURL,
|
|
46
|
+
};
|
|
47
|
+
// Running syncAuthorizationRequestOpts later as it is using the state
|
|
48
|
+
if (!this._state.authorizationRequestOpts) {
|
|
49
|
+
this._state.authorizationRequestOpts = this.syncAuthorizationRequestOpts(authorizationRequest);
|
|
50
|
+
}
|
|
51
|
+
debug(`Authorization req options: ${JSON.stringify(this._state.authorizationRequestOpts, null, 2)}`);
|
|
32
52
|
}
|
|
33
|
-
static
|
|
53
|
+
static fromCredentialIssuer({ kid, alg, retrieveServerMetadata, clientId, credentialIssuer, pkce, authorizationRequest, createAuthorizationRequestURL, }) {
|
|
34
54
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
const client = new OpenID4VCIClient(
|
|
55
|
+
const client = new OpenID4VCIClient({
|
|
56
|
+
kid,
|
|
57
|
+
alg,
|
|
58
|
+
clientId: clientId !== null && clientId !== void 0 ? clientId : authorizationRequest === null || authorizationRequest === void 0 ? void 0 : authorizationRequest.clientId,
|
|
59
|
+
credentialIssuer,
|
|
60
|
+
pkce,
|
|
61
|
+
authorizationRequest,
|
|
62
|
+
});
|
|
36
63
|
if (retrieveServerMetadata === undefined || retrieveServerMetadata) {
|
|
37
64
|
yield client.retrieveServerMetadata();
|
|
38
65
|
}
|
|
66
|
+
if (createAuthorizationRequestURL === undefined || createAuthorizationRequestURL) {
|
|
67
|
+
yield client.createAuthorizationRequestUrl({ authorizationRequest, pkce });
|
|
68
|
+
}
|
|
39
69
|
return client;
|
|
40
70
|
});
|
|
41
71
|
}
|
|
42
|
-
|
|
72
|
+
static fromState({ state }) {
|
|
43
73
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this._endpointMetadata = yield MetadataClient_1.MetadataClient.retrieveAllMetadataFromCredentialOffer(this.credentialOffer);
|
|
47
|
-
}
|
|
48
|
-
return this.endpointMetadata;
|
|
74
|
+
const clientState = typeof state === 'string' ? JSON.parse(state) : state;
|
|
75
|
+
return new OpenID4VCIClient(clientState);
|
|
49
76
|
});
|
|
50
77
|
}
|
|
51
|
-
|
|
78
|
+
static fromURI({ uri, kid, alg, retrieveServerMetadata, clientId, pkce, createAuthorizationRequestURL, authorizationRequest, resolveOfferUri, }) {
|
|
52
79
|
var _a;
|
|
53
|
-
// Scope and authorization_details can be used in the same authorization request
|
|
54
|
-
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param
|
|
55
|
-
if (!scope && !authorizationDetails) {
|
|
56
|
-
throw Error('Please provide a scope or authorization_details');
|
|
57
|
-
}
|
|
58
|
-
// todo: Probably can go with current logic in MetadataClient who will always set the authorization_endpoint when found
|
|
59
|
-
// handling this because of the support for v1_0-08
|
|
60
|
-
if (this._endpointMetadata &&
|
|
61
|
-
this._endpointMetadata.credentialIssuerMetadata &&
|
|
62
|
-
'authorization_endpoint' in this._endpointMetadata.credentialIssuerMetadata) {
|
|
63
|
-
this._endpointMetadata.authorization_endpoint = this._endpointMetadata.credentialIssuerMetadata.authorization_endpoint;
|
|
64
|
-
}
|
|
65
|
-
if (!((_a = this._endpointMetadata) === null || _a === void 0 ? void 0 : _a.authorization_endpoint)) {
|
|
66
|
-
throw Error('Server metadata does not contain authorization endpoint');
|
|
67
|
-
}
|
|
68
|
-
// add 'openid' scope if not present
|
|
69
|
-
if (!(scope === null || scope === void 0 ? void 0 : scope.includes('openid'))) {
|
|
70
|
-
scope = ['openid', scope].filter((s) => !!s).join(' ');
|
|
71
|
-
}
|
|
72
|
-
const queryObj = {
|
|
73
|
-
response_type: oid4vci_common_1.ResponseType.AUTH_CODE,
|
|
74
|
-
code_challenge_method: codeChallengeMethod,
|
|
75
|
-
code_challenge: codeChallenge,
|
|
76
|
-
authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)),
|
|
77
|
-
redirect_uri: redirectUri,
|
|
78
|
-
scope: scope,
|
|
79
|
-
};
|
|
80
|
-
if (this.clientId) {
|
|
81
|
-
queryObj['client_id'] = this.clientId;
|
|
82
|
-
}
|
|
83
|
-
if (this.credentialOffer.issuerState) {
|
|
84
|
-
queryObj['issuer_state'] = this.credentialOffer.issuerState;
|
|
85
|
-
}
|
|
86
|
-
return (0, functions_1.convertJsonToURI)(queryObj, {
|
|
87
|
-
baseUrl: this._endpointMetadata.authorization_endpoint,
|
|
88
|
-
uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details', 'issuer_state'],
|
|
89
|
-
mode: oid4vci_common_1.JsonURIMode.X_FORM_WWW_URLENCODED,
|
|
90
|
-
// We do not add the version here, as this always needs to be form encoded
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
acquirePushedAuthorizationRequestURI({ codeChallengeMethod, codeChallenge, authorizationDetails, redirectUri, scope, }) {
|
|
94
|
-
var _a, _b;
|
|
95
80
|
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
typeof this._endpointMetadata.credentialIssuerMetadata.pushed_authorization_request_endpoint !== 'string') {
|
|
108
|
-
throw Error('Server metadata does not contain pushed authorization request endpoint');
|
|
109
|
-
}
|
|
110
|
-
const parEndpoint = this._endpointMetadata.credentialIssuerMetadata.pushed_authorization_request_endpoint;
|
|
111
|
-
// add 'openid' scope if not present
|
|
112
|
-
if (!(scope === null || scope === void 0 ? void 0 : scope.includes('openid'))) {
|
|
113
|
-
scope = ['openid', scope].filter((s) => !!s).join(' ');
|
|
114
|
-
}
|
|
115
|
-
const queryObj = {
|
|
116
|
-
response_type: oid4vci_common_1.ResponseType.AUTH_CODE,
|
|
117
|
-
code_challenge_method: codeChallengeMethod,
|
|
118
|
-
code_challenge: codeChallenge,
|
|
119
|
-
authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)),
|
|
120
|
-
redirect_uri: redirectUri,
|
|
121
|
-
scope: scope,
|
|
122
|
-
};
|
|
123
|
-
if (this.clientId) {
|
|
124
|
-
queryObj['client_id'] = this.clientId;
|
|
81
|
+
const credentialOfferClient = yield CredentialOfferClient_1.CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri });
|
|
82
|
+
const client = new OpenID4VCIClient({
|
|
83
|
+
credentialOffer: credentialOfferClient,
|
|
84
|
+
kid,
|
|
85
|
+
alg,
|
|
86
|
+
clientId: (_a = clientId !== null && clientId !== void 0 ? clientId : authorizationRequest === null || authorizationRequest === void 0 ? void 0 : authorizationRequest.clientId) !== null && _a !== void 0 ? _a : credentialOfferClient.clientId,
|
|
87
|
+
pkce,
|
|
88
|
+
authorizationRequest,
|
|
89
|
+
});
|
|
90
|
+
if (retrieveServerMetadata === undefined || retrieveServerMetadata) {
|
|
91
|
+
yield client.retrieveServerMetadata();
|
|
125
92
|
}
|
|
126
|
-
if (
|
|
127
|
-
|
|
93
|
+
if (credentialOfferClient.supportedFlows.includes(oid4vci_common_1.AuthzFlowType.AUTHORIZATION_CODE_FLOW) &&
|
|
94
|
+
(createAuthorizationRequestURL === undefined || createAuthorizationRequestURL)) {
|
|
95
|
+
yield client.createAuthorizationRequestUrl({ authorizationRequest, pkce });
|
|
96
|
+
debug(`Authorization Request URL: ${client._state.authorizationURL}`);
|
|
128
97
|
}
|
|
129
|
-
|
|
130
|
-
return (0, functions_1.convertJsonToURI)({ request_uri: (_b = response.successBody) === null || _b === void 0 ? void 0 : _b.request_uri }, {
|
|
131
|
-
baseUrl: this._endpointMetadata.credentialIssuerMetadata.authorization_endpoint,
|
|
132
|
-
uriTypeProperties: ['request_uri'],
|
|
133
|
-
mode: oid4vci_common_1.JsonURIMode.X_FORM_WWW_URLENCODED,
|
|
134
|
-
});
|
|
98
|
+
return client;
|
|
135
99
|
});
|
|
136
100
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Allows you to create an Authorization Request URL when using an Authorization Code flow. This URL needs to be accessed using the front channel (browser)
|
|
103
|
+
*
|
|
104
|
+
* The Identity provider would present a login screen typically; after you authenticated, it would redirect to the provided redirectUri; which can be same device or cross-device
|
|
105
|
+
* @param opts
|
|
106
|
+
*/
|
|
107
|
+
createAuthorizationRequestUrl(opts) {
|
|
108
|
+
var _a;
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
if (!this._state.authorizationURL) {
|
|
111
|
+
this.calculatePKCEOpts(opts === null || opts === void 0 ? void 0 : opts.pkce);
|
|
112
|
+
this._state.authorizationRequestOpts = this.syncAuthorizationRequestOpts(opts === null || opts === void 0 ? void 0 : opts.authorizationRequest);
|
|
113
|
+
if (!this._state.authorizationRequestOpts) {
|
|
114
|
+
throw Error(`No Authorization Request options present or provided in this call`);
|
|
115
|
+
}
|
|
116
|
+
// todo: Probably can go with current logic in MetadataClient who will always set the authorization_endpoint when found
|
|
117
|
+
// handling this because of the support for v1_0-08
|
|
118
|
+
if (((_a = this._state.endpointMetadata) === null || _a === void 0 ? void 0 : _a.credentialIssuerMetadata) &&
|
|
119
|
+
'authorization_endpoint' in this._state.endpointMetadata.credentialIssuerMetadata) {
|
|
120
|
+
this._state.endpointMetadata.authorization_endpoint = this._state.endpointMetadata.credentialIssuerMetadata.authorization_endpoint;
|
|
121
|
+
}
|
|
122
|
+
this._state.authorizationURL = yield (0, AuthorizationCodeClient_1.createAuthorizationRequestUrl)({
|
|
123
|
+
pkce: this._state.pkce,
|
|
124
|
+
endpointMetadata: this.endpointMetadata,
|
|
125
|
+
authorizationRequest: this._state.authorizationRequestOpts,
|
|
126
|
+
credentialOffer: this.credentialOffer,
|
|
127
|
+
credentialsSupported: this.getCredentialsSupported(true),
|
|
128
|
+
});
|
|
144
129
|
}
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
return this._state.authorizationURL;
|
|
131
|
+
});
|
|
147
132
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
retrieveServerMetadata() {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
this.assertIssuerData();
|
|
136
|
+
if (!this._state.endpointMetadata) {
|
|
137
|
+
if (this.credentialOffer) {
|
|
138
|
+
this._state.endpointMetadata = yield MetadataClient_1.MetadataClient.retrieveAllMetadataFromCredentialOffer(this.credentialOffer);
|
|
139
|
+
}
|
|
140
|
+
else if (this._state.credentialIssuer) {
|
|
141
|
+
this._state.endpointMetadata = yield MetadataClient_1.MetadataClient.retrieveAllMetadata(this._state.credentialIssuer);
|
|
155
142
|
}
|
|
156
143
|
else {
|
|
157
|
-
|
|
144
|
+
throw Error(`Cannot retrieve issuer metadata without either a credential offer, or issuer value`);
|
|
158
145
|
}
|
|
159
146
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
147
|
+
return this.endpointMetadata;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
calculatePKCEOpts(pkce) {
|
|
151
|
+
this._state.pkce = (0, AuthorizationUtil_1.generateMissingPKCEOpts)(Object.assign(Object.assign({}, this._state.pkce), pkce));
|
|
165
152
|
}
|
|
166
153
|
acquireAccessToken(opts) {
|
|
167
|
-
var _a, _b;
|
|
154
|
+
var _a, _b, _c, _d, _e, _f;
|
|
168
155
|
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
-
const { pin, clientId
|
|
156
|
+
const { pin, clientId } = opts !== null && opts !== void 0 ? opts : {};
|
|
157
|
+
let { redirectUri } = opts !== null && opts !== void 0 ? opts : {};
|
|
158
|
+
const code = (_a = opts === null || opts === void 0 ? void 0 : opts.code) !== null && _a !== void 0 ? _a : ((opts === null || opts === void 0 ? void 0 : opts.authorizationResponse) ? (0, oid4vci_common_1.toAuthorizationResponsePayload)(opts.authorizationResponse).code : undefined);
|
|
159
|
+
if (opts === null || opts === void 0 ? void 0 : opts.codeVerifier) {
|
|
160
|
+
this._state.pkce.codeVerifier = opts.codeVerifier;
|
|
161
|
+
}
|
|
170
162
|
this.assertIssuerData();
|
|
171
163
|
if (clientId) {
|
|
172
|
-
this.
|
|
164
|
+
this._state.clientId = clientId;
|
|
173
165
|
}
|
|
174
|
-
if (!this.
|
|
166
|
+
if (!this._state.accessTokenResponse) {
|
|
175
167
|
const accessTokenClient = new AccessTokenClient_1.AccessTokenClient();
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
asOpts: { clientId }
|
|
184
|
-
});
|
|
168
|
+
if (redirectUri && redirectUri !== ((_b = this._state.authorizationRequestOpts) === null || _b === void 0 ? void 0 : _b.redirectUri)) {
|
|
169
|
+
console.log(`Redirect URI mismatch between access-token (${redirectUri}) and authorization request (${(_c = this._state.authorizationRequestOpts) === null || _c === void 0 ? void 0 : _c.redirectUri}). According to the specification that is not allowed.`);
|
|
170
|
+
}
|
|
171
|
+
if (((_d = this._state.authorizationRequestOpts) === null || _d === void 0 ? void 0 : _d.redirectUri) && !redirectUri) {
|
|
172
|
+
redirectUri = this._state.authorizationRequestOpts.redirectUri;
|
|
173
|
+
}
|
|
174
|
+
const response = yield accessTokenClient.acquireAccessToken(Object.assign(Object.assign({ credentialOffer: this.credentialOffer, metadata: this.endpointMetadata, credentialIssuer: this.getIssuer(), pin }, (!this._state.pkce.disabled && { codeVerifier: this._state.pkce.codeVerifier })), { code,
|
|
175
|
+
redirectUri, asOpts: { clientId: this.clientId } }));
|
|
185
176
|
if (response.errorBody) {
|
|
186
177
|
debug(`Access token error:\r\n${response.errorBody}`);
|
|
187
|
-
throw Error(`Retrieving an access token from ${(
|
|
178
|
+
throw Error(`Retrieving an access token from ${(_e = this._state.endpointMetadata) === null || _e === void 0 ? void 0 : _e.token_endpoint} for issuer ${this.getIssuer()} failed with status: ${response.origResponse.status}`);
|
|
188
179
|
}
|
|
189
180
|
else if (!response.successBody) {
|
|
190
181
|
debug(`Access token error. No success body`);
|
|
191
|
-
throw Error(`Retrieving an access token from ${(
|
|
182
|
+
throw Error(`Retrieving an access token from ${(_f = this._state.endpointMetadata) === null || _f === void 0 ? void 0 : _f.token_endpoint} for issuer ${this.getIssuer()} failed as there was no success response body`);
|
|
192
183
|
}
|
|
193
|
-
this.
|
|
184
|
+
this._state.accessTokenResponse = response.successBody;
|
|
194
185
|
}
|
|
195
186
|
return this.accessTokenResponse;
|
|
196
187
|
});
|
|
197
188
|
}
|
|
198
|
-
acquireCredentials({ credentialTypes, proofCallbacks, format, kid, alg, jti, }) {
|
|
189
|
+
acquireCredentials({ credentialTypes, context, proofCallbacks, format, kid, jwk, alg, jti, deferredCredentialAwait, deferredCredentialIntervalInMS, }) {
|
|
199
190
|
var _a, _b, _c;
|
|
200
191
|
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
-
if (
|
|
202
|
-
|
|
192
|
+
if ([jwk, kid].filter((v) => v !== undefined).length > 1) {
|
|
193
|
+
throw new Error(oid4vci_common_1.KID_JWK_X5C_ERROR + `. jwk: ${jwk !== undefined}, kid: ${kid !== undefined}`);
|
|
203
194
|
}
|
|
204
|
-
if (
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
195
|
+
if (alg)
|
|
196
|
+
this._state.alg = alg;
|
|
197
|
+
if (jwk)
|
|
198
|
+
this._state.jwk = jwk;
|
|
199
|
+
if (kid)
|
|
200
|
+
this._state.kid = kid;
|
|
201
|
+
const requestBuilder = this.credentialOffer
|
|
202
|
+
? CredentialRequestClientBuilder_1.CredentialRequestClientBuilder.fromCredentialOffer({
|
|
203
|
+
credentialOffer: this.credentialOffer,
|
|
204
|
+
metadata: this.endpointMetadata,
|
|
205
|
+
})
|
|
206
|
+
: CredentialRequestClientBuilder_1.CredentialRequestClientBuilder.fromCredentialIssuer({
|
|
207
|
+
credentialIssuer: this.getIssuer(),
|
|
208
|
+
credentialTypes,
|
|
209
|
+
metadata: this.endpointMetadata,
|
|
210
|
+
version: this.version(),
|
|
211
|
+
});
|
|
211
212
|
requestBuilder.withTokenFromResponse(this.accessTokenResponse);
|
|
213
|
+
requestBuilder.withDeferredCredentialAwait(deferredCredentialAwait !== null && deferredCredentialAwait !== void 0 ? deferredCredentialAwait : false, deferredCredentialIntervalInMS);
|
|
212
214
|
if ((_a = this.endpointMetadata) === null || _a === void 0 ? void 0 : _a.credentialIssuerMetadata) {
|
|
213
215
|
const metadata = this.endpointMetadata.credentialIssuerMetadata;
|
|
214
|
-
const types = Array.isArray(credentialTypes) ? credentialTypes
|
|
216
|
+
const types = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
|
|
215
217
|
if (metadata.credentials_supported && Array.isArray(metadata.credentials_supported)) {
|
|
216
218
|
let typeSupported = false;
|
|
217
219
|
metadata.credentials_supported.forEach((supportedCredential) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (supportedCredential.types.sort().every((t, i) => types[i] === t) ||
|
|
222
|
-
(types.length === 1 && (types[0] === supportedCredential.id || supportedCredential.types.includes(types[0])))) {
|
|
220
|
+
const subTypes = (0, oid4vci_common_1.getTypesFromCredentialSupported)(supportedCredential);
|
|
221
|
+
if (subTypes.every((t, i) => types[i] === t) ||
|
|
222
|
+
(types.length === 1 && (types[0] === supportedCredential.id || subTypes.includes(types[0])))) {
|
|
223
223
|
typeSupported = true;
|
|
224
224
|
}
|
|
225
225
|
});
|
|
226
226
|
if (!typeSupported) {
|
|
227
|
-
|
|
227
|
+
console.log(`Not all credential types ${JSON.stringify(credentialTypes)} are present in metadata for ${this.getIssuer()}`);
|
|
228
|
+
// throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`);
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
else if (metadata.credentials_supported && !Array.isArray(metadata.credentials_supported)) {
|
|
@@ -242,8 +243,13 @@ class OpenID4VCIClient {
|
|
|
242
243
|
version: this.version(),
|
|
243
244
|
})
|
|
244
245
|
.withIssuer(this.getIssuer())
|
|
245
|
-
.withAlg(this.alg)
|
|
246
|
-
|
|
246
|
+
.withAlg(this.alg);
|
|
247
|
+
if (this._state.jwk) {
|
|
248
|
+
proofBuilder.withJWK(this._state.jwk);
|
|
249
|
+
}
|
|
250
|
+
if (this._state.kid) {
|
|
251
|
+
proofBuilder.withKid(this._state.kid);
|
|
252
|
+
}
|
|
247
253
|
if (this.clientId) {
|
|
248
254
|
proofBuilder.withClientId(this.clientId);
|
|
249
255
|
}
|
|
@@ -252,26 +258,32 @@ class OpenID4VCIClient {
|
|
|
252
258
|
}
|
|
253
259
|
const response = yield credentialRequestClient.acquireCredentialsUsingProof({
|
|
254
260
|
proofInput: proofBuilder,
|
|
255
|
-
credentialTypes
|
|
261
|
+
credentialTypes,
|
|
262
|
+
context,
|
|
256
263
|
format,
|
|
257
264
|
});
|
|
258
265
|
if (response.errorBody) {
|
|
259
|
-
debug(`Credential request error:\r\n${response.errorBody}`);
|
|
260
|
-
throw Error(`Retrieving a credential from ${(_b = this.
|
|
266
|
+
debug(`Credential request error:\r\n${JSON.stringify(response.errorBody)}`);
|
|
267
|
+
throw Error(`Retrieving a credential from ${(_b = this._state.endpointMetadata) === null || _b === void 0 ? void 0 : _b.credential_endpoint} for issuer ${this.getIssuer()} failed with status: ${response.origResponse.status}`);
|
|
261
268
|
}
|
|
262
269
|
else if (!response.successBody) {
|
|
263
270
|
debug(`Credential request error. No success body`);
|
|
264
|
-
throw Error(`Retrieving a credential from ${(_c = this.
|
|
271
|
+
throw Error(`Retrieving a credential from ${(_c = this._state.endpointMetadata) === null || _c === void 0 ? void 0 : _c.credential_endpoint} for issuer ${this.getIssuer()} failed as there was no success response body`);
|
|
265
272
|
}
|
|
266
273
|
return response.successBody;
|
|
267
274
|
});
|
|
268
275
|
}
|
|
276
|
+
exportState() {
|
|
277
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
return JSON.stringify(this._state);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
269
281
|
// FIXME: We really should convert <v11 to v12 objects first. Right now the logic doesn't map nicely and is brittle.
|
|
270
282
|
// We should resolve IDs to objects first in case of strings.
|
|
271
283
|
// When < v11 convert into a v12 object. When v12 object retain it.
|
|
272
284
|
// Then match the object array on server metadata
|
|
273
285
|
getCredentialsSupported(restrictToInitiationTypes, format) {
|
|
274
|
-
return (0,
|
|
286
|
+
return (0, oid4vci_common_1.getSupportedCredentials)({
|
|
275
287
|
issuerMetadata: this.endpointMetadata.credentialIssuerMetadata,
|
|
276
288
|
version: this.version(),
|
|
277
289
|
format: format,
|
|
@@ -279,7 +291,10 @@ class OpenID4VCIClient {
|
|
|
279
291
|
});
|
|
280
292
|
}
|
|
281
293
|
getCredentialOfferTypes() {
|
|
282
|
-
if (this.credentialOffer
|
|
294
|
+
if (!this.credentialOffer) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
else if (this.credentialOffer.version < oid4vci_common_1.OpenId4VCIVersion.VER_1_0_11) {
|
|
283
298
|
const orig = this.credentialOffer.original_credential_offer;
|
|
284
299
|
const types = typeof orig.credential_type === 'string' ? [orig.credential_type] : orig.credential_type;
|
|
285
300
|
const result = [];
|
|
@@ -288,52 +303,77 @@ class OpenID4VCIClient {
|
|
|
288
303
|
}
|
|
289
304
|
else {
|
|
290
305
|
return this.credentialOffer.credential_offer.credentials.map((c) => {
|
|
291
|
-
|
|
306
|
+
if (typeof c === 'string') {
|
|
307
|
+
return [c];
|
|
308
|
+
}
|
|
309
|
+
else if ('types' in c) {
|
|
310
|
+
return c.types;
|
|
311
|
+
}
|
|
312
|
+
else if ('vct' in c) {
|
|
313
|
+
return [c.vct];
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
return c.credential_definition.types;
|
|
317
|
+
}
|
|
292
318
|
});
|
|
293
319
|
}
|
|
294
320
|
}
|
|
295
321
|
issuerSupportedFlowTypes() {
|
|
296
|
-
|
|
322
|
+
var _a, _b, _c, _d;
|
|
323
|
+
return ((_b = (_a = this.credentialOffer) === null || _a === void 0 ? void 0 : _a.supportedFlows) !== null && _b !== void 0 ? _b : (((_d = (_c = this._state.endpointMetadata) === null || _c === void 0 ? void 0 : _c.credentialIssuerMetadata) === null || _d === void 0 ? void 0 : _d.authorization_endpoint) ? [oid4vci_common_1.AuthzFlowType.AUTHORIZATION_CODE_FLOW] : []));
|
|
324
|
+
}
|
|
325
|
+
isFlowTypeSupported(flowType) {
|
|
326
|
+
return this.issuerSupportedFlowTypes().includes(flowType);
|
|
327
|
+
}
|
|
328
|
+
get authorizationURL() {
|
|
329
|
+
return this._state.authorizationURL;
|
|
330
|
+
}
|
|
331
|
+
hasAuthorizationURL() {
|
|
332
|
+
return !!this.authorizationURL;
|
|
297
333
|
}
|
|
298
334
|
get credentialOffer() {
|
|
299
|
-
return this.
|
|
335
|
+
return this._state.credentialOffer;
|
|
300
336
|
}
|
|
301
337
|
version() {
|
|
302
|
-
|
|
338
|
+
var _a, _b;
|
|
339
|
+
return (_b = (_a = this.credentialOffer) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : oid4vci_common_1.OpenId4VCIVersion.VER_1_0_11;
|
|
303
340
|
}
|
|
304
341
|
get endpointMetadata() {
|
|
305
342
|
this.assertServerMetadata();
|
|
306
343
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
307
|
-
return this.
|
|
344
|
+
return this._state.endpointMetadata;
|
|
308
345
|
}
|
|
309
346
|
get kid() {
|
|
310
347
|
this.assertIssuerData();
|
|
311
|
-
if (!this.
|
|
348
|
+
if (!this._state.kid) {
|
|
312
349
|
throw new Error('No value for kid is supplied');
|
|
313
350
|
}
|
|
314
|
-
return this.
|
|
351
|
+
return this._state.kid;
|
|
315
352
|
}
|
|
316
353
|
get alg() {
|
|
317
354
|
this.assertIssuerData();
|
|
318
|
-
if (!this.
|
|
355
|
+
if (!this._state.alg) {
|
|
319
356
|
throw new Error('No value for alg is supplied');
|
|
320
357
|
}
|
|
321
|
-
return this.
|
|
358
|
+
return this._state.alg;
|
|
359
|
+
}
|
|
360
|
+
set clientId(value) {
|
|
361
|
+
this._state.clientId = value;
|
|
322
362
|
}
|
|
323
363
|
get clientId() {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return this.
|
|
364
|
+
return this._state.clientId;
|
|
365
|
+
}
|
|
366
|
+
hasAccessTokenResponse() {
|
|
367
|
+
return !!this._state.accessTokenResponse;
|
|
328
368
|
}
|
|
329
369
|
get accessTokenResponse() {
|
|
330
370
|
this.assertAccessToken();
|
|
331
371
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
332
|
-
return this.
|
|
372
|
+
return this._state.accessTokenResponse;
|
|
333
373
|
}
|
|
334
374
|
getIssuer() {
|
|
335
375
|
this.assertIssuerData();
|
|
336
|
-
return this.
|
|
376
|
+
return this._state.credentialIssuer;
|
|
337
377
|
}
|
|
338
378
|
getAccessTokenEndpoint() {
|
|
339
379
|
this.assertIssuerData();
|
|
@@ -345,21 +385,60 @@ class OpenID4VCIClient {
|
|
|
345
385
|
this.assertIssuerData();
|
|
346
386
|
return this.endpointMetadata ? this.endpointMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
|
|
347
387
|
}
|
|
388
|
+
hasDeferredCredentialEndpoint() {
|
|
389
|
+
return !!this.getAccessTokenEndpoint();
|
|
390
|
+
}
|
|
391
|
+
getDeferredCredentialEndpoint() {
|
|
392
|
+
this.assertIssuerData();
|
|
393
|
+
return this.endpointMetadata ? this.endpointMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Too bad we need a method like this, but EBSI is not exposing metadata
|
|
397
|
+
*/
|
|
398
|
+
isEBSI() {
|
|
399
|
+
var _a, _b, _c;
|
|
400
|
+
if ((_a = this.credentialOffer) === null || _a === void 0 ? void 0 : _a.credential_offer.credentials.find((cred) =>
|
|
401
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
402
|
+
// @ts-ignore
|
|
403
|
+
typeof cred !== 'string' && 'trust_framework' in cred && 'name' in cred.trust_framework && cred.trust_framework.name.includes('ebsi'))) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
this.assertIssuerData();
|
|
407
|
+
return (_c = (_b = this.endpointMetadata.credentialIssuerMetadata) === null || _b === void 0 ? void 0 : _b.authorization_endpoint) === null || _c === void 0 ? void 0 : _c.includes('ebsi.eu');
|
|
408
|
+
}
|
|
348
409
|
assertIssuerData() {
|
|
349
|
-
if (!this.
|
|
410
|
+
if (!this._state.credentialIssuer) {
|
|
411
|
+
throw Error(`No credential issuer value present`);
|
|
412
|
+
}
|
|
413
|
+
else if (!this._state.credentialOffer && this._state.endpointMetadata && this.issuerSupportedFlowTypes().length === 0) {
|
|
350
414
|
throw Error(`No issuance initiation or credential offer present`);
|
|
351
415
|
}
|
|
352
416
|
}
|
|
353
417
|
assertServerMetadata() {
|
|
354
|
-
if (!this.
|
|
418
|
+
if (!this._state.endpointMetadata) {
|
|
355
419
|
throw Error('No server metadata');
|
|
356
420
|
}
|
|
357
421
|
}
|
|
358
422
|
assertAccessToken() {
|
|
359
|
-
if (!this.
|
|
423
|
+
if (!this._state.accessTokenResponse) {
|
|
360
424
|
throw Error(`No access token present`);
|
|
361
425
|
}
|
|
362
426
|
}
|
|
427
|
+
syncAuthorizationRequestOpts(opts) {
|
|
428
|
+
var _a, _b;
|
|
429
|
+
let authorizationRequestOpts = Object.assign(Object.assign({}, (_a = this._state) === null || _a === void 0 ? void 0 : _a.authorizationRequestOpts), opts);
|
|
430
|
+
if (!authorizationRequestOpts) {
|
|
431
|
+
// We only set a redirectUri if no options are provided.
|
|
432
|
+
// Note that this only works for mobile apps, that can handle a code query param on the default openid-credential-offer deeplink.
|
|
433
|
+
// Provide your own options if that is not desired!
|
|
434
|
+
authorizationRequestOpts = { redirectUri: `${oid4vci_common_1.DefaultURISchemes.CREDENTIAL_OFFER}://` };
|
|
435
|
+
}
|
|
436
|
+
const clientId = (_b = authorizationRequestOpts.clientId) !== null && _b !== void 0 ? _b : this._state.clientId;
|
|
437
|
+
// sync clientId
|
|
438
|
+
this._state.clientId = clientId;
|
|
439
|
+
authorizationRequestOpts.clientId = clientId;
|
|
440
|
+
return authorizationRequestOpts;
|
|
441
|
+
}
|
|
363
442
|
}
|
|
364
443
|
exports.OpenID4VCIClient = OpenID4VCIClient;
|
|
365
444
|
//# sourceMappingURL=OpenID4VCIClient.js.map
|