gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-typescript-core] branch master updated: fix #9754


From: Admin
Subject: [taler-typescript-core] branch master updated: fix #9754
Date: Sat, 14 Jun 2025 00:08:46 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository taler-typescript-core.

The following commit(s) were added to refs/heads/master by this push:
     new 4db0f46e5 fix #9754
4db0f46e5 is described below

commit 4db0f46e53e5676402820907a51a622ccc58ffdd
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Jun 13 19:07:21 2025 -0300

    fix #9754
---
 packages/bank-ui/src/Routing.tsx                   | 77 +++-------------------
 packages/bank-ui/src/hooks/session.ts              | 74 ++++++++++++++++++++-
 packages/bank-ui/src/pages/LoginForm.tsx           |  7 +-
 .../merchant-backoffice-ui/src/AdminRoutes.tsx     |  4 ++
 packages/merchant-backoffice-ui/src/Routing.tsx    | 38 +++++++++--
 .../src/paths/admin/list/TableActive.tsx           | 23 ++++++-
 .../src/paths/admin/list/View.tsx                  |  3 +
 .../src/paths/admin/list/index.tsx                 |  8 ++-
 .../src/paths/instance/token/DetailPage.tsx        |  9 ++-
 .../src/paths/instance/token/index.tsx             | 49 +++++++++++---
 .../src/paths/instance/update/index.tsx            |  5 --
 packages/taler-util/src/http-client/bank-core.ts   | 40 ++++++++++-
 12 files changed, 235 insertions(+), 102 deletions(-)

diff --git a/packages/bank-ui/src/Routing.tsx b/packages/bank-ui/src/Routing.tsx
index 70d3ace92..80a996bef 100644
--- a/packages/bank-ui/src/Routing.tsx
+++ b/packages/bank-ui/src/Routing.tsx
@@ -16,27 +16,28 @@
 
 import {
   LocalNotificationBanner,
-  RouteDefinition,
   urlPattern,
   useBankCoreApiContext,
   useCurrentLocation,
   useLocalNotification,
   useNavigationContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 
 import {
   AbsoluteTime,
   AccessToken,
-  Duration,
   HttpStatusCode,
+  TalerErrorCode,
+  TokenRequest,
   TranslatedString,
   assertUnreachable,
-  createRFC8959AccessTokenEncoded,
+  createRFC8959AccessTokenEncoded
 } from "@gnu-taler/taler-util";
 import { useEffect } from "preact/hooks";
-import { useSessionState } from "./hooks/session.js";
+import { useBankState } from "./hooks/bank-state.js";
+import { useRefreshSessionBeforeExpires, useSessionState } from 
"./hooks/session.js";
 import { AccountPage } from "./pages/AccountPage/index.js";
 import { BankFrame } from "./pages/BankFrame.js";
 import { LoginForm, SESSION_DURATION } from "./pages/LoginForm.js";
@@ -56,9 +57,6 @@ import { RemoveAccount } from 
"./pages/admin/RemoveAccount.js";
 import { ConversionConfig } from "./pages/regional/ConversionConfig.js";
 import { CreateCashout } from "./pages/regional/CreateCashout.js";
 import { ShowCashoutDetails } from "./pages/regional/ShowCashoutDetails.js";
-import { useBankState } from "./hooks/bank-state.js";
-import { TalerErrorCode } from "@gnu-taler/taler-util";
-import { TokenRequest } from "@gnu-taler/taler-util";
 
 const TALER_SCREEN_ID = 100;
 
@@ -66,64 +64,7 @@ Routing.SCREEN_ID = TALER_SCREEN_ID;
 export function Routing(): VNode {
   const session = useSessionState();
 
-  const refreshSession =
-    session.state.status !== "loggedIn" ||
-    session.state.expiration.t_ms === "never"
-      ? undefined
-      : {
-          user: session.state.username,
-          time: session.state.expiration,
-          auth: session.state.token,
-        };
-
-  const {
-    lib: { bank },
-  } = useBankCoreApiContext();
-
-  useEffect(() => {
-    if (!refreshSession) return;
-    /**
-     * we need to wait before refreshing the session. Waiting too much and the 
token will
-     * be expired. So 20% before expiration should be close enough.
-     */
-    const timeLeftBeforeExpiration = 
Duration.getRemaining(refreshSession.time);
-    const refreshWindow = Duration.multiply(
-      Duration.fromTalerProtocolDuration(SESSION_DURATION),
-      0.2,
-    );
-    if (
-      timeLeftBeforeExpiration.d_ms === "forever" ||
-      refreshWindow.d_ms === "forever"
-    )
-      return;
-    const remain = Math.max(
-      timeLeftBeforeExpiration.d_ms - refreshWindow.d_ms,
-      0,
-    );
-    const timeoutId = setTimeout(async () => {
-      const result = await bank.createAccessTokenBasic(
-        refreshSession.user,
-        refreshSession.auth,
-        {
-          scope: "readwrite",
-          duration: SESSION_DURATION,
-          refreshable: true,
-        },
-      );
-      if (result.type === "fail") {
-        console.log(`could not refresh session ${result.case}`);
-        return;
-      }
-      session.logIn({
-        username: refreshSession.user,
-        token: createRFC8959AccessTokenEncoded(result.body.access_token),
-        expiration: AbsoluteTime.fromProtocolTimestamp(result.body.expiration),
-      });
-    }, remain);
-    return () => {
-      clearTimeout(timeoutId);
-    };
-  }, [refreshSession]);
+  useRefreshSessionBeforeExpires()
 
   if (session.state.status === "loggedIn") {
     const { isUserAdministrator, username } = session.state;
@@ -189,9 +130,9 @@ function PublicRounting({
         duration: SESSION_DURATION,
         refreshable: true,
       } as TokenRequest;
-      const resp = await lib.bank.createAccessTokenBasic(
+      const resp = await lib.bank.createAccessToken(
         username,
-        password,
+        { type: "basic", password },
         tokenRequest,
       );
       if (resp.type === "ok") {
diff --git a/packages/bank-ui/src/hooks/session.ts 
b/packages/bank-ui/src/hooks/session.ts
index 631c08bea..f7f0a8508 100644
--- a/packages/bank-ui/src/hooks/session.ts
+++ b/packages/bank-ui/src/hooks/session.ts
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Duration } from "@gnu-taler/taler-util";
 import {
   AbsoluteTime,
   AccessToken,
@@ -26,8 +27,11 @@ import {
   codecForString,
   codecOptionalDefault,
 } from "@gnu-taler/taler-util";
-import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
+import { buildStorageKey, useBankCoreApiContext, useLocalStorage } from 
"@gnu-taler/web-util/browser";
 import { mutate } from "swr";
+import { SESSION_DURATION } from "../pages/LoginForm.js";
+import { createRFC8959AccessTokenEncoded } from "@gnu-taler/taler-util";
+import { useEffect } from "preact/hooks";
 
 /**
  * Has the information to reach and
@@ -140,3 +144,71 @@ export function useSessionState(): SessionStateHandler {
 function cleanAllCache(): void {
   mutate(() => true, undefined, { revalidate: false });
 }
+
+/**
+ * Loads the session from local storage
+ * Sets a timeout before the session expires
+ * Makes a request to refresh the session
+ * Saves new session
+ */
+export function useRefreshSessionBeforeExpires() {
+  const session = useSessionState();
+  
+  const {
+    lib: { bank },
+  } = useBankCoreApiContext();
+
+  const refreshSession =
+    session.state.status !== "loggedIn" ||
+    session.state.expiration.t_ms === "never"
+      ? undefined
+      : session.state;
+
+  useEffect(() => {
+    if (!refreshSession) return;
+    /**
+     * we need to wait before refreshing the session. Waiting too much and the 
token will
+     * be expired. So 20% before expiration should be close enough.
+     */
+    const timeLeftBeforeExpiration = 
Duration.getRemaining(refreshSession.expiration);
+    const refreshWindow = Duration.multiply(
+      Duration.fromTalerProtocolDuration(SESSION_DURATION),
+      0.2,
+    );
+    if (
+      timeLeftBeforeExpiration.d_ms === "forever" ||
+      refreshWindow.d_ms === "forever"
+    )
+      return;
+    const remain = Math.max(
+      timeLeftBeforeExpiration.d_ms - refreshWindow.d_ms,
+      0,
+    );
+    const timeoutId = setTimeout(async () => {
+      const result = await bank.createAccessToken(
+        refreshSession.username,
+        { type: "bearer", accessToken: refreshSession.token },
+        {
+          scope: "readwrite",
+          duration: SESSION_DURATION,
+          refreshable: true,
+        },
+      );
+      if (result.type === "fail") {
+        console.log(
+          `could not refresh session ${result.case}: 
${JSON.stringify(result)}`,
+        );
+        return;
+      }
+      session.logIn({
+        username: refreshSession.username,
+        token: createRFC8959AccessTokenEncoded(result.body.access_token),
+        expiration: AbsoluteTime.fromProtocolTimestamp(result.body.expiration),
+      });
+    }, remain);
+    return () => {
+      clearTimeout(timeoutId);
+    };
+  }, [refreshSession]);
+
+}
diff --git a/packages/bank-ui/src/pages/LoginForm.tsx 
b/packages/bank-ui/src/pages/LoginForm.tsx
index 75fda2736..cd809f6a7 100644
--- a/packages/bank-ui/src/pages/LoginForm.tsx
+++ b/packages/bank-ui/src/pages/LoginForm.tsx
@@ -43,6 +43,7 @@ const TALER_SCREEN_ID = 104;
 
 export const SESSION_DURATION = Duration.toTalerProtocolDuration(
   Duration.fromSpec({
+    // seconds: 6,
     minutes: 30,
   }),
 );
@@ -110,9 +111,9 @@ export function LoginForm({
       ? undefined
       : withErrorHandler(
           async () =>
-            authenticator.createAccessTokenBasic(
+            authenticator.createAccessToken(
               username,
-              password,
+              { type: "basic", password },
               tokenRequest,
             ),
           (result) => {
@@ -138,7 +139,7 @@ export function LoginForm({
                     password,
                   },
                 });
-                onAuthorizationRequired()
+                onAuthorizationRequired();
                 return i18n.str`Second factor authentication required.`;
               }
               case TalerErrorCode.GENERIC_FORBIDDEN:
diff --git a/packages/merchant-backoffice-ui/src/AdminRoutes.tsx 
b/packages/merchant-backoffice-ui/src/AdminRoutes.tsx
index b186f1408..783040f07 100644
--- a/packages/merchant-backoffice-ui/src/AdminRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/AdminRoutes.tsx
@@ -30,6 +30,10 @@ export function AdminRoutes(): VNode {
       <Route
         path={AdminPaths.list_instances}
         component={InstanceListPage}
+        onChangePassword={(id) => {
+          console.log("ASDASD")
+          route(`/instance/${id}/token`);
+        }}
         onCreate={() => {
           route(AdminPaths.new_instance);
         }}
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx 
b/packages/merchant-backoffice-ui/src/Routing.tsx
index a9fcbf9e4..85965bfa6 100644
--- a/packages/merchant-backoffice-ui/src/Routing.tsx
+++ b/packages/merchant-backoffice-ui/src/Routing.tsx
@@ -38,13 +38,16 @@ import {
 } from "./components/menu/index.js";
 import { useSessionContext } from "./context/session.js";
 import { useInstanceBankAccounts } from "./hooks/bank.js";
-import { useInstanceKYCDetails, useInstanceKYCDetailsLongPolling } from 
"./hooks/instance.js";
+import { useInstanceKYCDetailsLongPolling } from "./hooks/instance.js";
 import { usePreference } from "./hooks/preference.js";
 import InstanceCreatePage from "./paths/admin/create/index.js";
 import InstanceListPage from "./paths/admin/list/index.js";
 import BankAccountCreatePage from "./paths/instance/accounts/create/index.js";
 import BankAccountListPage from "./paths/instance/accounts/list/index.js";
 import BankAccountUpdatePage from "./paths/instance/accounts/update/index.js";
+import CreateCategory from "./paths/instance/categories/create/index.js";
+import ListCategories from "./paths/instance/categories/list/index.js";
+import UpdateCategory from "./paths/instance/categories/update/index.js";
 import ListKYCPage from "./paths/instance/kyc/list/index.js";
 import OrderCreatePage from "./paths/instance/orders/create/index.js";
 import OrderDetailsPage from "./paths/instance/orders/details/index.js";
@@ -60,7 +63,10 @@ import TemplateListPage from 
"./paths/instance/templates/list/index.js";
 import TemplateQrPage from "./paths/instance/templates/qr/index.js";
 import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
 import TemplateUsePage from "./paths/instance/templates/use/index.js";
-import TokenPage from "./paths/instance/token/index.js";
+import TokenPage, {
+    AdminToken as InstanceAdminTokenPage,
+  Props as InstanceAdminTokenProps,
+} from "./paths/instance/token/index.js";
 import TokenFamilyCreatePage from 
"./paths/instance/tokenfamilies/create/index.js";
 import TokenFamilyListPage from "./paths/instance/tokenfamilies/list/index.js";
 import TokenFamilyUpdatePage from 
"./paths/instance/tokenfamilies/update/index.js";
@@ -76,9 +82,6 @@ import WebhookUpdatePage from 
"./paths/instance/webhooks/update/index.js";
 import { LoginPage } from "./paths/login/index.js";
 import { Settings } from "./paths/settings/index.js";
 import { Notification } from "./utils/types.js";
-import ListCategories from "./paths/instance/categories/list/index.js";
-import CreateCategory from "./paths/instance/categories/create/index.js";
-import UpdateCategory from "./paths/instance/categories/update/index.js";
 
 export enum InstancePaths {
   error = "/error",
@@ -131,6 +134,7 @@ export enum AdminPaths {
   list_instances = "/instances",
   new_instance = "/instance/new",
   update_instance = "/instance/:id/update",
+  update_instance_auth = "/instance/:id/token",
 }
 
 export interface Props {}
@@ -267,6 +271,9 @@ export function Routing(_p: Props): VNode {
           <Route
             path={AdminPaths.list_instances}
             component={InstanceListPage}
+            onChangePassword={(id: string): void => {
+              route(`/instance/${id}/token`);
+            }}
             onCreate={() => {
               route(AdminPaths.new_instance);
             }}
@@ -295,6 +302,16 @@ export function Routing(_p: Props): VNode {
             }}
           />
         )}
+        {state.isAdmin && (
+          <Route
+            path={AdminPaths.update_instance_auth}
+            component={AdminInstanceUpdateTokenPage}
+            onCancel={() => route(AdminPaths.list_instances)}
+            onChange={() => {
+              route(AdminPaths.list_instances);
+            }}
+          />
+        )}
         {/**
          * Update instance page
          */}
@@ -662,6 +679,17 @@ function AdminInstanceUpdatePage({
   );
 }
 
+function AdminInstanceUpdateTokenPage({
+  id,
+  ...rest
+}: { id: string } & InstanceAdminTokenProps): VNode {
+  return (
+    <Fragment>
+      <InstanceAdminTokenPage {...rest} instanceId={id} />
+    </Fragment>
+  );
+}
+
 function BankAccountBanner(): VNode {
   const { i18n } = useTranslationContext();
 
diff --git 
a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
index 61dbea9de..792606039 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
@@ -28,6 +28,7 @@ import { useSessionContext } from 
"../../../context/session.js";
 interface Props {
   instances: TalerMerchantApi.Instance[];
   onUpdate: (id: string) => void;
+  onChangePassword: (id: string) => void;
   onDelete: (id: TalerMerchantApi.Instance) => void;
   onPurge: (id: TalerMerchantApi.Instance) => void;
   onCreate: () => void;
@@ -40,6 +41,7 @@ export function CardTable({
   onUpdate,
   onPurge,
   onDelete,
+  onChangePassword,
   selected,
 }: Props): VNode {
   const [actionQueue, actionQueueHandler] = useState<Actions[]>([]);
@@ -114,6 +116,7 @@ export function CardTable({
                 onPurge={onPurge}
                 onUpdate={onUpdate}
                 onDelete={onDelete}
+                onChangePassword={onChangePassword}
                 rowSelection={rowSelection}
                 rowSelectionHandler={rowSelectionHandler}
               />
@@ -130,6 +133,7 @@ interface TableProps {
   rowSelection: string[];
   instances: TalerMerchantApi.Instance[];
   onUpdate: (id: string) => void;
+  onChangePassword: (id: string) => void;
   onDelete: (id: TalerMerchantApi.Instance) => void;
   onPurge: (id: TalerMerchantApi.Instance) => void;
   rowSelectionHandler: StateUpdater<string[]>;
@@ -140,11 +144,17 @@ function toggleSelected<T>(id: T): (prev: T[]) => T[] {
     prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
 }
 
+/**
+ * FIXME: put this on UI settings
+ */
+const HIDE_EDIT_INSTANCE = true;
+
 function Table({
   rowSelection,
   rowSelectionHandler,
   instances,
   onUpdate,
+  onChangePassword,
   onDelete,
   onPurge,
 }: TableProps): VNode {
@@ -212,12 +222,21 @@ function Table({
                 <td>{i.name}</td>
                 <td class="is-actions-cell right-sticky">
                   <div class="buttons is-right">
+                    {HIDE_EDIT_INSTANCE ? undefined : (
+                      <button
+                        class="button is-small is-success jb-modal"
+                        type="button"
+                        onClick={(): void => onUpdate(i.id)}
+                      >
+                        <i18n.Translate>Edit</i18n.Translate>
+                      </button>
+                    )}
                     <button
                       class="button is-small is-success jb-modal"
                       type="button"
-                      onClick={(): void => onUpdate(i.id)}
+                      onClick={(): void => onChangePassword(i.id)}
                     >
-                      <i18n.Translate>Edit</i18n.Translate>
+                      <i18n.Translate>Change password</i18n.Translate>
                     </button>
                     {!i.deleted && (
                       <button
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
index 969a037a9..0813a9a2c 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
@@ -29,6 +29,7 @@ interface Props {
   instances: TalerMerchantApi.Instance[];
   onCreate: () => void;
   onUpdate: (id: string) => void;
+  onChangePassword: (id: string) => void;
   onDelete: (id: TalerMerchantApi.Instance) => void;
   onPurge: (id: TalerMerchantApi.Instance) => void;
   selected?: boolean;
@@ -40,6 +41,7 @@ export function View({
   onDelete,
   onPurge,
   onUpdate,
+  onChangePassword,
   selected,
 }: Props): VNode {
   const [show, setShow] = useState<"active" | "deleted" | null>("active");
@@ -98,6 +100,7 @@ export function View({
         instances={showingInstances}
         onDelete={onDelete}
         onPurge={onPurge}
+        onChangePassword={onChangePassword}
         onUpdate={onUpdate}
         selected={selected}
         onCreate={onCreate}
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
index 8ce243686..3e0321e19 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
@@ -41,10 +41,15 @@ import { View } from "./View.js";
 interface Props {
   onCreate: () => void;
   onUpdate: (id: string) => void;
+  onChangePassword: (id: string) => void;
   instances: TalerMerchantApi.Instance[];
 }
 
-export default function Instances({ onCreate, onUpdate }: Props): VNode {
+export default function Instances({
+  onCreate,
+  onUpdate,
+  onChangePassword,
+}: Props): VNode {
   const result = useBackendInstances();
   const [deleting, setDeleting] = useState<TalerMerchantApi.Instance | null>(
     null,
@@ -80,6 +85,7 @@ export default function Instances({ onCreate, onUpdate }: 
Props): VNode {
         onCreate={onCreate}
         onPurge={setPurging}
         onUpdate={onUpdate}
+        onChangePassword={onChangePassword}
         selected={!!deleting}
       />
       {deleting && (
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
index c3a00bbed..3b786d2ad 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { AccessToken, createRFC8959AccessTokenPlain } from 
"@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -26,11 +27,10 @@ import { AsyncButton } from 
"../../../components/exception/AsyncButton.js";
 import { FormProvider } from "../../../components/form/FormProvider.js";
 import { Input } from "../../../components/form/Input.js";
 import { NotificationCard } from "../../../components/menu/index.js";
-import { useSessionContext } from "../../../context/session.js";
-import { AccessToken, createRFC8959AccessTokenPlain } from 
"@gnu-taler/taler-util";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
 interface Props {
+  instanceId: string;
   hasToken: boolean | undefined;
   onClearToken: (c: AccessToken | undefined) => void;
   onNewToken: (c: AccessToken | undefined, s: AccessToken) => void;
@@ -38,6 +38,7 @@ interface Props {
 }
 
 export function DetailPage({
+  instanceId,
   hasToken,
   onBack,
   onNewToken,
@@ -69,9 +70,7 @@ export function DetailPage({
 
   const hasErrors = errors !== undefined;
 
-  const { state } = useSessionContext();
-
-  const text = i18n.str`You are updating the password from instance with id 
"${state.instance}"`;
+  const text = i18n.str`You are updating the password from instance with id 
"${instanceId}"`;
 
   async function submitForm() {
     if (hasErrors) return;
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
index 77071e532..1d0201bd9 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
@@ -17,6 +17,8 @@ import {
   HttpStatusCode,
   MerchantAuthMethod,
   TalerError,
+  TalerMerchantInstanceHttpClient,
+  TalerMerchantManagementResultByMethod,
   assertUnreachable,
 } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -26,22 +28,47 @@ import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.j
 import { Loading } from "../../../components/exception/loading.js";
 import { NotificationCard } from "../../../components/menu/index.js";
 import { useSessionContext } from "../../../context/session.js";
-import { useInstanceDetails } from "../../../hooks/instance.js";
+import { useInstanceDetails, useManagedInstanceDetails } from 
"../../../hooks/instance.js";
 import { Notification } from "../../../utils/types.js";
 import { LoginPage } from "../../login/index.js";
 import { NotFoundPageOrAdminCreate } from "../../notfound/index.js";
 import { DetailPage } from "./DetailPage.js";
 
-interface Props {
+export interface Props {
   onChange: () => void;
   onCancel: () => void;
 }
 
-export default function Token({ onChange, onCancel }: Props): VNode {
+export default function Token(props: Props): VNode {
+  const { lib } = useSessionContext();
+  const updateCurrentInstanceAuthentication = 
lib.instance.updateCurrentInstanceAuthentication.bind(lib.instance);
+  const createAuthTokenFromToken = 
lib.instance.createAuthTokenFromToken.bind(lib.instance);
+  const result = useInstanceDetails();
+  return CommonToken(props, result, updateCurrentInstanceAuthentication, 
createAuthTokenFromToken, true)
+}
+
+export function AdminToken(props: Props& { instanceId: string }): VNode {
+  const { lib } = useSessionContext();
+  const subInstaceLib = lib.subInstanceApi(props.instanceId).instance;
+  const updateCurrentInstanceAuthentication = 
subInstaceLib.updateCurrentInstanceAuthentication.bind(subInstaceLib);
+  const createAuthTokenFromToken = 
subInstaceLib.createAuthTokenFromToken.bind(subInstaceLib);
+  const result = useManagedInstanceDetails(props.instanceId);
+  return CommonToken(props, result, updateCurrentInstanceAuthentication, 
createAuthTokenFromToken, false)
+}
+
+function CommonToken(
+  { onChange, onCancel }: Props,
+  result:
+    | TalerMerchantManagementResultByMethod<"getInstanceDetails">
+    | TalerError
+    | undefined,
+  updateCurrentInstanceAuthentication: typeof 
TalerMerchantInstanceHttpClient.prototype.updateCurrentInstanceAuthentication,
+  createAuthTokenFromToken: typeof 
TalerMerchantInstanceHttpClient.prototype.createAuthTokenFromToken,
+  replaceSession: boolean,
+): VNode {
   const { i18n } = useTranslationContext();
-  const { state, logIn, lib } = useSessionContext();
+  const { state, logIn } = useSessionContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const result = useInstanceDetails();
 
   if (!result) return <Loading />;
   if (result instanceof TalerError) {
@@ -68,10 +95,11 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
       <NotificationCard notification={notif} />
       <DetailPage
         onBack={onCancel}
+        instanceId={result.body.name}
         hasToken={hasToken}
         onClearToken={async (currentToken): Promise<void> => {
           try {
-            const resp = await 
lib.instance.updateCurrentInstanceAuthentication(
+            const resp = await updateCurrentInstanceAuthentication(
               currentToken,
               {
                 method: MerchantAuthMethod.EXTERNAL,
@@ -99,7 +127,7 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
           try {
             {
               const resp =
-                await lib.instance.updateCurrentInstanceAuthentication(
+                await updateCurrentInstanceAuthentication(
                   currentToken,
                   {
                     token: newToken,
@@ -114,7 +142,7 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
                 });
               }
             }
-            const resp = await lib.instance.createAuthTokenFromToken(newToken, 
{
+            const resp = await createAuthTokenFromToken(newToken, {
               scope: "write",
               duration: {
                 d_us: "forever",
@@ -122,7 +150,10 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
               refreshable: true,
             });
             if (resp.type === "ok") {
-              logIn(state.instance, resp.body.token);
+              if (replaceSession) {
+                // only renew the session token if we are not the admin
+                logIn(state.instance, resp.body.token);
+              }
               return onChange();
             } else {
               return setNotif({
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
index 2b2327eb2..eeb522a6e 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
@@ -35,11 +35,6 @@ import { UpdatePage } from "./UpdatePage.js";
 export interface Props {
   onBack: () => void;
   onConfirm: () => void;
-
-  // onUnauthorized: () => VNode;
-  // onNotFound: () => VNode;
-  // onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
-  // onUpdateError: (e: HttpError<TalerErrorDetail>) => void;
 }
 
 export default function Update(props: Props): VNode {
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index 60443fa50..9e1e2097e 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -29,6 +29,7 @@ import {
   TokenRequest,
   UserAndPassword,
   UserAndToken,
+  assertUnreachable,
   codecForTalerCommonConfigResponse,
   codecForTokenInfoList,
   codecForTokenSuccessResponse,
@@ -100,6 +101,15 @@ export enum TalerCoreBankCacheEviction {
   CREATE_CASHOUT,
 }
 
+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.
  *
@@ -129,11 +139,12 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
+   *
    * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
    */
-  async createAccessTokenBasic(
+  async createAccessToken(
     username: string,
-    password: string,
+    cred: Credentials,
     body: TokenRequest,
     cid?: string,
   ) {
@@ -141,7 +152,12 @@ export class TalerCoreBankHttpClient {
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
       headers: {
-        Authorization: makeBasicAuthHeader(username, password),
+        Authorization:
+          cred.type === "basic"
+            ? makeBasicAuthHeader(username, cred.password)
+            : cred.type === "bearer"
+              ? makeBearerTokenAuthHeader(cred.accessToken)
+              : assertUnreachable(cred),
         "X-Challenge-Id": cid,
       },
       body,
@@ -175,6 +191,24 @@ export class TalerCoreBankHttpClient {
     }
   }
 
+  /**
+   * @deprecated use createAccessToken
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
+   */
+  async createAccessTokenBasic(
+    username: string,
+    password: string,
+    body: TokenRequest,
+    cid?: string,
+  ) {
+    return this.createAccessToken(
+      username,
+      { type: "basic", password },
+      body,
+      cid,
+    );
+  }
+
   /**
    * 
https://docs.taler.net/core/api-corebank.html#delete--accounts-$USERNAME-token
    */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]