@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
package/lib/AccessTokenClient.ts
CHANGED
|
@@ -1,245 +1,277 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AccessTokenRequest,
|
|
3
|
-
AccessTokenRequestOpts,
|
|
4
|
-
AccessTokenResponse,
|
|
5
|
-
assertedUniformCredentialOffer,
|
|
6
|
-
AuthorizationServerOpts,
|
|
7
|
-
AuthzFlowType,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
issuer
|
|
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
|
-
accessTokenRequest
|
|
67
|
-
|
|
68
|
-
metadata
|
|
69
|
-
asOpts
|
|
70
|
-
issuerOpts
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
request.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (!
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AccessTokenRequest,
|
|
3
|
+
AccessTokenRequestOpts,
|
|
4
|
+
AccessTokenResponse,
|
|
5
|
+
assertedUniformCredentialOffer,
|
|
6
|
+
AuthorizationServerOpts,
|
|
7
|
+
AuthzFlowType,
|
|
8
|
+
CredentialOfferPayloadV1_0_13,
|
|
9
|
+
CredentialOfferV1_0_13,
|
|
10
|
+
determineSpecVersionFromOffer,
|
|
11
|
+
EndpointMetadata,
|
|
12
|
+
getIssuerFromCredentialOfferPayload,
|
|
13
|
+
GrantTypes,
|
|
14
|
+
IssuerOpts,
|
|
15
|
+
JsonURIMode,
|
|
16
|
+
OpenId4VCIVersion,
|
|
17
|
+
OpenIDResponse,
|
|
18
|
+
PRE_AUTH_CODE_LITERAL,
|
|
19
|
+
TokenErrorResponse,
|
|
20
|
+
toUniformCredentialOfferRequest,
|
|
21
|
+
TxCodeAndPinRequired,
|
|
22
|
+
UniformCredentialOfferPayload,
|
|
23
|
+
} from '@sphereon/oid4vci-common';
|
|
24
|
+
import { ObjectUtils } from '@sphereon/ssi-types';
|
|
25
|
+
import Debug from 'debug';
|
|
26
|
+
|
|
27
|
+
import { MetadataClient } from './MetadataClient';
|
|
28
|
+
import { convertJsonToURI, formPost } from './functions';
|
|
29
|
+
|
|
30
|
+
const debug = Debug('sphereon:oid4vci:token');
|
|
31
|
+
|
|
32
|
+
export class AccessTokenClient {
|
|
33
|
+
public async acquireAccessToken(opts: AccessTokenRequestOpts): Promise<OpenIDResponse<AccessTokenResponse>> {
|
|
34
|
+
const { asOpts, pin, codeVerifier, code, redirectUri, metadata } = opts;
|
|
35
|
+
|
|
36
|
+
const credentialOffer = opts.credentialOffer ? await assertedUniformCredentialOffer(opts.credentialOffer) : undefined;
|
|
37
|
+
const pinMetadata: TxCodeAndPinRequired | undefined = credentialOffer && this.getPinMetadata(credentialOffer.credential_offer);
|
|
38
|
+
const issuer =
|
|
39
|
+
opts.credentialIssuer ??
|
|
40
|
+
(credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : (metadata?.issuer as string));
|
|
41
|
+
if (!issuer) {
|
|
42
|
+
throw Error('Issuer required at this point');
|
|
43
|
+
}
|
|
44
|
+
const issuerOpts = {
|
|
45
|
+
issuer,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return await this.acquireAccessTokenUsingRequest({
|
|
49
|
+
accessTokenRequest: await this.createAccessTokenRequest({
|
|
50
|
+
credentialOffer,
|
|
51
|
+
asOpts,
|
|
52
|
+
codeVerifier,
|
|
53
|
+
code,
|
|
54
|
+
redirectUri,
|
|
55
|
+
pin,
|
|
56
|
+
pinMetadata,
|
|
57
|
+
}),
|
|
58
|
+
pinMetadata,
|
|
59
|
+
metadata,
|
|
60
|
+
asOpts,
|
|
61
|
+
issuerOpts,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async acquireAccessTokenUsingRequest({
|
|
66
|
+
accessTokenRequest,
|
|
67
|
+
pinMetadata,
|
|
68
|
+
metadata,
|
|
69
|
+
asOpts,
|
|
70
|
+
issuerOpts,
|
|
71
|
+
}: {
|
|
72
|
+
accessTokenRequest: AccessTokenRequest;
|
|
73
|
+
pinMetadata?: TxCodeAndPinRequired;
|
|
74
|
+
metadata?: EndpointMetadata;
|
|
75
|
+
asOpts?: AuthorizationServerOpts;
|
|
76
|
+
issuerOpts?: IssuerOpts;
|
|
77
|
+
}): Promise<OpenIDResponse<AccessTokenResponse>> {
|
|
78
|
+
this.validate(accessTokenRequest, pinMetadata);
|
|
79
|
+
|
|
80
|
+
const requestTokenURL = AccessTokenClient.determineTokenURL({
|
|
81
|
+
asOpts,
|
|
82
|
+
issuerOpts,
|
|
83
|
+
metadata: metadata
|
|
84
|
+
? metadata
|
|
85
|
+
: issuerOpts?.fetchMetadata
|
|
86
|
+
? await MetadataClient.retrieveAllMetadata(issuerOpts.issuer, { errorOnNotFound: false })
|
|
87
|
+
: undefined,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return this.sendAuthCode(requestTokenURL, accessTokenRequest);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public async createAccessTokenRequest(opts: AccessTokenRequestOpts): Promise<AccessTokenRequest> {
|
|
94
|
+
const { asOpts, pin, codeVerifier, code, redirectUri } = opts;
|
|
95
|
+
const credentialOfferRequest =
|
|
96
|
+
opts.credentialOffer &&
|
|
97
|
+
determineSpecVersionFromOffer(opts.credentialOffer as CredentialOfferPayloadV1_0_13).valueOf() <= OpenId4VCIVersion.VER_1_0_13.valueOf()
|
|
98
|
+
? await toUniformCredentialOfferRequest(opts.credentialOffer as CredentialOfferV1_0_13)
|
|
99
|
+
: undefined;
|
|
100
|
+
const request: Partial<AccessTokenRequest> = {};
|
|
101
|
+
|
|
102
|
+
if (asOpts?.clientId) {
|
|
103
|
+
request.client_id = asOpts.clientId;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
|
|
107
|
+
this.assertAlphanumericPin(opts.pinMetadata, pin);
|
|
108
|
+
request.user_pin = pin;
|
|
109
|
+
|
|
110
|
+
request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE;
|
|
111
|
+
// we actually know it is there because of the isPreAuthCode call
|
|
112
|
+
request[PRE_AUTH_CODE_LITERAL] =
|
|
113
|
+
credentialOfferRequest?.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.[PRE_AUTH_CODE_LITERAL];
|
|
114
|
+
|
|
115
|
+
return request as AccessTokenRequest;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
|
|
119
|
+
request.grant_type = GrantTypes.AUTHORIZATION_CODE;
|
|
120
|
+
request.code = code;
|
|
121
|
+
request.redirect_uri = redirectUri;
|
|
122
|
+
|
|
123
|
+
if (codeVerifier) {
|
|
124
|
+
request.code_verifier = codeVerifier;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return request as AccessTokenRequest;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new Error('Credential offer request does not follow neither pre-authorized code nor authorization code flow requirements.');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private assertPreAuthorizedGrantType(grantType: GrantTypes): void {
|
|
134
|
+
if (GrantTypes.PRE_AUTHORIZED_CODE !== grantType) {
|
|
135
|
+
throw new Error("grant type must be 'urn:ietf:params:oauth:grant-type:pre-authorized_code'");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private assertAuthorizationGrantType(grantType: GrantTypes): void {
|
|
140
|
+
if (GrantTypes.AUTHORIZATION_CODE !== grantType) {
|
|
141
|
+
throw new Error("grant type must be 'authorization_code'");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private getPinMetadata(requestPayload: UniformCredentialOfferPayload): TxCodeAndPinRequired {
|
|
146
|
+
if (!requestPayload) {
|
|
147
|
+
throw new Error(TokenErrorResponse.invalid_request);
|
|
148
|
+
}
|
|
149
|
+
const issuer = getIssuerFromCredentialOfferPayload(requestPayload);
|
|
150
|
+
|
|
151
|
+
const grantDetails = requestPayload.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code'];
|
|
152
|
+
const isPinRequired = !!grantDetails?.tx_code || !!grantDetails?.['pre-authorized_code'];
|
|
153
|
+
|
|
154
|
+
debug(`Pin required for issuer ${issuer}: ${isPinRequired}`);
|
|
155
|
+
return {
|
|
156
|
+
txCode: grantDetails?.tx_code,
|
|
157
|
+
isPinRequired,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private assertAlphanumericPin(pinMeta?: TxCodeAndPinRequired, pin?: string): void {
|
|
162
|
+
if (pinMeta?.isPinRequired) {
|
|
163
|
+
let regex;
|
|
164
|
+
|
|
165
|
+
if (pinMeta.txCode) {
|
|
166
|
+
const { input_mode, length } = pinMeta.txCode;
|
|
167
|
+
|
|
168
|
+
if (input_mode === 'numeric') {
|
|
169
|
+
// Create a regex for numeric input. If no length specified, allow any length of numeric input.
|
|
170
|
+
regex = length ? new RegExp(`^\\d{1,${length}}$`) : /^\d+$/;
|
|
171
|
+
} else if (input_mode === 'text') {
|
|
172
|
+
// Create a regex for text input. If no length specified, allow any length of alphanumeric input.
|
|
173
|
+
regex = length ? new RegExp(`^[a-zA-Z0-9]{1,${length}}$`) : /^[a-zA-Z0-9]+$/;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Default regex for alphanumeric with no specific length limit if no input_mode is specified.
|
|
178
|
+
regex = regex || /^[a-zA-Z0-9]+$|^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/;
|
|
179
|
+
|
|
180
|
+
if (!pin || !regex.test(pin)) {
|
|
181
|
+
debug(
|
|
182
|
+
`Pin is not valid. Expected format: ${pinMeta?.txCode?.input_mode || 'alphanumeric'}, Length: up to ${pinMeta?.txCode?.length || 'any number of'} characters`,
|
|
183
|
+
);
|
|
184
|
+
throw new Error('A valid pin must be present according to the specified transaction code requirements.');
|
|
185
|
+
}
|
|
186
|
+
} else if (pin) {
|
|
187
|
+
debug('Pin set, whilst not required');
|
|
188
|
+
throw new Error('Cannot set a pin when the pin is not required.');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private assertNonEmptyPreAuthorizedCode(accessTokenRequest: AccessTokenRequest): void {
|
|
193
|
+
if (!accessTokenRequest[PRE_AUTH_CODE_LITERAL]) {
|
|
194
|
+
debug(`No pre-authorized code present, whilst it is required`);
|
|
195
|
+
throw new Error('Pre-authorization must be proven by presenting the pre-authorized code. Code must be present.');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private assertNonEmptyCodeVerifier(accessTokenRequest: AccessTokenRequest): void {
|
|
200
|
+
if (!accessTokenRequest.code_verifier) {
|
|
201
|
+
debug('No code_verifier present, whilst it is required');
|
|
202
|
+
throw new Error('Authorization flow requires the code_verifier to be present');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private assertNonEmptyCode(accessTokenRequest: AccessTokenRequest): void {
|
|
207
|
+
if (!accessTokenRequest.code) {
|
|
208
|
+
debug('No code present, whilst it is required');
|
|
209
|
+
throw new Error('Authorization flow requires the code to be present');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
private validate(accessTokenRequest: AccessTokenRequest, pinMeta?: TxCodeAndPinRequired): void {
|
|
213
|
+
if (accessTokenRequest.grant_type === GrantTypes.PRE_AUTHORIZED_CODE) {
|
|
214
|
+
this.assertPreAuthorizedGrantType(accessTokenRequest.grant_type);
|
|
215
|
+
this.assertNonEmptyPreAuthorizedCode(accessTokenRequest);
|
|
216
|
+
this.assertAlphanumericPin(pinMeta, accessTokenRequest['pre-authorized_code']);
|
|
217
|
+
} else if (accessTokenRequest.grant_type === GrantTypes.AUTHORIZATION_CODE) {
|
|
218
|
+
this.assertAuthorizationGrantType(accessTokenRequest.grant_type);
|
|
219
|
+
this.assertNonEmptyCodeVerifier(accessTokenRequest);
|
|
220
|
+
this.assertNonEmptyCode(accessTokenRequest);
|
|
221
|
+
} else {
|
|
222
|
+
this.throwNotSupportedFlow();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private async sendAuthCode(requestTokenURL: string, accessTokenRequest: AccessTokenRequest): Promise<OpenIDResponse<AccessTokenResponse>> {
|
|
227
|
+
return await formPost(requestTokenURL, convertJsonToURI(accessTokenRequest, { mode: JsonURIMode.X_FORM_WWW_URLENCODED }));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public static determineTokenURL({
|
|
231
|
+
asOpts,
|
|
232
|
+
issuerOpts,
|
|
233
|
+
metadata,
|
|
234
|
+
}: {
|
|
235
|
+
asOpts?: AuthorizationServerOpts;
|
|
236
|
+
issuerOpts?: IssuerOpts;
|
|
237
|
+
metadata?: EndpointMetadata;
|
|
238
|
+
}): string {
|
|
239
|
+
if (!asOpts && !metadata?.token_endpoint && !issuerOpts) {
|
|
240
|
+
throw new Error('Cannot determine token URL if no issuer, metadata and no Authorization Server values are present');
|
|
241
|
+
}
|
|
242
|
+
let url;
|
|
243
|
+
if (asOpts && asOpts.as) {
|
|
244
|
+
url = this.creatTokenURLFromURL(asOpts.as, asOpts?.allowInsecureEndpoints, asOpts.tokenEndpoint);
|
|
245
|
+
} else if (metadata?.token_endpoint) {
|
|
246
|
+
url = metadata.token_endpoint;
|
|
247
|
+
} else {
|
|
248
|
+
if (!issuerOpts?.issuer) {
|
|
249
|
+
throw Error('Either authorization server options, a token endpoint or issuer options are required at this point');
|
|
250
|
+
}
|
|
251
|
+
url = this.creatTokenURLFromURL(issuerOpts.issuer, asOpts?.allowInsecureEndpoints, issuerOpts.tokenEndpoint);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!url || !ObjectUtils.isString(url)) {
|
|
255
|
+
throw new Error('No authorization server token URL present. Cannot acquire access token');
|
|
256
|
+
}
|
|
257
|
+
debug(`Token endpoint determined to be ${url}`);
|
|
258
|
+
return url;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private static creatTokenURLFromURL(url: string, allowInsecureEndpoints?: boolean, tokenEndpoint?: string): string {
|
|
262
|
+
if (allowInsecureEndpoints !== true && url.startsWith('http:')) {
|
|
263
|
+
throw Error(
|
|
264
|
+
`Unprotected token endpoints are not allowed ${url}. Use the 'allowInsecureEndpoints' param if you really need this for dev/testing!`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const hostname = url.replace(/https?:\/\//, '').replace(/\/$/, '');
|
|
268
|
+
const endpoint = tokenEndpoint ? (tokenEndpoint.startsWith('/') ? tokenEndpoint : tokenEndpoint.substring(1)) : '/token';
|
|
269
|
+
const scheme = url.split('://')[0];
|
|
270
|
+
return `${scheme ? scheme + '://' : 'https://'}${hostname}${endpoint}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private throwNotSupportedFlow(): void {
|
|
274
|
+
debug(`Only pre-authorized or authorization code flows supported.`);
|
|
275
|
+
throw new Error('Only pre-authorized-code or authorization code flows are supported');
|
|
276
|
+
}
|
|
277
|
+
}
|