From 0b2c03dc5e1060cd229aeafb84263f171b5a9788 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 25 Sep 2023 09:31:17 -0300 Subject: [PATCH] new libeufin api --- packages/demobank-ui/src/declaration.d.ts | 9 ++-- packages/demobank-ui/src/hooks/backend.ts | 43 +++++-------------- packages/demobank-ui/src/hooks/circuit.ts | 23 +++++----- .../src/hooks/useCredentialsChecker.ts | 12 +++--- .../src/pages/AccountPage/state.ts | 4 +- .../src/pages/AccountPage/views.tsx | 10 ++--- packages/demobank-ui/src/pages/BankFrame.tsx | 6 +-- packages/demobank-ui/src/pages/LoginForm.tsx | 8 ++-- .../src/pages/RegistrationPage.tsx | 12 +++++- .../demobank-ui/src/pages/admin/Account.tsx | 7 +-- .../src/pages/admin/RemoveAccount.tsx | 4 +- .../demobank-ui/src/pages/business/Home.tsx | 7 +-- packages/demobank-ui/src/stories.test.ts | 3 +- 13 files changed, 60 insertions(+), 88 deletions(-) diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts index a9573fbcd..0c083a939 100644 --- a/packages/demobank-ui/src/declaration.d.ts +++ b/packages/demobank-ui/src/declaration.d.ts @@ -319,11 +319,10 @@ namespace SandboxBackend { interface ListBankAccountsResponse { accounts: AccountMinimalData[]; } - // interface Balance { - // amount: Amount; - // credit_debit_indicator: "credit" | "debit"; - // } - type Balance = Amount + interface Balance { + amount: Amount; + credit_debit_indicator: "credit" | "debit"; + } interface AccountMinimalData { // Username username: string; diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts index f2be90f08..e6a3a1c0c 100644 --- a/packages/demobank-ui/src/hooks/backend.ts +++ b/packages/demobank-ui/src/hooks/backend.ts @@ -40,6 +40,7 @@ import { useCallback, useEffect, useState } from "preact/hooks"; import { useSWRConfig } from "swr"; import { useBackendContext } from "../context/backend.js"; import { bankUiSettings } from "../settings.js"; +import { AccessToken } from "./useCredentialsChecker.js"; /** * Has the information to reach and @@ -49,7 +50,7 @@ export type BackendState = LoggedIn | LoggedOut; export interface BackendCredentials { username: string; - password: string; + token: AccessToken; } interface LoggedIn extends BackendCredentials { @@ -64,7 +65,7 @@ export const codecForBackendStateLoggedIn = (): Codec => buildCodecForObject() .property("status", codecForConstString("loggedIn")) .property("username", codecForString()) - .property("password", codecForString()) + .property("token", codecForString() as Codec) .property("isUserAdministrator", codecForBoolean()) .build("BackendState.LoggedIn"); @@ -255,35 +256,11 @@ interface InvalidationResult { error: unknown; } -export function useCredentialsCheckerOld() { - const { request } = useApiContext(); - const baseUrl = getInitialBackendBaseURL(); - //check against account details endpoint - //while sandbox backend doesn't have a login endpoint - return async function testLogin( - username: string, - password: string, - ): Promise { - try { - await request(baseUrl, `access-api/accounts/${username}/`, { - basicAuth: { username, password }, - preventCache: true, - }); - return { valid: true }; - } catch (error) { - if (error instanceof RequestError) { - return { valid: false, requestError: true, cause: error.cause }; - } - return { valid: false, requestError: false, error }; - } - }; -} - export function useAuthenticatedBackend(): useBackendType { const { state } = useBackendContext(); const { request: requestHandler } = useApiContext(); - const creds = state.status === "loggedIn" ? state : undefined; + const creds = state.status === "loggedIn" ? state.token : undefined; const baseUrl = getInitialBackendBaseURL(); const request = useCallback( @@ -291,14 +268,14 @@ export function useAuthenticatedBackend(): useBackendType { path: string, options: RequestOptions = {}, ): Promise> { - return requestHandler(baseUrl, path, { basicAuth: creds, ...options }); + return requestHandler(baseUrl, path, { token: creds, ...options }); }, [baseUrl, creds], ); const fetcher = useCallback( function fetcherImpl(endpoint: string): Promise> { - return requestHandler(baseUrl, endpoint, { basicAuth: creds }); + return requestHandler(baseUrl, endpoint, { token: creds }); }, [baseUrl, creds], ); @@ -309,7 +286,7 @@ export function useAuthenticatedBackend(): useBackendType { number, ]): Promise> { return requestHandler(baseUrl, endpoint, { - basicAuth: creds, + token: creds, params: { delta: size, start: size * page }, }); }, @@ -321,7 +298,7 @@ export function useAuthenticatedBackend(): useBackendType { > { return Promise.all( endpoints.map((endpoint) => - requestHandler(baseUrl, endpoint, { basicAuth: creds }), + requestHandler(baseUrl, endpoint, { token: creds }), ), ); }, @@ -335,7 +312,7 @@ export function useAuthenticatedBackend(): useBackendType { string, ]): Promise> { return requestHandler(baseUrl, endpoint, { - basicAuth: creds, + token: creds, params: { page: page || 1, size }, }); }, @@ -347,7 +324,7 @@ export function useAuthenticatedBackend(): useBackendType { HttpResponseOk > { return requestHandler(baseUrl, endpoint, { - basicAuth: creds, + token: creds, params: { account }, }); }, diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts index 06557b77f..4ef80b055 100644 --- a/packages/demobank-ui/src/hooks/circuit.ts +++ b/packages/demobank-ui/src/hooks/circuit.ts @@ -33,6 +33,7 @@ import { // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import _useSWR, { SWRHook } from "swr"; import { AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { AccessToken } from "./useCredentialsChecker.js"; const useSWR = _useSWR as unknown as SWRHook; export function useAdminAccountAPI(): AdminAccountAPI { @@ -90,7 +91,8 @@ export function useAdminAccountAPI(): AdminAccountAPI { await mutateAll(/.*/); logIn({ username: account, - password: data.new_password, + //FIXME: change password api + token: data.new_password as AccessToken, }); } return res; @@ -215,14 +217,15 @@ export interface CircuitAccountAPI { async function getBusinessStatus( request: ReturnType["request"], - basicAuth: { username: string; password: string }, + username: string, + token: AccessToken, ): Promise { try { const url = getInitialBackendBaseURL(); const result = await request( url, - `circuit-api/accounts/${basicAuth.username}`, - { basicAuth }, + `circuit-api/accounts/${username}`, + { token }, ); return result.ok; } catch (error) { @@ -264,10 +267,10 @@ type CashoutEstimators = { export function useEstimator(): CashoutEstimators { const { state } = useBackendContext(); const { request } = useApiContext(); - const basicAuth = + const creds = state.status === "loggedOut" ? undefined - : { username: state.username, password: state.password }; + : state.token; return { estimateByCredit: async (amount, fee, rate) => { const zeroBalance = Amounts.zeroOfCurrency(fee.currency); @@ -282,7 +285,7 @@ export function useEstimator(): CashoutEstimators { url, `circuit-api/cashouts/estimates`, { - basicAuth, + token: creds, params: { amount_credit: Amounts.stringify(amount), }, @@ -313,7 +316,7 @@ export function useEstimator(): CashoutEstimators { url, `circuit-api/cashouts/estimates`, { - basicAuth, + token: creds, params: { amount_debit: Amounts.stringify(amount), }, @@ -339,11 +342,11 @@ export function useBusinessAccountFlag(): boolean | undefined { const creds = state.status === "loggedOut" ? undefined - : { username: state.username, password: state.password }; + : {user: state.username, token: state.token}; useEffect(() => { if (!creds) return; - getBusinessStatus(request, creds) + getBusinessStatus(request, creds.user, creds.token) .then((result) => { setIsBusiness(result); }) diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts index 05954348f..f66a4a7c6 100644 --- a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts +++ b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts @@ -23,13 +23,13 @@ export function useCredentialsChecker() { const response = await request(baseUrl, `accounts/${username}/token`, { method: "POST", basicAuth: { - username: username, + username, password, }, data, contentType: "json" }); - return { valid: true, token: response.data.token, expiration: response.data.expiration }; + return { valid: true, token: `secret-token:${response.data.access_token}` as AccessToken, expiration: response.data.expiration }; } catch (error) { if (error instanceof RequestError) { return { valid: false, cause: error.cause }; @@ -76,13 +76,13 @@ export function useCredentialsChecker() { } } - return requestNewLoginToken(baseUrl, token.token as AccessToken) + return requestNewLoginToken(baseUrl, token.token) } return { requestNewLoginToken, refreshLoginToken } } export interface LoginToken { - token: string, + token: AccessToken, expiration: Timestamp, } // token used to get loginToken @@ -95,7 +95,7 @@ export type AccessToken = string & { type YesOrNo = "yes" | "no"; export type LoginResult = { valid: true; - token: string; + token: AccessToken; expiration: Timestamp; } | { valid: false; @@ -121,7 +121,7 @@ export interface LoginTokenSuccessResponse { // that are in scope for some time. Must be prefixed // with "Bearer " when used in the "Authorization" HTTP header. // Will already begin with the RFC 8959 prefix. - token: string; + access_token: AccessToken; // Scope of the token (which kinds of operations it will allow) scope: "readonly" | "write"; diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts index 3df463f4e..a23d3b569 100644 --- a/packages/demobank-ui/src/pages/AccountPage/state.ts +++ b/packages/demobank-ui/src/pages/AccountPage/state.ts @@ -63,9 +63,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe const { data } = result; - // FIXME: balance - // const balance = Amounts.parseOrThrow(data.balance.amount); - const balance = Amounts.parseOrThrow(data.balance); + const balance = Amounts.parseOrThrow(data.balance.amount); const debitThreshold = Amounts.parseOrThrow(data.debit_threshold); const payto = parsePaytoUri(data.payto_uri); diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx index b3996c5fc..32aedebf2 100644 --- a/packages/demobank-ui/src/pages/AccountPage/views.tsx +++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx @@ -14,16 +14,14 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, stringifyPaytoUri } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { Fragment, h, VNode } from "preact"; +import { Fragment, VNode, h } from "preact"; import { Transactions } from "../../components/Transactions/index.js"; -import { PaymentOptions } from "../PaymentOptions.js"; -import { State } from "./index.js"; -import { CopyButton } from "../../components/CopyButton.js"; -import { bankUiSettings } from "../../settings.js"; import { useBusinessAccountDetails } from "../../hooks/circuit.js"; import { useSettings } from "../../hooks/settings.js"; +import { bankUiSettings } from "../../settings.js"; +import { PaymentOptions } from "../PaymentOptions.js"; +import { State } from "./index.js"; export function InvalidIbanView({ error }: State.InvalidIban) { return ( diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index 214aac063..39ca09f1b 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -474,11 +474,9 @@ function AccountBalance({ account }: { account: string }): VNode { const result = useAccountDetails(account); if (!result.ok) return
- // FIXME: balance return
- {Amounts.currencyOf(result.data.balance)} {Amounts.stringifyValue(result.data.balance)} - {/* {Amounts.currencyOf(result.data.balance.amount)} + {Amounts.currencyOf(result.data.balance.amount)}  {result.data.balance.credit_debit_indicator === "debit" ? "-" : ""} - {Amounts.stringifyValue(result.data.balance.amount)} */} + {Amounts.stringifyValue(result.data.balance.amount)}
} diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index cfef71b9a..f1ba439ac 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -18,13 +18,11 @@ import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util"; import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; +import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; import { useBackendContext } from "../context/backend.js"; +import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js"; import { bankUiSettings } from "../settings.js"; import { undefinedIfEmpty } from "../utils.js"; -import { USERNAME_REGEX } from "./RegistrationPage.js"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; -import { AccessToken, useCredentialsChecker } from "../hooks/useCredentialsChecker.js"; -import { useCredentialsCheckerOld } from "../hooks/backend.js"; /** * Collect and submit login data. @@ -62,7 +60,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode { setBusy({}) const result = await requestNewLoginToken(username, password); if (result.valid) { - backend.logIn({ username, password }); + backend.logIn({ username, token: result.token }); } else { const { cause } = result; switch (cause.type) { diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx index 5325f43ab..a2543f977 100644 --- a/packages/demobank-ui/src/pages/RegistrationPage.tsx +++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx @@ -28,6 +28,7 @@ import { bankUiSettings } from "../settings.js"; import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js"; import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; import { getRandomPassword, getRandomUsername } from "./rnd.js"; +import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js"; const logger = new Logger("RegistrationPage"); @@ -58,6 +59,7 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on const [name, setName] = useState(); const [password, setPassword] = useState(); const [repeatPassword, setRepeatPassword] = useState(); + const {requestNewLoginToken} = useCredentialsChecker() const { register } = useTestingAPI(); const { i18n } = useTranslationContext(); @@ -83,8 +85,11 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on if (!username || !password || !name) return; try { await register({ name, username, password }); + const resp = await requestNewLoginToken(username, password) setUsername(undefined); - backend.logIn({ username, password }); + if (resp.valid) { + backend.logIn({ username, token: resp.token }); + } onComplete(); } catch (error) { if (error instanceof RequestError) { @@ -125,7 +130,10 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on setRepeatPassword(undefined); const username = `_${user.first}-${user.second}_` await register({ username, name: `${user.first} ${user.second}`, password: pass }); - backend.logIn({ username, password: pass }); + const resp = await requestNewLoginToken(username, pass) + if (resp.valid) { + backend.logIn({ username, token: resp.token }); + } onComplete(); } catch (error) { if (error instanceof RequestError) { diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx index d368c4319..8ea59cdcb 100644 --- a/packages/demobank-ui/src/pages/admin/Account.tsx +++ b/packages/demobank-ui/src/pages/admin/Account.tsx @@ -17,11 +17,8 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode } const { data } = result; - //FIXME: libeufin does not follow the spec - const balance = Amounts.parseOrThrow(data.balance); - const balanceIsDebit = true; - // const balance = Amounts.parseOrThrow(data.balance.amount); - // const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit"; + const balance = Amounts.parseOrThrow(data.balance.amount); + const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit"; const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold); const limit = balanceIsDebit diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx index 63a7c79f3..1e5370afc 100644 --- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx @@ -41,9 +41,7 @@ export function RemoveAccount({ if (focus) ref.current?.focus(); }, [focus]); - //FIXME: libeufin does not follow the spec - const balance = Amounts.parse(result.data.balance); - // const balance = Amounts.parse(result.data.balance.amount); + const balance = Amounts.parse(result.data.balance.amount); if (!balance) { return
there was an error reading the balance
; } diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx index 427cfc656..628ae328d 100644 --- a/packages/demobank-ui/src/pages/business/Home.tsx +++ b/packages/demobank-ui/src/pages/business/Home.tsx @@ -236,11 +236,8 @@ function CreateCashout({ if (!ratiosResult.ok) return onLoadNotOk(ratiosResult); const config = ratiosResult.data; - //FIXME: libeufin does not follow the spec - const balance = Amounts.parseOrThrow(result.data.balance); - const balanceIsDebit = true; - // const balance = Amounts.parseOrThrow(result.data.balance.amount); - // const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit"; + const balance = Amounts.parseOrThrow(result.data.balance.amount); + const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit"; const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold); const zero = Amounts.zeroOfCurrency(balance.currency); diff --git a/packages/demobank-ui/src/stories.test.ts b/packages/demobank-ui/src/stories.test.ts index e68788f16..07db7d8cf 100644 --- a/packages/demobank-ui/src/stories.test.ts +++ b/packages/demobank-ui/src/stories.test.ts @@ -26,6 +26,7 @@ import * as pages from "./pages/index.stories.js"; import { ComponentChildren, VNode, h as create } from "preact"; import { BackendStateProviderTesting } from "./context/backend.js"; +import { AccessToken } from "./hooks/useCredentialsChecker.js"; setupI18n("en", { en: {} }); @@ -56,7 +57,7 @@ function DefaultTestingContext({ state: { status: "loggedIn", username: "test", - password: "pwd", + token: "pwd" as AccessToken, isUserAdministrator: false, }, });