taler-typescript-core

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

commit 1223268bdd21e947a327fc81056f17a504a55466
parent 1516e6ea13de77263243e21c65a0c92b9211708e
Author: Florian Dold <florian@dold.me>
Date:   Tue, 25 Feb 2025 14:42:37 +0100

util: get rid of separate auth client

Auth is part of each service client

Diffstat:
Mpackages/bank-ui/src/Routing.tsx | 30+++++++++++++++---------------
Mpackages/bank-ui/src/pages/BankFrame.tsx | 5+++--
Mpackages/bank-ui/src/pages/LoginForm.tsx | 9+++++----
Mpackages/merchant-backoffice-ui/src/paths/admin/create/index.tsx | 18++++++++----------
Mpackages/merchant-backoffice-ui/src/paths/instance/token/index.tsx | 15++++++---------
Mpackages/merchant-backoffice-ui/src/paths/login/index.tsx | 2+-
Mpackages/taler-harness/src/index.ts | 16+++-------------
Dpackages/taler-util/src/http-client/authentication.ts | 165-------------------------------------------------------------------------------
Mpackages/taler-util/src/http-client/bank-core.ts | 30++++++++++++++++++++++--------
Mpackages/taler-util/src/http-client/merchant.ts | 11++++++++++-
Mpackages/taler-util/src/index.ts | 6+-----
Mpackages/web-util/src/context/activity.ts | 3---
Mpackages/web-util/src/context/bank-api.ts | 7-------
Mpackages/web-util/src/context/merchant-api.ts | 6------
14 files changed, 74 insertions(+), 249 deletions(-)

diff --git a/packages/bank-ui/src/Routing.tsx b/packages/bank-ui/src/Routing.tsx @@ -38,7 +38,7 @@ import { useEffect } from "preact/hooks"; import { useSessionState } from "./hooks/session.js"; import { AccountPage } from "./pages/AccountPage/index.js"; import { BankFrame } from "./pages/BankFrame.js"; -import { SESSION_DURATION, LoginForm } from "./pages/LoginForm.js"; +import { LoginForm, SESSION_DURATION } from "./pages/LoginForm.js"; import { PublicHistoriesPage } from "./pages/PublicHistoriesPage.js"; import { RegistrationPage } from "./pages/RegistrationPage.js"; import { ShowNotifications } from "./pages/ShowNotifications.js"; @@ -73,7 +73,7 @@ export function Routing(): VNode { }; const { - lib: { auth: authenticator }, + lib: { bank }, } = useBankCoreApiContext(); useEffect(() => { @@ -97,13 +97,15 @@ export function Routing(): VNode { 0, ); const timeoutId = setTimeout(async () => { - const result = await authenticator( + const result = await bank.createAccessTokenBasic( refreshSession.user, - ).createAccessTokenBearer_BANK(refreshSession.auth, { - scope: "readwrite", - duration: SESSION_DURATION, - refreshable: true, - }); + refreshSession.auth, + { + scope: "readwrite", + duration: SESSION_DURATION, + refreshable: true, + }, + ); if (result.type === "fail") { console.log(`could not refresh session ${result.case}`); return; @@ -175,13 +177,11 @@ function PublicRounting({ async function doAutomaticLogin(username: string, password: string) { await handleError(async () => { - const resp = await lib - .auth(username) - .createAccessTokenBasic(username, password, { - scope: "readwrite", - duration: SESSION_DURATION, - refreshable: true, - }); + const resp = await lib.bank.createAccessTokenBasic(username, password, { + scope: "readwrite", + duration: SESSION_DURATION, + refreshable: true, + }); if (resp.type === "ok") { onLoggedUser( username, diff --git a/packages/bank-ui/src/pages/BankFrame.tsx b/packages/bank-ui/src/pages/BankFrame.tsx @@ -69,7 +69,7 @@ export function BankFrame({ const [, , resetBankState] = useBankState(); const d = useBankCoreApiContext(); const config = d === undefined ? undefined : d.config; - const authenticator = d === undefined ? undefined : d.lib.auth; + const authenticator = d === undefined ? undefined : d.lib.bank; const [error, resetError] = useErrorBoundary(); useEffect(() => { @@ -108,7 +108,8 @@ export function BankFrame({ ? undefined : () => { if (session.state.status === "loggedIn" && authenticator) { - authenticator(session.state.username).deleteAccessToken( + // FIXME: This returns a promise, should await on it! + authenticator.deleteAccessToken(session.state.username, session.state.token, ); } diff --git a/packages/bank-ui/src/pages/LoginForm.tsx b/packages/bank-ui/src/pages/LoginForm.tsx @@ -30,7 +30,7 @@ import { useTranslationContext, } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; -import { useEffect, useRef, useState } from "preact/hooks"; +import { useState } from "preact/hooks"; import { useSessionState } from "../hooks/session.js"; import { undefinedIfEmpty } from "../utils.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; @@ -69,7 +69,7 @@ export function LoginForm({ const [password, setPassword] = useState<string | undefined>(); const { i18n } = useTranslationContext(); const { - lib: { auth: authenticator }, + lib: { bank: authenticator }, } = useBankCoreApiContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); const { config } = useBankCoreApiContext(); @@ -85,7 +85,8 @@ export function LoginForm({ async function doLogout() { if (sessionState) { - authenticator(sessionState.username).deleteAccessToken( + authenticator.deleteAccessToken( + sessionState.username, sessionState.token, ); } @@ -97,7 +98,7 @@ export function LoginForm({ ? undefined : withErrorHandler( async () => - authenticator(username).createAccessTokenBasic(username, password, { + authenticator.createAccessTokenBasic(username, password, { scope: "readwrite", duration: SESSION_DURATION, refreshable: true, diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx @@ -59,16 +59,13 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode { } if (d.auth.token) { //if auth has been updated, request a new access token - const result = await lib.authenticate.createAccessTokenBearer( - d.auth.token, - { - scope: "write", - duration: { - d_us: "forever", - }, - refreshable: true, + const result = await lib.instance.createAuthTokenFromToken(d.auth.token, { + scope: "write", + duration: { + d_us: "forever", }, - ); + refreshable: true, + }); if (result.type === "ok") { const { token } = result.body; logIn(token); @@ -79,7 +76,8 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode { setNotif({ message: i18n.str`Failed to create instance`, type: "ERROR", - description: error instanceof Error ? error.message : String(error), + description: + error instanceof Error ? error.message : String(error), }); } }} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx @@ -114,16 +114,13 @@ export default function Token({ onChange, onCancel }: Props): VNode { }); } } - const resp = await lib.authenticate.createAccessTokenBearer( - newToken, - { - scope: "write", - duration: { - d_us: "forever", - }, - refreshable: true, + const resp = await lib.instance.createAuthTokenFromToken(newToken, { + scope: "write", + duration: { + d_us: "forever", }, - ); + refreshable: true, + }); if (resp.type === "ok") { logIn(resp.body.token); return onChange(); diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -48,7 +48,7 @@ export function LoginPage(_p: Props): VNode { const { i18n } = useTranslationContext(); async function doLoginImpl() { - const result = await lib.authenticate.createAccessTokenBearer( + const result = await lib.instance.createAuthTokenFromToken( createRFC8959AccessTokenEncoded(token), tokenRequest, ); diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts @@ -29,7 +29,6 @@ import { Logger, MerchantAuthMethod, PaytoString, - TalerAuthenticationHttpClient, TalerBankConversionHttpClient, TalerCoreBankHttpClient, TalerKycAml, @@ -201,7 +200,7 @@ testingCli const merchantBaseUrl = "https://backend.test.taler.net/instances/sandbox/"; const merchantApi = new TalerMerchantInstanceHttpClient(merchantBaseUrl); const tokResp = succeedOrThrow( - await merchantApi.createLoginToken("secret-token:sandbox", { + await merchantApi.createAuthTokenFromToken("secret-token:sandbox", { scope: "write", }), ); @@ -852,10 +851,6 @@ deploymentCli bank.getConversionInfoAPI().href, httpLib, ); - const bankAuth = new TalerAuthenticationHttpClient( - bank.getAuthenticationAPI(id).href, - httpLib, - ); const bc = await bank.getConfig(); if (bc.type === "fail") { @@ -882,12 +877,7 @@ deploymentCli let bankAdminToken: AccessToken | undefined; if (bankAdminPassword) { - const adminAuth = new TalerAuthenticationHttpClient( - bank.getAuthenticationAPI("admin").href, - httpLib, - ); - - const resp = await adminAuth.createAccessTokenBasic( + const resp = await bank.createAccessTokenBasic( "admin", bankAdminPassword, { @@ -1059,7 +1049,7 @@ deploymentCli logger.info("random password: ", randomPassword); let token: AccessToken; { - const resp = await bankAuth.createAccessTokenBasic(id, prevPassword, { + const resp = await bank.createAccessTokenBasic(id, prevPassword, { scope: "readwrite", duration: Duration.toTalerProtocolDuration( Duration.fromSpec({ minutes: 1 }), diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts @@ -1,165 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 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 - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { HttpStatusCode } from "../http-status-codes.js"; -import { - HttpRequestLibrary, - createPlatformHttpLib, - makeBasicAuthHeader, - readTalerErrorResponse, -} from "../http.js"; -import { LibtoolVersion } from "../libtool-version.js"; -import { - opEmptySuccess, - opKnownHttpFailure, - opSuccessFromHttp, - opUnknownFailure, -} from "../operation.js"; -import { - AccessToken, - TokenRequest, - codecForTokenSuccessResponse, - codecForTokenSuccessResponseMerchant, -} from "../types-taler-common.js"; -import { makeBearerTokenAuthHeader } from "./utils.js"; - -export class TalerAuthenticationHttpClient { - public readonly PROTOCOL_VERSION = "0:0:0"; - - httpLib: HttpRequestLibrary; - - constructor( - readonly baseUrl: string, - httpClient?: HttpRequestLibrary, - ) { - this.httpLib = httpClient ?? createPlatformHttpLib(); - } - - isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); - return compare?.compatible ?? false; - } - - /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token - * - * @returns - */ - async createAccessTokenBasic( - username: string, - password: string, - body: TokenRequest, - ) { - const url = new URL(`token`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - headers: { - Authorization: makeBasicAuthHeader(username, password), - }, - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForTokenSuccessResponse()); - //FIXME: missing in docs - case HttpStatusCode.Unauthorized: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - } - } - - /** - * FIXME: merge this with createAccessTokenBearer the protocol of both - * services need to reply the same - * - * @returns - */ - async createAccessTokenBearer_BANK(token: AccessToken, body: TokenRequest) { - const url = new URL(`token`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - headers: { - Authorization: makeBearerTokenAuthHeader(token), - }, - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForTokenSuccessResponse()); - //FIXME: missing in docs - case HttpStatusCode.Unauthorized: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - } - } - - /** - * - * @returns - */ - async createAccessTokenBearer(token: AccessToken, body: TokenRequest) { - const url = new URL(`token`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - headers: { - Authorization: makeBearerTokenAuthHeader(token), - }, - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForTokenSuccessResponseMerchant()); - //FIXME: missing in docs - case HttpStatusCode.Unauthorized: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - } - } - - async deleteAccessToken(token: AccessToken) { - const url = new URL(`token`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "DELETE", - headers: { - Authorization: makeBearerTokenAuthHeader(token), - }, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opEmptySuccess(resp); - //FIXME: missing in docs - case HttpStatusCode.NoContent: - return opEmptySuccess(resp); - //FIXME: missing in docs - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - } - } -} diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts @@ -152,6 +152,28 @@ export class TalerCoreBankHttpClient { } } + async deleteAccessToken(user: string, token: AccessToken) { + const url = new URL(`accounts/${user}/token`, this.baseUrl); + const resp = await this.httpLib.fetch(url.href, { + method: "DELETE", + headers: { + Authorization: makeBearerTokenAuthHeader(token), + }, + }); + switch (resp.status) { + case HttpStatusCode.Ok: + return opEmptySuccess(resp); + // FIXME: missing in docs + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + // FIXME: missing in docs + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + } + } + /** * https://docs.taler.net/core/api-corebank.html#config * @@ -1143,14 +1165,6 @@ export class TalerCoreBankHttpClient { * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token * */ - getAuthenticationAPI(username: string): URL { - return new URL(`accounts/${username}/`, this.baseUrl); - } - - /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token - * - */ getConversionInfoAPI(): URL { return new URL(`conversion-info/`, this.baseUrl); } diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -219,7 +219,16 @@ export class TalerMerchantInstanceHttpClient { // Auth // - async createLoginToken( + /** + * Create an auth token from a login token. + * + * See https://bugs.gnunet.org/view.php?id=9556 to explain + * this weirdness. + * + * In the future, we'll only create auth tokens from login + * credentials. + */ + async createAuthTokenFromToken( token: string, body: TokenRequest, ): Promise< diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts @@ -1,7 +1,4 @@ -import { TalerErrorCode } from "./taler-error-codes.js"; - -export { TalerErrorCode }; - +export { TalerErrorCode } from "./taler-error-codes.js"; export * from "./amounts.js"; export * from "./bank-api-client.js"; export * from "./base64.js"; @@ -12,7 +9,6 @@ export * from "./contract-terms.js"; export * from "./errors.js"; export { fnutil } from "./fnutils.js"; export * from "./helpers.js"; -export * from "./http-client/authentication.js"; export * from "./http-client/bank-conversion.js"; export * from "./http-client/bank-core.js"; export * from "./http-client/bank-integration.js"; diff --git a/packages/web-util/src/context/activity.ts b/packages/web-util/src/context/activity.ts @@ -17,7 +17,6 @@ import { ChallengerHttpClient, ObservabilityEvent, - TalerAuthenticationHttpClient, TalerBankConversionHttpClient, TalerCoreBankHttpClient, TalerExchangeHttpClient, @@ -64,7 +63,6 @@ export interface APIClient<T, C> { export interface MerchantLib { instance: TalerMerchantManagementHttpClient; - authenticate: TalerAuthenticationHttpClient; subInstanceApi: (instanceId: string) => MerchantLib; } @@ -75,7 +73,6 @@ export interface ExchangeLib { export interface BankLib { bank: TalerCoreBankHttpClient; conversion: TalerBankConversionHttpClient; - auth: (user: string) => TalerAuthenticationHttpClient; } export interface ChallengerLib { diff --git a/packages/web-util/src/context/bank-api.ts b/packages/web-util/src/context/bank-api.ts @@ -19,7 +19,6 @@ import { LibtoolVersion, ObservabilityEvent, ObservableHttpClientLibrary, - TalerAuthenticationHttpClient, TalerBankConversionCacheEviction, TalerBankConversionHttpClient, TalerCoreBankCacheEviction, @@ -183,11 +182,6 @@ function buildBankApiClient( httpLib, evictors.conversion, ); - const auth = (user: string) => - new TalerAuthenticationHttpClient( - bank.getAuthenticationAPI(user).href, - httpLib, - ); async function getRemoteConfig(): Promise<TalerCorebankApi.TalerCorebankConfigResponse> { const resp = await bank.getConfig(); @@ -209,7 +203,6 @@ function buildBankApiClient( lib: { bank, conversion, - auth, }, onActivity: tracker.subscribe, cancelRequest: httpLib.cancelRequest, diff --git a/packages/web-util/src/context/merchant-api.ts b/packages/web-util/src/context/merchant-api.ts @@ -19,7 +19,6 @@ import { LibtoolVersion, ObservabilityEvent, ObservableHttpClientLibrary, - TalerAuthenticationHttpClient, TalerError, TalerMerchantApi, TalerMerchantInstanceCacheEviction, @@ -182,10 +181,6 @@ function buildMerchantApiClient( httpLib, evictors.management, ); - const authenticate = new TalerAuthenticationHttpClient( - instance.getAuthenticationAPI().href, - httpLib, - ); function getSubInstanceAPI(instanceId: string): MerchantLib { const api = buildMerchantApiClient( @@ -212,7 +207,6 @@ function buildMerchantApiClient( VERSION: instance.PROTOCOL_VERSION, lib: { instance, - authenticate, subInstanceApi: getSubInstanceAPI, }, onActivity: tracker.subscribe,