@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.
Files changed (72) hide show
  1. package/README.md +9 -8
  2. package/dist/AccessTokenClient.d.ts +0 -1
  3. package/dist/AccessTokenClient.d.ts.map +1 -1
  4. package/dist/AccessTokenClient.js +11 -18
  5. package/dist/AccessTokenClient.js.map +1 -1
  6. package/dist/AuthorizationCodeClient.d.ts +9 -0
  7. package/dist/AuthorizationCodeClient.d.ts.map +1 -0
  8. package/dist/AuthorizationCodeClient.js +132 -0
  9. package/dist/AuthorizationCodeClient.js.map +1 -0
  10. package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -1
  11. package/dist/AuthorizationDetailsBuilder.js.map +1 -1
  12. package/dist/CredentialOfferClient.d.ts.map +1 -1
  13. package/dist/CredentialOfferClient.js +3 -1
  14. package/dist/CredentialOfferClient.js.map +1 -1
  15. package/dist/CredentialRequestClient.d.ts +15 -0
  16. package/dist/CredentialRequestClient.d.ts.map +1 -1
  17. package/dist/CredentialRequestClient.js +91 -43
  18. package/dist/CredentialRequestClient.js.map +1 -1
  19. package/dist/CredentialRequestClientBuilder.d.ts +19 -7
  20. package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
  21. package/dist/CredentialRequestClientBuilder.js +31 -1
  22. package/dist/CredentialRequestClientBuilder.js.map +1 -1
  23. package/dist/MetadataClient.d.ts.map +1 -1
  24. package/dist/MetadataClient.js +12 -1
  25. package/dist/MetadataClient.js.map +1 -1
  26. package/dist/OpenID4VCIClient.d.ts +62 -27
  27. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  28. package/dist/OpenID4VCIClient.js +255 -176
  29. package/dist/OpenID4VCIClient.js.map +1 -1
  30. package/dist/ProofOfPossessionBuilder.d.ts +3 -1
  31. package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
  32. package/dist/ProofOfPossessionBuilder.js +5 -0
  33. package/dist/ProofOfPossessionBuilder.js.map +1 -1
  34. package/dist/functions/AuthorizationUtil.d.ts +3 -0
  35. package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
  36. package/dist/functions/AuthorizationUtil.js +22 -0
  37. package/dist/functions/AuthorizationUtil.js.map +1 -0
  38. package/dist/functions/ProofUtil.d.ts +2 -1
  39. package/dist/functions/ProofUtil.d.ts.map +1 -1
  40. package/dist/functions/ProofUtil.js +6 -4
  41. package/dist/functions/ProofUtil.js.map +1 -1
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/types/index.js +2 -0
  49. package/dist/types/index.js.map +1 -0
  50. package/lib/AccessTokenClient.ts +16 -20
  51. package/lib/AuthorizationCodeClient.ts +163 -0
  52. package/lib/AuthorizationDetailsBuilder.ts +2 -2
  53. package/lib/CredentialOfferClient.ts +4 -1
  54. package/lib/CredentialRequestClient.ts +116 -45
  55. package/lib/CredentialRequestClientBuilder.ts +53 -8
  56. package/lib/MetadataClient.ts +13 -1
  57. package/lib/OpenID4VCIClient.ts +348 -216
  58. package/lib/ProofOfPossessionBuilder.ts +8 -0
  59. package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
  60. package/lib/__tests__/CredentialRequestClient.spec.ts +28 -8
  61. package/lib/__tests__/EBSIE2E.spec.test.ts +145 -0
  62. package/lib/__tests__/MetadataClient.spec.ts +4 -1
  63. package/lib/__tests__/OpenID4VCIClient.spec.ts +117 -76
  64. package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
  65. package/lib/__tests__/SdJwt.spec.ts +163 -0
  66. package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
  67. package/lib/__tests__/data/VciDataFixtures.ts +14 -13
  68. package/lib/functions/AuthorizationUtil.ts +18 -0
  69. package/lib/functions/ProofUtil.ts +18 -4
  70. package/lib/index.ts +1 -0
  71. package/lib/types/index.ts +0 -0
  72. package/package.json +8 -6
@@ -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 functions_1 = require("./functions");
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, clientId) {
28
- this._credentialOffer = credentialOffer;
29
- this._kid = kid;
30
- this._alg = alg;
31
- this._clientId = clientId;
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 fromURI({ uri, kid, alg, retrieveServerMetadata, clientId, resolveOfferUri, }) {
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(yield CredentialOfferClient_1.CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }), kid, alg, clientId);
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
- retrieveServerMetadata() {
72
+ static fromState({ state }) {
43
73
  return __awaiter(this, void 0, void 0, function* () {
44
- this.assertIssuerData();
45
- if (!this._endpointMetadata) {
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
- createAuthorizationRequestUrl({ codeChallengeMethod, codeChallenge, authorizationDetails, redirectUri, scope }) {
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
- // Scope and authorization_details can be used in the same authorization request
97
- // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param
98
- if (!scope && !authorizationDetails) {
99
- throw Error('Please provide a scope or authorization_details');
100
- }
101
- // Authorization servers supporting PAR SHOULD include the URL of their pushed authorization request endpoint in their authorization server metadata document
102
- // Note that the presence of pushed_authorization_request_endpoint is sufficient for a client to determine that it may use the PAR flow.
103
- // What happens if it doesn't ???
104
- // let parEndpoint: string
105
- if (!((_a = this._endpointMetadata) === null || _a === void 0 ? void 0 : _a.credentialIssuerMetadata) ||
106
- !('pushed_authorization_request_endpoint' in this._endpointMetadata.credentialIssuerMetadata) ||
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 (this.credentialOffer.issuerState) {
127
- queryObj['issuer_state'] = this.credentialOffer.issuerState;
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
- const response = yield (0, functions_1.formPost)(parEndpoint, new URLSearchParams(queryObj));
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
- handleAuthorizationDetails(authorizationDetails) {
138
- if (authorizationDetails) {
139
- if (Array.isArray(authorizationDetails)) {
140
- return authorizationDetails.map((value) => this.handleLocations(Object.assign({}, value)));
141
- }
142
- else {
143
- return this.handleLocations(Object.assign({}, authorizationDetails));
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
- return authorizationDetails;
130
+ return this._state.authorizationURL;
131
+ });
147
132
  }
148
- handleLocations(authorizationDetails) {
149
- var _a;
150
- if (authorizationDetails &&
151
- (((_a = this.endpointMetadata.credentialIssuerMetadata) === null || _a === void 0 ? void 0 : _a.authorization_server) || this.endpointMetadata.authorization_endpoint)) {
152
- if (authorizationDetails.locations) {
153
- if (Array.isArray(authorizationDetails.locations)) {
154
- authorizationDetails.locations.push(this.endpointMetadata.issuer);
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
- authorizationDetails.locations = [authorizationDetails.locations, this.endpointMetadata.issuer];
144
+ throw Error(`Cannot retrieve issuer metadata without either a credential offer, or issuer value`);
158
145
  }
159
146
  }
160
- else {
161
- authorizationDetails.locations = this.endpointMetadata.issuer;
162
- }
163
- }
164
- return authorizationDetails;
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, codeVerifier, code, redirectUri } = opts !== null && opts !== void 0 ? opts : {};
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._clientId = clientId;
164
+ this._state.clientId = clientId;
173
165
  }
174
- if (!this._accessTokenResponse) {
166
+ if (!this._state.accessTokenResponse) {
175
167
  const accessTokenClient = new AccessTokenClient_1.AccessTokenClient();
176
- const response = yield accessTokenClient.acquireAccessToken({
177
- credentialOffer: this.credentialOffer,
178
- metadata: this.endpointMetadata,
179
- pin,
180
- codeVerifier,
181
- code,
182
- redirectUri,
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 ${(_a = this._endpointMetadata) === null || _a === void 0 ? void 0 : _a.token_endpoint} for issuer ${this.getIssuer()} failed with status: ${response.origResponse.status}`);
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 ${(_b = this._endpointMetadata) === null || _b === void 0 ? void 0 : _b.token_endpoint} for issuer ${this.getIssuer()} failed as there was no success response body`);
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._accessTokenResponse = response.successBody;
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 (alg) {
202
- this._alg = alg;
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 (kid) {
205
- this._kid = kid;
206
- }
207
- const requestBuilder = CredentialRequestClientBuilder_1.CredentialRequestClientBuilder.fromCredentialOffer({
208
- credentialOffer: this.credentialOffer,
209
- metadata: this.endpointMetadata,
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.sort() : [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
- if (!supportedCredential.types || supportedCredential.types.length === 0) {
219
- throw Error('types is required in the credentials supported');
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
- throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`);
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
- .withKid(this.kid);
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: 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._endpointMetadata) === null || _b === void 0 ? void 0 : _b.credential_endpoint} for issuer ${this.getIssuer()} failed with status: ${response.origResponse.status}`);
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._endpointMetadata) === null || _c === void 0 ? void 0 : _c.credential_endpoint} for issuer ${this.getIssuer()} failed as there was no success response body`);
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, IssuerMetadataUtils_1.getSupportedCredentials)({
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.version < oid4vci_common_1.OpenId4VCIVersion.VER_1_0_11) {
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
- return typeof c === 'string' ? [c] : c.types;
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
- return this.credentialOffer.supportedFlows;
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._credentialOffer;
335
+ return this._state.credentialOffer;
300
336
  }
301
337
  version() {
302
- return this.credentialOffer.version;
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._endpointMetadata;
344
+ return this._state.endpointMetadata;
308
345
  }
309
346
  get kid() {
310
347
  this.assertIssuerData();
311
- if (!this._kid) {
348
+ if (!this._state.kid) {
312
349
  throw new Error('No value for kid is supplied');
313
350
  }
314
- return this._kid;
351
+ return this._state.kid;
315
352
  }
316
353
  get alg() {
317
354
  this.assertIssuerData();
318
- if (!this._alg) {
355
+ if (!this._state.alg) {
319
356
  throw new Error('No value for alg is supplied');
320
357
  }
321
- return this._alg;
358
+ return this._state.alg;
359
+ }
360
+ set clientId(value) {
361
+ this._state.clientId = value;
322
362
  }
323
363
  get clientId() {
324
- /*if (!this._clientId) {
325
- throw Error('No client id present');
326
- }*/
327
- return this._clientId;
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._accessTokenResponse;
372
+ return this._state.accessTokenResponse;
333
373
  }
334
374
  getIssuer() {
335
375
  this.assertIssuerData();
336
- return this._endpointMetadata ? this.endpointMetadata.issuer : this.getIssuer();
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._credentialOffer) {
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._endpointMetadata) {
418
+ if (!this._state.endpointMetadata) {
355
419
  throw Error('No server metadata');
356
420
  }
357
421
  }
358
422
  assertAccessToken() {
359
- if (!this._accessTokenResponse) {
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