commit 9fab59bfda03a8c32bcaef131cd84c8c6cb634f6
parent da2a8b0bafacedae5a925d2ddb4ab541910386cd
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 11:41:47 +0200
util: give the corebank token listing its own type
One TokenInfo served two endpoints with incompatible bodies; it matched the
merchant shape, so every conforming corebank response failed to decode.
Diffstat:
2 files changed, 69 insertions(+), 4 deletions(-)
diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts
@@ -28,11 +28,11 @@ import {
OperationOk,
PaginationParams,
TalerErrorCode,
- TokenInfos,
+ BankTokenInfos,
TokenRequest,
UserAndToken,
carefullyParseConfig,
- codecForTokenInfoList,
+ codecForBankTokenInfoList,
codecForTokenSuccessResponse,
opKnownAlternativeHttpFailure,
opKnownHttpFailure,
@@ -248,7 +248,7 @@ export class TalerCoreBankHttpClient {
token: AccessToken,
pagination?: PaginationParams,
): Promise<
- | OperationOk<TokenInfos>
+ | OperationOk<BankTokenInfos>
| OperationFail<
| HttpStatusCode.Unauthorized
| HttpStatusCode.Forbidden
@@ -265,7 +265,7 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
case HttpStatusCode.Ok:
- return opSuccessFromHttp(resp, codecForTokenInfoList());
+ return opSuccessFromHttp(resp, codecForBankTokenInfoList());
case HttpStatusCode.NoContent:
return opFixedSuccess(resp, { tokens: [] });
case HttpStatusCode.Unauthorized:
diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts
@@ -392,6 +392,71 @@ export interface TokenSuccessResponse {
access_token: AccessToken;
}
+/**
+ * Response of the corebank `GET /accounts/$USERNAME/tokens`.
+ *
+ * The merchant backend answers its own token listing with a different object;
+ * see {@link TokenInfos}.
+ */
+export interface BankTokenInfos {
+ tokens: BankTokenInfo[];
+}
+
+export interface BankTokenInfo {
+ // Time when the token was created.
+ creation_time: Timestamp;
+
+ // Expiration determined by the server.
+ expiration: Timestamp;
+
+ // Scope for the token. Deliberately not narrowed to BankTokenScope: a
+ // listing must not fail to decode because the server knows a scope this
+ // client does not. Requests are narrowed, see TokenRequest.
+ scope: string;
+
+ // Is the token refreshable into a new token during its
+ // validity?
+ // Refreshable tokens effectively provide indefinite
+ // access if they are refreshed in time.
+ refreshable: boolean;
+
+ // Optional token description
+ description?: string;
+
+ // Time when the token was last used.
+ last_access: Timestamp;
+
+ // ID identifying the token.
+ token_id: Integer;
+
+ /**
+ * @deprecated since corebank protocol v9, use token_id
+ */
+ row_id?: Integer;
+}
+
+export const codecForBankTokenInfo = (): Codec<BankTokenInfo> =>
+ buildCodecForObject<BankTokenInfo>()
+ .property("creation_time", codecForTimestamp)
+ .property("expiration", codecForTimestamp)
+ .property("scope", codecForString())
+ .property("refreshable", codecForBoolean())
+ .property("description", codecOptional(codecForString()))
+ .property("last_access", codecForTimestamp)
+ .property("token_id", codecForNumber())
+ .property("row_id", codecOptional(codecForNumber()))
+ .build("BankTokenInfo");
+
+export const codecForBankTokenInfoList = (): Codec<BankTokenInfos> =>
+ buildCodecForObject<BankTokenInfos>()
+ .property("tokens", codecForList(codecForBankTokenInfo()))
+ .build("BankTokenInfoList");
+
+/**
+ * Response of the merchant `GET [/instances/$INSTANCE]/private/tokens`.
+ *
+ * The corebank listing has a different shape; see {@link BankTokenInfos}.
+ */
export interface TokenInfos {
tokens: TokenInfo[];
}