aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/hooks/useCredentialsChecker.ts')
-rw-r--r--packages/demobank-ui/src/hooks/useCredentialsChecker.ts135
1 files changed, 135 insertions, 0 deletions
diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
new file mode 100644
index 000000000..b3dedb654
--- /dev/null
+++ b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
@@ -0,0 +1,135 @@
+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: string,
+ ): Promise<LoginResult> {
+ const data: LoginTokenRequest = {
+ scope: "readwrite" as "write", //FIX: different than merchant
+ duration: {
+ // d_us: "forever" //FIX: should return shortest
+ d_us: 60 * 60 * 24 * 7 * 1000 * 1000
+ },
+ refreshable: true,
+ }
+ try {
+ const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
+ method: "POST",
+ basicAuth: {
+ username,
+ password,
+ },
+ data,
+ contentType: "json"
+ });
+ 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 };
+ }
+
+ 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)
+ }
+ return { requestNewLoginToken, refreshLoginToken }
+}
+
+export interface LoginToken {
+ token: AccessToken,
+ 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: AccessToken;
+ 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.
+ access_token: AccessToken;
+
+ // 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;
+}