aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-09-21 13:54:02 -0300
committerSebastian <sebasjm@gmail.com>2023-09-25 14:50:41 -0300
commitaf623f5096138631383719bf737f5ff21660e052 (patch)
tree06077e6e938d29381d03a85e4b9cd21b43258d0d
parent0b7bbed99d155ba030a1328e357ab6751bdbb835 (diff)
downloadwallet-core-af623f5096138631383719bf737f5ff21660e052.tar.xz
preparing for the new token api
-rw-r--r--packages/demobank-ui/src/hooks/backend.ts2
-rw-r--r--packages/demobank-ui/src/hooks/useCredentialsChecker.ts130
-rw-r--r--packages/demobank-ui/src/pages/LoginForm.tsx7
3 files changed, 136 insertions, 3 deletions
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
index c05ab33e9..9d32db4b8 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -255,7 +255,7 @@ interface InvalidationResult {
error: unknown;
}
-export function useCredentialsChecker() {
+export function useCredentialsCheckerOld() {
const { request } = useApiContext();
const baseUrl = getInitialBackendBaseURL();
//check against account details endpoint
diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
new file mode 100644
index 000000000..b76754ffe
--- /dev/null
+++ b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
@@ -0,0 +1,130 @@
+import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util";
+import { ErrorType, HttpError, RequestError, useApiContext } from "@gnu-taler/web-util/browser";
+import { getInitialBackendBaseURL } from "./backend.js";
+
+export function useCredentialsChecker() {
+ const { request } = useApiContext();
+ const baseUrl = getInitialBackendBaseURL();
+ //check against instance details endpoint
+ //while merchant backend doesn't have a login endpoint
+ async function requestNewLoginToken(
+ username: string,
+ password: AccessToken,
+ ): Promise<LoginResult> {
+ const data: LoginTokenRequest = {
+ scope: "write",
+ duration: {
+ d_us: "forever"
+ },
+ refreshable: true,
+ }
+ try {
+ const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
+ method: "POST",
+ token: password,
+ data
+ });
+ return { valid: true, token: response.data.token, expiration: response.data.expiration };
+ } catch (error) {
+ if (error instanceof RequestError) {
+ return { valid: false, cause: error.cause };
+ }
+
+ return {
+ valid: false, cause: {
+ type: ErrorType.UNEXPECTED,
+ loading: false,
+ info: {
+ hasToken: true,
+ status: 0,
+ options: {},
+ url: `/private/token`,
+ payload: {}
+ },
+ exception: error,
+ message: (error instanceof Error ? error.message : "unpexepected error")
+ }
+ };
+ }
+ };
+
+ async function refreshLoginToken(
+ baseUrl: string,
+ token: LoginToken
+ ): Promise<LoginResult> {
+
+ if (AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(token.expiration))) {
+ return {
+ valid: false, cause: {
+ type: ErrorType.CLIENT,
+ status: HttpStatusCode.Unauthorized,
+ message: "login token expired, login again.",
+ info: {
+ hasToken: true,
+ status: 401,
+ options: {},
+ url: `/private/token`,
+ payload: {}
+ },
+ payload: {}
+ },
+ }
+ }
+
+ return requestNewLoginToken(baseUrl, token.token as AccessToken)
+ }
+ return { requestNewLoginToken, refreshLoginToken }
+}
+
+export interface LoginToken {
+ token: string,
+ expiration: Timestamp,
+}
+// token used to get loginToken
+// must forget after used
+declare const __ac_token: unique symbol;
+export type AccessToken = string & {
+ [__ac_token]: true;
+};
+
+type YesOrNo = "yes" | "no";
+export type LoginResult = {
+ valid: true;
+ token: string;
+ expiration: Timestamp;
+} | {
+ valid: false;
+ cause: HttpError<{}>;
+}
+
+
+// DELETE /private/instances/$INSTANCE
+export interface LoginTokenRequest {
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ duration?: RelativeTime;
+
+ // Can this token be refreshed?
+ // Defaults to false.
+ refreshable?: boolean;
+}
+export interface LoginTokenSuccessResponse {
+ // The login token that can be used to access resources
+ // 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;
+
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ expiration: Timestamp;
+
+ // Can this token be refreshed?
+ refreshable: boolean;
+}
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index 46039005a..f6ea0e1d1 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -19,11 +19,12 @@ import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-ut
import { Fragment, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
-import { useCredentialsChecker } from "../hooks/backend.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.
@@ -33,7 +34,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
const [username, setUsername] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
- const testLogin = useCredentialsChecker();
+ // const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
+
+ const testLogin = useCredentialsCheckerOld();
const ref = useRef<HTMLInputElement>(null);
useEffect(function focusInput() {
ref.current?.focus();