commit c6d64e20bd90d81504c4e64b01b8c07cf944da9f
parent eeea26dd11a76719b3acec034a613a36cc4b3456
Author: Florian Dold <florian@dold.me>
Date: Fri, 13 Feb 2026 12:19:43 +0100
util: fix handling of ETag header in kyc-info request
Diffstat:
1 file changed, 60 insertions(+), 41 deletions(-)
diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts
@@ -114,6 +114,7 @@ import {
AmountJson,
Amounts,
CancellationToken,
+ Logger,
LongpollQueue,
PaytoHash,
signAmlDecision,
@@ -129,6 +130,8 @@ export type TalerExchangeErrorsByMethod2<
prop extends keyof TalerExchangeHttpClient,
> = FailCasesByMethod<TalerExchangeHttpClient, prop>;
+const logger = new Logger("exchange-client.ts");
+
export enum TalerExchangeCacheEviction {
UPLOAD_KYC_FORM,
MAKE_AML_DECISION,
@@ -387,13 +390,13 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<ExchangeMergeSuccessResponse>
| OperationAlternative<
- HttpStatusCode.UnavailableForLegalReasons,
- LegitimizationNeededResponse
- >
+ HttpStatusCode.UnavailableForLegalReasons,
+ LegitimizationNeededResponse
+ >
| OperationAlternative<
- HttpStatusCode.Conflict,
- ExchangeMergeConflictResponse
- >
+ HttpStatusCode.Conflict,
+ ExchangeMergeConflictResponse
+ >
| OperationFail<HttpStatusCode.Forbidden>
| OperationFail<HttpStatusCode.NotFound>
| OperationFail<HttpStatusCode.Gone>
@@ -440,9 +443,9 @@ export class TalerExchangeHttpClient {
| OperationFail<HttpStatusCode.NotFound>
| OperationAlternative<HttpStatusCode.Conflict, PurseConflictPartial>
| OperationAlternative<
- HttpStatusCode.UnavailableForLegalReasons,
- LegitimizationNeededResponse
- >
+ HttpStatusCode.UnavailableForLegalReasons,
+ LegitimizationNeededResponse
+ >
> {
const resp = await this.fetch(`reserves/${pursePub}/purse`, {
method: "POST",
@@ -695,7 +698,9 @@ export class TalerExchangeHttpClient {
{
method: "GET",
headers: {
- "If-None-Match": known.length ? known.map(d => `"${d}"`).join(",") : undefined,
+ "If-None-Match": known.length
+ ? known.map((d) => `"${d}"`).join(",")
+ : undefined,
},
},
longpoll,
@@ -719,6 +724,8 @@ export class TalerExchangeHttpClient {
/**
* SPA-Specific version of checkKycInfo
+ *
+ * FIXME: Unify with checkKycInfo
*/
async checkKycInfoSpa(
token: AccessToken,
@@ -740,7 +747,20 @@ export class TalerExchangeHttpClient {
// we need to add the etag to the response because the
// client needs it to repeat the request and
// do the long polling
- const etag = resp.headers.get("etag") ?? undefined;
+ const etagRaw = resp.headers.get("etag") ?? undefined;
+ let etag: string | undefined;
+ if (
+ etagRaw != null &&
+ etagRaw.startsWith('"') &&
+ etagRaw.endsWith('"')
+ ) {
+ etag = etagRaw.substring(1, etagRaw.length - 1);
+ } else if (etagRaw == null) {
+ // No ETag, fine.
+ etag = etagRaw;
+ } else {
+ logger.warn(`malformed ETag header in kyc-info response`);
+ }
const body = await readSuccessResponseJsonOrThrow(
resp,
codecForKycProcessClientInformation(),
@@ -805,10 +825,10 @@ export class TalerExchangeHttpClient {
body: object = {},
): Promise<
| OperationFail<
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- | HttpStatusCode.PayloadTooLarge
- >
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ | HttpStatusCode.PayloadTooLarge
+ >
| OperationOk<KycProcessStartInformation>
> {
const resp = await this.fetch(`kyc-start/${requirement}`, {
@@ -867,10 +887,10 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<AvailableMeasureSummary>
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
> {
const resp = await this.fetch(`aml/${auth.id}/measures`, {
method: "GET",
@@ -1052,7 +1072,6 @@ export class TalerExchangeHttpClient {
}
}
-
/**
* https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions
*
@@ -1066,10 +1085,10 @@ export class TalerExchangeHttpClient {
} = {},
): Promise<
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
| OperationOk<AmlDecisionsResponse>
> {
const url = new URL(`aml/${auth.id}/decisions`, this.baseUrl);
@@ -1161,10 +1180,10 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<KycAttributes>
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
> {
const url = new URL(`aml/${auth.id}/attributes/${account}`, this.baseUrl);
@@ -1193,7 +1212,7 @@ export class TalerExchangeHttpClient {
/**
* https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-attributes-$H_NORMALIZED_PAYTO
- *
+ *
*/
async getAmlAttributesForAccountAsPdf(
auth: OfficerSession,
@@ -1281,10 +1300,10 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<ExchangeTransferList>
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
> {
const url = new URL(`aml/${auth.id}/transfers-credit`, this.baseUrl);
@@ -1332,10 +1351,10 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<ExchangeTransferList>
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
> {
const url = new URL(`aml/${auth.id}/transfers-debit`, this.baseUrl);
@@ -1383,10 +1402,10 @@ export class TalerExchangeHttpClient {
): Promise<
| OperationOk<ExchangeTransferList>
| OperationFail<
- | HttpStatusCode.Forbidden
- | HttpStatusCode.NotFound
- | HttpStatusCode.Conflict
- >
+ | HttpStatusCode.Forbidden
+ | HttpStatusCode.NotFound
+ | HttpStatusCode.Conflict
+ >
> {
const url = new URL(`aml/${auth.id}/transfers-kycauth`, this.baseUrl);