taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit ec9ede6a90cb49f0d628c14bd60cb4795c78b7d1
parent df1fdbc5ecc4d18a9b5a28b066b1ac7098953a39
Author: Antoine A <>
Date:   Thu,  9 Apr 2026 10:22:35 +0200

common: clean auth header shared logic

Diffstat:
Mpackages/bank-ui/src/Routing.tsx | 4++--
Mpackages/bank-ui/src/hooks/regional.ts | 16++++++++++------
Mpackages/bank-ui/src/hooks/session.ts | 4++--
Mpackages/bank-ui/src/pages/LoginForm.tsx | 4++--
Mpackages/bank-ui/src/pages/admin/CreateNewAccount.tsx | 4++--
Mpackages/bank-ui/src/pages/regional/ConversionConfig.tsx | 4++--
Mpackages/taler-harness/src/harness/environments.ts | 6+++---
Mpackages/taler-harness/src/harness/harness.ts | 76+++++++++++++++++++++++++++++++++++-----------------------------------------
Mpackages/taler-harness/src/index.ts | 37++++++++++++++++++++++---------------
Mpackages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts | 4++--
Mpackages/taler-harness/src/integrationtests/test-libeufin-conversion.ts | 8+++++---
Mpackages/taler-harness/src/integrationtests/test-merchant-deposit-large.ts | 3++-
Mpackages/taler-harness/src/integrationtests/test-merchant-payto-reuse.ts | 3+--
Mpackages/taler-harness/src/integrationtests/test-prepared-transfer.ts | 5++---
Mpackages/taler-harness/src/integrationtests/test-wallet-deposit-large.ts | 3++-
Mpackages/taler-harness/src/integrationtests/test-wire-metadata.ts | 7+++----
Mpackages/taler-util/src/bank-api-client.ts | 32++++++++++++--------------------
Mpackages/taler-util/src/http-client/bank-conversion.ts | 37++++++++++++-------------------------
Mpackages/taler-util/src/http-client/bank-core.ts | 42++++++++----------------------------------
Mpackages/taler-util/src/http-client/bank-revenue.ts | 12++++--------
Mpackages/taler-util/src/http-client/bank-wire.ts | 34+++++++++-------------------------
Mpackages/taler-util/src/http-client/merchant.ts | 25++++++++++++++-----------
Mpackages/taler-util/src/http-client/utils.ts | 29+++++++++++------------------
Mpackages/taler-util/src/http-common.ts | 19+++----------------
24 files changed, 170 insertions(+), 248 deletions(-)

diff --git a/packages/bank-ui/src/Routing.tsx b/packages/bank-ui/src/Routing.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -138,7 +138,7 @@ function PublicRounting({ (username: string, password: string, challengeIds: string[]) => lib.bank.createAccessToken( username, - { type: "basic", password }, + { type: "basic", username, password }, tokenRequest, { challengeIds }, ), diff --git a/packages/bank-ui/src/hooks/regional.ts b/packages/bank-ui/src/hooks/regional.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -29,6 +29,7 @@ import { TalerCoreBankResultByMethod, TalerCorebankApi, TalerHttpError, + TokenAuth, assertUnreachable, opFixedSuccess } from "@gnu-taler/taler-util"; @@ -106,7 +107,9 @@ export function useConversionRateForUser( } = useBankCoreApiContext(); async function fetcher() { - return await conversionForUser(username).getRate(token); + return await conversionForUser(username).getRate( + token != null ? { type: "bearer", token } : undefined + ); } const { data, error } = useSWR< TalerBankConversionResultByMethod<"getRate">, @@ -142,28 +145,29 @@ function buildEstimatorWithTheBackend( | "cashout-rate-from-debit", ): EstimatorFunction { return async (amount, fee) => { + const auth: TokenAuth | undefined = token != null ? { type: "bearer", token } : undefined; let resp; switch (estimation) { case "cashin-rate-from-credit": { - resp = await conversion.getCashinRate(token, { + resp = await conversion.getCashinRate(auth, { credit: amount, }); break; } case "cashin-rate-from-debit": { - resp = await conversion.getCashinRate(token, { + resp = await conversion.getCashinRate(auth, { debit: amount, }); break; } case "cashout-rate-from-credit": { - resp = await conversion.getCashoutRate(token, { + resp = await conversion.getCashoutRate(auth, { credit: amount, }); break; } case "cashout-rate-from-debit": { - resp = await conversion.getCashoutRate(token, { + resp = await conversion.getCashoutRate(auth, { debit: amount, }); break; diff --git a/packages/bank-ui/src/hooks/session.ts b/packages/bank-ui/src/hooks/session.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -219,7 +219,7 @@ export function useRefreshSessionBeforeExpires() { const timeoutId = setTimeout(async () => { const result = await bank.createAccessToken( refreshSession.username, - { type: "bearer", accessToken: refreshSession.token }, + { type: "bearer", token: refreshSession.token }, { scope: "readwrite", duration: SESSION_DURATION, diff --git a/packages/bank-ui/src/pages/LoginForm.tsx b/packages/bank-ui/src/pages/LoginForm.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -113,7 +113,7 @@ export function LoginForm({ (username: string, password: string, challengeIds: string[]) => api.createAccessToken( username, - { type: "basic", password }, + { type: "basic", username, password }, tokenRequest, { challengeIds }, ), diff --git a/packages/bank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/bank-ui/src/pages/admin/CreateNewAccount.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -62,7 +62,7 @@ export function CreateNewAccount({ const create = safeFunctionHandler( i18n.str`create account`, api.createAccount.bind(api), - !submitAccount || !token ? undefined : [token, submitAccount], + !submitAccount || !token ? undefined : [{ type: "bearer", token }, submitAccount], ); create.onSuccess = (success, token, account) => { notifyInfo(i18n.str`Account created with password "${account.password}".`); diff --git a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -231,7 +231,7 @@ function useComponentState({ conversion.updateConversionRate.bind(conversion), !creds || status.status === "fail" ? undefined - : [creds.token, status.result.conv], + : [{ type: "bearer", token: creds.token }, status.result.conv], ); update.onSuccess = () => { diff --git a/packages/taler-harness/src/harness/environments.ts b/packages/taler-harness/src/harness/environments.ts @@ -1000,10 +1000,9 @@ export async function withdrawViaBankV4( bank: BankService; amount: AmountString | string; restrictAge?: number; - bankAdminTok: AccessToken; }, ): Promise<WithdrawViaBankResult> { - const { walletClient: wallet, exchange, amount, bank, bankAdminTok } = p; + const { walletClient: wallet, exchange, amount, bank } = p; const bankClient = new TalerCoreBankHttpClient(bank.corebankApiBaseUrl); @@ -1011,7 +1010,7 @@ export async function withdrawViaBankV4( const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase(); const user = succeedOrThrow( - await bankClient.createAccount(bankAdminTok, { + await bankClient.createAccount(bank.getAdminAuth(), { name: username, password: password, username: username, @@ -1023,6 +1022,7 @@ export async function withdrawViaBankV4( username, { type: "basic", + username, password, }, { diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts @@ -33,7 +33,6 @@ import { ConfigSources, Configuration, CoreApiResponse, - Credentials, Duration, EddsaKeyPair, InstanceAuthConfigurationMessage, @@ -53,6 +52,7 @@ import { TalerMerchantManagementHttpClient, TalerProtocolDuration, TalerWireGatewayHttpClient, + TokenAuth, Transaction, TransactionIdStr, WalletNotification, @@ -677,16 +677,8 @@ class BankServiceBase { protected globalTestState: GlobalTestState, protected bankConfig: BankConfig, protected configFile: string, - ) { } - getAdminAuth(): BasicAuth { - // Bank admin PW is brutally hard-coded in tests right now. - return { - type: "basic", - username: "admin", - password: "admin-password", - }; - } + ) { } } export type RestrictionFlag = "credit-restriction" | "debit-restriction"; @@ -750,9 +742,12 @@ export class FakebankService return new FakebankService(gc, bc, cfgFilename); } - async getAdminTok(): Promise<AccessToken> { - // fakebank does not support token auth. - return "secret-token:admin" as AccessToken; + getAdminAuth(): BasicOrTokenAuth { + return { + type: "basic", + username: "admin", + password: "admin-password", + } } static fromExistingConfig( @@ -923,11 +918,6 @@ export class LibeufinNexusService { } } -export const harnessBankAdminCreds: Credentials = { - type: "basic", - password: "admin-password", -}; - /** * Implementation of the bank service using the libeufin-bank implementation. */ @@ -938,6 +928,8 @@ export class LibeufinBankService http = createPlatformHttpLib({ enableThrottling: false }); + private adminAuth: TokenAuth; + // We store "created" accounts during setup and // register them after startup. private accounts: { @@ -994,14 +986,8 @@ export class LibeufinBankService return new LibeufinBankService(gc, bc, cfgFilename); } - async getAdminTok(): Promise<AccessToken> { - const bankClient = new TalerCoreBankHttpClient(this.corebankApiBaseUrl); - const bankAdminTok = succeedOrThrow( - await bankClient.createAccessToken("admin", harnessBankAdminCreds, { - scope: "readwrite", - }), - ).access_token; - return bankAdminTok; + getAdminAuth(): BasicOrTokenAuth { + return this.adminAuth } static fromExistingConfig( @@ -1113,21 +1099,32 @@ export class LibeufinBankService "libeufin-bank-httpd", ); await this.pingUntilAvailable(); + const bankClient = new TalerCoreBankHttpClient(this.corebankApiBaseUrl); // Check version - { - const bankClient = new TalerCoreBankHttpClient(this.corebankApiBaseUrl); - // This would fail/throw if the version doesn't match. - const resp = await bankClient.getConfig(); - this.globalTestState.assertTrue(resp.type === "ok"); - } + succeedOrThrow(await bankClient.getConfig()); + // Create admin token + const token = succeedOrThrow( + await bankClient.createAccessToken("admin", { + type: "basic", + username: "admin", + password: "admin-password" + }, { + scope: "readwrite", + }), + ); + this.adminAuth = { + type: "bearer", + token: token.access_token + }; // Register accounts - { - // FIXME: This still uses the old-style client. - const bankClient = new TalerCorebankApiClient(this.corebankApiBaseUrl); - for (const acc of this.accounts) { - await bankClient.registerAccount(acc.accountName, acc.accountPassword); - } + for (const acc of this.accounts) { + succeedOrThrow(await bankClient.createAccount(this.adminAuth, { + username: acc.accountName, + password: acc.accountPassword, + name: acc.accountName + })) } + } async pingUntilAvailable(): Promise<void> { @@ -1157,9 +1154,6 @@ export interface BankServiceHandle { pingUntilAvailable(): Promise<void>; stop(): Promise<void>; getAdminAuth(): BasicOrTokenAuth; - - getAdminTok(): Promise<AccessToken>; - changeConfig(f: (config: Configuration) => void): void; } diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2019 Taler Systems S.A. + (C) 2019, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -40,6 +40,7 @@ import { TalerMerchantInstanceHttpClient, TalerMerchantManagementHttpClient, TalerProtocolTimestamp, + TokenAuth, TransactionsResponse, createRFC8959AccessTokenEncoded, createRFC8959AccessTokenPlain, @@ -909,7 +910,7 @@ deploymentCli const bc = succeedOrThrow(await bank.getConfig()); const mc = succeedOrThrow(await merchantManager.getConfig()); - let bankAdminToken: AccessToken | undefined; + let bankAdminAuth: TokenAuth | undefined; if (bankAdminPassword) { const resp = await bank.createAccessTokenBasic( "admin", @@ -926,9 +927,15 @@ deploymentCli logger.error(`could not get bank admin token from password.`); return; } - bankAdminToken = resp.body.access_token; - } else { - bankAdminToken = bankAdminTokenArg; + bankAdminAuth = { + type: "bearer", + token: resp.body.access_token + } + } else if (bankAdminTokenArg != null) { + bankAdminAuth = { + type: "bearer", + token: bankAdminTokenArg + } } let merchantAdminToken: AccessToken | undefined; @@ -960,16 +967,16 @@ deploymentCli */ let accountPayto: PaytoString; { - const resp = await bank.createAccount(bankAdminToken, { + const resp = await bank.createAccount(bankAdminAuth, { name: name, password: password, username: id, contact_data: email || phone ? { - email: email, - phone: phone, - } + email: email, + phone: phone, + } : undefined, }); @@ -1332,10 +1339,10 @@ deploymentCli credit_facade_credentials: bankUser && bankPassword ? { - type: "basic", - username: bankUser, - password: bankPassword, - } + type: "basic", + username: bankUser, + password: bankPassword, + } : undefined, }); if (createAccountResp.type != "ok") { @@ -1993,8 +2000,8 @@ merchantCli const scope = args.token.scope ?? "all"; const duration = args.token.duration ? Duration.toTalerProtocolDuration( - Duration.fromPrettyString(args.token.duration), - ) + Duration.fromPrettyString(args.token.duration), + ) : undefined; const tokResp = await merchantApi.createAccessToken(instance, password, { scope: scope as LoginTokenScope, diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2024 Taler Systems S.A. + (C) 2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -174,7 +174,7 @@ export async function runKycMerchantDepositRewriteTest(t: GlobalTestState) { logger.info("We need to wire some money to ", exchangeWireTarget); succeedOrThrow( - await bankApi.createAccount(merchantAdminAccessToken, { + await bankApi.createAccount(bank.getAdminAuth(), { name: "merchant-default", password: "merchant-default", username: "merchant-default", diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-conversion.ts b/packages/taler-harness/src/integrationtests/test-libeufin-conversion.ts @@ -177,6 +177,7 @@ export async function runLibeufinConversionTest(t: GlobalTestState) { adminUser, { type: "basic", + username: "exchange", password: adminPassword, }, { @@ -198,14 +199,15 @@ export async function runLibeufinConversionTest(t: GlobalTestState) { noReset: true, }); - const adminTok = adminTokResp.access_token; - const cc = new TalerBankConversionHttpClient( bank.baseUrl + `conversion-info/`, ); succeedOrThrow( - await cc.updateConversionRate(adminTok, { + await cc.updateConversionRate({ + type: "bearer", + token: adminTokResp.access_token + }, { cashin_fee: "TESTKUDOS:0", cashin_min_amount: "FOO:5", cashin_ratio: "1", diff --git a/packages/taler-harness/src/integrationtests/test-merchant-deposit-large.ts b/packages/taler-harness/src/integrationtests/test-merchant-deposit-large.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2024 Taler Systems S.A. + (C) 2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -175,6 +175,7 @@ export async function runMerchantDepositLargeTest(t: GlobalTestState) { "exchange", { type: "basic", + username: "exchange", password: "mypw-password", }, { diff --git a/packages/taler-harness/src/integrationtests/test-merchant-payto-reuse.ts b/packages/taler-harness/src/integrationtests/test-merchant-payto-reuse.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2024 Taler Systems S.A. + (C) 2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -194,7 +194,6 @@ export async function runMerchantPaytoReuseTest(t: GlobalTestState) { bank, amount: "TESTKUDOS:30", exchange, - bankAdminTok: await bank.getAdminTok(), }); await wres.withdrawalFinishedCond; diff --git a/packages/taler-harness/src/integrationtests/test-prepared-transfer.ts b/packages/taler-harness/src/integrationtests/test-prepared-transfer.ts @@ -63,9 +63,8 @@ export async function runPreparedTransferTest(t: GlobalTestState) { const wireGatewayApiClient = new TalerWireGatewayHttpClient( `${bank.corebankApiBaseUrl}accounts/${exchangeBankUsername}/taler-wire-gateway/`, ); - const bankAdminTok = await bank.getAdminTok(); const merchant = succeedOrThrow( - await bankClient.createAccount(bankAdminTok, { + await bankClient.createAccount(bank.getAdminAuth(), { name: "merchant-default", password: "merchant-default-pw", username: "merchant-default", @@ -73,7 +72,7 @@ export async function runPreparedTransferTest(t: GlobalTestState) { ); succeedOrThrow( - await bankClient.createAccount(bankAdminTok, { + await bankClient.createAccount(bank.getAdminAuth(), { name: receiverName, password: exchangeBankPassword, username: exchangeBankUsername, diff --git a/packages/taler-harness/src/integrationtests/test-wallet-deposit-large.ts b/packages/taler-harness/src/integrationtests/test-wallet-deposit-large.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2024 Taler Systems S.A. + (C) 2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -103,6 +103,7 @@ export async function runWalletDepositLargeTest(t: GlobalTestState) { "exchange", { type: "basic", + username: "exchange", password: "mypw-password", }, { diff --git a/packages/taler-harness/src/integrationtests/test-wire-metadata.ts b/packages/taler-harness/src/integrationtests/test-wire-metadata.ts @@ -99,9 +99,8 @@ export async function runWireMetadataTest(t: GlobalTestState) { await bank.start(); const bankClient = new TalerCoreBankHttpClient(bank.corebankApiBaseUrl); - const bankAdminTok = await bank.getAdminTok(); - await bankClient.createAccount(bankAdminTok, { + await bankClient.createAccount(bank.getAdminAuth(), { name: receiverName, password: exchangeBankPassword, username: exchangeBankUsername, @@ -175,7 +174,7 @@ export async function runWireMetadataTest(t: GlobalTestState) { ).access_token; succeedOrThrow( - await bankClient.createAccount(bankAdminTok, { + await bankClient.createAccount(bank.getAdminAuth(), { name: "minst1", password: "minst1-password", username: "minst1", @@ -197,7 +196,6 @@ export async function runWireMetadataTest(t: GlobalTestState) { const wres = await withdrawViaBankV4(t, { walletClient, - bankAdminTok, amount: "TESTKUDOS:20", bank, exchange, @@ -273,6 +271,7 @@ export async function runWireMetadataTest(t: GlobalTestState) { "exchange", { type: "basic", + username: "exchange", password: "mypw-password", }, { diff --git a/packages/taler-util/src/bank-api-client.ts b/packages/taler-util/src/bank-api-client.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -48,6 +48,7 @@ import { HttpRequestLibrary, readSuccessResponseJsonOrThrow, } from "@gnu-taler/taler-util/http"; +import { authHeaders } from "./http-client/utils.js"; const logger = new Logger("bank-api-client.ts"); @@ -74,15 +75,6 @@ export interface WithdrawalOperationInfo { taler_withdraw_uri: string; } -/** - * Helper function to generate the "Authorization" HTTP header. - */ -function makeBasicAuthHeader(username: string, password: string): string { - const auth = `${username}:${password}`; - const authEncoded: string = base64FromArrayBuffer(stringToBytes(auth)); - return `Basic ${authEncoded}`; -} - const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> => buildCodecForObject<WithdrawalOperationInfo>() .property("withdrawal_id", codecForString()) @@ -126,13 +118,11 @@ export class TalerCorebankApiClient { if (!this.args.auth) { return {}; } - const authHeaderValue = makeBasicAuthHeader( - this.args.auth.username, - this.args.auth.password, - ); - return { - Authorization: authHeaderValue, - }; + return authHeaders({ + type: "basic", + username: this.args.auth.username, + password: this.args.auth.password, + }) } async getAccountBalance( @@ -244,9 +234,11 @@ export class TalerCorebankApiClient { // FIXME: Corebank should directly return this info! const infoUrl = new URL(`accounts/${username}`, this.baseUrl); const infoResp = await this.httpLib.fetch(infoUrl.href, { - headers: { - Authorization: makeBasicAuthHeader(username, password), - }, + headers: authHeaders({ + type: "basic", + username, + password + }) }); // FIXME: Validate! const acctInfo: TalerCorebankApi.AccountData = diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -40,12 +40,13 @@ import { codecForConversionBankConfig, codecForConversionRate, } from "../types-taler-bank-conversion.js"; -import { AccessToken } from "../types-taler-common.js"; import { codecForTalerErrorDetail } from "../types-taler-wallet.js"; import { + authHeaders, + BasicOrTokenAuth, CacheEvictor, - makeBearerTokenAuthHeader, nullEvictor, + TokenAuth, } from "./utils.js"; export type TalerBankConversionResultByMethod< @@ -110,15 +111,11 @@ export class TalerBankConversionHttpClient { * https://docs.taler.net/core/api-bank-conversion-info.html#get--rate * */ - async getRate(auth: AccessToken | undefined) { + async getRate(auth: TokenAuth | undefined) { const url = new URL(`rate`, this.baseUrl); - const headers: Record<string, string> = {}; - if (auth) { - headers.Authorization = makeBearerTokenAuthHeader(auth); - } const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers, + headers: authHeaders(auth) }); switch (resp.status) { case HttpStatusCode.Ok: @@ -135,7 +132,7 @@ export class TalerBankConversionHttpClient { * */ async getCashinRate( - auth: AccessToken | undefined, + auth: TokenAuth | undefined, conversion: { debit?: AmountJson; credit?: AmountJson }, ) { const url = new URL(`cashin-rate`, this.baseUrl); @@ -148,13 +145,9 @@ export class TalerBankConversionHttpClient { Amounts.stringify(conversion.credit), ); } - const headers: Record<string, string> = {}; - if (auth) { - headers.Authorization = makeBearerTokenAuthHeader(auth); - } const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers, + headers: authHeaders(auth) }); switch (resp.status) { case HttpStatusCode.Ok: @@ -187,7 +180,7 @@ export class TalerBankConversionHttpClient { * */ async getCashoutRate( - auth: AccessToken | undefined, + auth: TokenAuth | undefined, conversion: { debit?: AmountJson; credit?: AmountJson; @@ -203,13 +196,9 @@ export class TalerBankConversionHttpClient { Amounts.stringify(conversion.credit), ); } - const headers: Record<string, string> = {}; - if (auth) { - headers.Authorization = makeBearerTokenAuthHeader(auth); - } const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers, + headers: authHeaders(auth) }); switch (resp.status) { case HttpStatusCode.Ok: @@ -241,13 +230,11 @@ export class TalerBankConversionHttpClient { * https://docs.taler.net/core/api-bank-conversion-info.html#post--conversion-rate * */ - async updateConversionRate(auth: AccessToken, body: ConversionRate) { + async updateConversionRate(auth: BasicOrTokenAuth | undefined, body: ConversionRate) { const url = new URL(`conversion-rate`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", - headers: { - Authorization: makeBearerTokenAuthHeader(auth), - }, + headers: authHeaders(auth), body, }); switch (resp.status) { diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -17,6 +17,7 @@ import { AbsoluteTime, AccessToken, + BasicOrTokenAuth, ChallengeResponse, HttpStatusCode, LibtoolVersion, @@ -28,7 +29,6 @@ import { TalerErrorCode, TokenRequest, UserAndToken, - assertUnreachable, carefullyParseConfig, codecForTokenInfoList, codecForTokenSuccessResponse, @@ -90,7 +90,7 @@ import { CacheEvictor, addLongPollingParam, addPaginationParams, - makeBasicAuthHeader, + authHeaders, makeBearerTokenAuthHeader, nullEvictor, } from "./utils.js"; @@ -117,15 +117,6 @@ export enum TalerCoreBankCacheEviction { DELETE_CONVERSION_RATE_CLASS, } -export type Credentials = BasicCredentials | BearerCredentials; -export type BasicCredentials = { - type: "basic"; - password: string; -}; -export type BearerCredentials = { - type: "bearer"; - accessToken: AccessToken; -}; /** * Protocol version spoken with the core bank. * @@ -160,26 +151,13 @@ export class TalerCoreBankHttpClient { */ async createAccessToken( username: string, - cred: Credentials, + auth: BasicOrTokenAuth, body: TokenRequest, params: { challengeIds?: string[] } = {}, ) { const url = new URL(`accounts/${username}/token`, this.baseUrl); - const headers: Record<string, string> = {}; - switch (cred.type) { - case "basic": { - headers.Authorization = makeBasicAuthHeader(username, cred.password); - break; - } - case "bearer": { - headers.Authorization = makeBearerTokenAuthHeader(cred.accessToken); - break; - } - default: { - assertUnreachable(cred); - } - } + const headers = authHeaders(auth) if (params.challengeIds && params.challengeIds.length > 0) { headers["Taler-Challenge-Ids"] = params.challengeIds.join(", "); } @@ -226,7 +204,7 @@ export class TalerCoreBankHttpClient { password: string, body: TokenRequest, ) { - return this.createAccessToken(username, { type: "basic", password }, body); + return this.createAccessToken(username, { type: "basic", username, password }, body); } /** @@ -309,7 +287,7 @@ export class TalerCoreBankHttpClient { * */ async createAccount( - auth: AccessToken | undefined, + auth: BasicOrTokenAuth | undefined, body: RegisterAccountRequest, ): Promise< | OperationOk<RegisterAccountResponse> @@ -329,14 +307,10 @@ export class TalerCoreBankHttpClient { | OperationFail<TalerErrorCode.BANK_CONVERSION_RATE_CLASS_UNKNOWN> > { const url = new URL(`accounts`, this.baseUrl); - const headers: Record<string, string> = {}; - if (auth) { - headers.Authorization = makeBearerTokenAuthHeader(auth); - } const resp = await this.httpLib.fetch(url.href, { method: "POST", body, - headers: headers, + headers: authHeaders(auth), }); switch (resp.status) { case HttpStatusCode.Ok: { diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -35,8 +35,8 @@ import { import { addLongPollingParam, addPaginationParams, + authHeaders, BasicOrTokenAuth, - createAuthorizationHeader, } from "./utils.js"; export type TalerBankRevenueResultByMethod< @@ -75,9 +75,7 @@ export class TalerRevenueHttpClient { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(auth), - }, + headers: authHeaders(auth), }); switch (resp.status) { case HttpStatusCode.Ok: @@ -109,9 +107,7 @@ export class TalerRevenueHttpClient { addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(auth), - }, + headers: authHeaders(auth), }); switch (resp.status) { case HttpStatusCode.Ok: diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts @@ -35,8 +35,8 @@ import { import { addLongPollingParam, addPaginationParams, + authHeaders, BasicOrTokenAuth, - createAuthorizationHeader, } from "./utils.js"; import { LongPollParams, PaginationParams } from "../types-taler-common.js"; @@ -114,9 +114,7 @@ export class TalerWireGatewayHttpClient { const url = new URL(`transfer`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), body: req.body, }); switch (resp.status) { @@ -161,9 +159,7 @@ export class TalerWireGatewayHttpClient { addPaginationParams(url, req.params); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), }); switch (resp.status) { case HttpStatusCode.Ok: @@ -190,9 +186,7 @@ export class TalerWireGatewayHttpClient { const url = new URL(`transfers/${req.rowId}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), }); switch (resp.status) { case HttpStatusCode.Ok: @@ -219,9 +213,7 @@ export class TalerWireGatewayHttpClient { addLongPollingParam(url, req.params); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), }); switch (resp.status) { case HttpStatusCode.Ok: @@ -253,9 +245,7 @@ export class TalerWireGatewayHttpClient { addLongPollingParam(url, req.params); const resp = await this.httpLib.fetch(url.href, { method: "GET", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), }); switch (resp.status) { case HttpStatusCode.Ok: @@ -285,9 +275,7 @@ export class TalerWireGatewayHttpClient { const url = new URL(`admin/add-incoming`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), body: req.body, }); switch (resp.status) { @@ -320,9 +308,7 @@ export class TalerWireGatewayHttpClient { const url = new URL(`admin/add-kycauth`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), body: req.body, }); switch (resp.status) { @@ -342,9 +328,7 @@ export class TalerWireGatewayHttpClient { const url = new URL(`admin/add-mapped`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", - headers: { - Authorization: createAuthorizationHeader(req.auth), - }, + headers: authHeaders(req.auth), body: req.body, }); switch (resp.status) { diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -110,7 +110,7 @@ import { opSuccessFromHttp } from "../operation.js"; import { CacheEvictor, addPaginationParams, - makeBasicAuthHeader, + authHeaders, makeBearerTokenAuthHeader, nullEvictor, } from "./utils.js"; @@ -256,14 +256,17 @@ export class TalerMerchantInstanceHttpClient { | OperationFail<HttpStatusCode.NotFound> | OperationOk<TalerMerchantApi.LoginTokenSuccessResponse> | OperationAlternative< - HttpStatusCode.Accepted, - TalerMerchantApi.ChallengeResponse - > + HttpStatusCode.Accepted, + TalerMerchantApi.ChallengeResponse + > | OperationFail<HttpStatusCode.Unauthorized> > { const url = new URL(`private/token`, this.baseUrl); - const headers: Record<string, string> = {}; - headers.Authorization = makeBasicAuthHeader(instance, password); + const headers = authHeaders({ + type: "basic", + username: instance, + password + }) if (params.challengeIds && params.challengeIds.length > 0) { headers["Taler-Challenge-Ids"] = params.challengeIds.join(", "); } @@ -895,7 +898,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NoContent: // FIXME: using opKnownHttpFailure is wrong here // we expect to read a body with the error description - return opKnownFailure(resp.status); + return opKnownFailure(resp.status); case HttpStatusCode.NotModified: return opKnownFailureWithBody(resp.status, { etag }); case HttpStatusCode.Unauthorized: // FIXME: missing in docs @@ -928,9 +931,9 @@ export class TalerMerchantInstanceHttpClient { | OperationOk<TalerMerchantApi.AccountAddResponse> | OperationFail<HttpStatusCode.NotFound> | OperationAlternative< - HttpStatusCode.Accepted, - TalerMerchantApi.ChallengeResponse - > + HttpStatusCode.Accepted, + TalerMerchantApi.ChallengeResponse + > | OperationFail<HttpStatusCode.Unauthorized> | OperationFail<HttpStatusCode.Conflict> > { diff --git a/packages/taler-util/src/http-client/utils.ts b/packages/taler-util/src/http-client/utils.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022-2024 Taler Systems S.A. + (C) 2022-2024, 2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,18 +26,6 @@ import { } from "../types-taler-common.js"; /** - * Helper function to generate the "Authorization" HTTP header. - */ -export function makeBasicAuthHeader( - username: string, - password: string, -): string { - const auth = `${username}:${password}`; - const authEncoded: string = base64FromArrayBuffer(stringToBytes(auth)); - return `Basic ${authEncoded}`; -} - -/** * rfc8959 * @param token * @returns @@ -59,17 +47,22 @@ export type TokenAuth = { token: AccessToken; }; -export function createAuthorizationHeader(auth?: BasicOrTokenAuth): string | undefined { - if (!auth) return undefined; +export function authHeaders(auth?: BasicOrTokenAuth): Record<string, string> { + if (!auth) return {}; switch (auth.type) { case "basic": { - return makeBasicAuthHeader(auth.username, auth.password); + const credentials = `${auth.username}:${auth.password}`; + const authEncoded: string = base64FromArrayBuffer(stringToBytes(credentials)); + return { + Authorization: `Basic ${authEncoded}` + } } case "bearer": { - return makeBearerTokenAuthHeader(auth.token); + return { + Authorization: makeBearerTokenAuthHeader(auth.token) + } } } - return undefined; } export function addPaginationParams(url: URL, pagination?: PaginationParams) { diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2023-2025 Taler Systems S.A. + (C) 2023-2026 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -16,13 +16,11 @@ SPDX-License-Identifier: AGPL3.0-or-later */ -import { base64FromArrayBuffer } from "./base64.js"; import { CancellationToken } from "./CancellationToken.js"; import { Codec } from "./codec.js"; import { makeErrorDetail, TalerError } from "./errors.js"; import { j2s } from "./helpers.js"; import { Logger } from "./logging.js"; -import { stringToBytes } from "./taler-crypto.js"; import { TalerErrorCode } from "./taler-error-codes.js"; import { AbsoluteTime, Duration } from "./time.js"; import { TalerErrorDetail } from "./types-taler-wallet.js"; @@ -538,16 +536,4 @@ export function getDefaultHeaders(method: string): Record<string, string> { headers["Accept"] = "application/json"; return headers; -} - -/** - * Helper function to generate the "Authorization" HTTP header. - */ -export function makeBasicAuthHeader( - username: string, - password: string, -): string { - const auth = `${username}:${password}`; - const authEncoded: string = base64FromArrayBuffer(stringToBytes(auth)); - return `Basic ${authEncoded}`; -} +} +\ No newline at end of file