preparing for the new token api
This commit is contained in:
parent
0b7bbed99d
commit
af623f5096
@ -255,7 +255,7 @@ interface InvalidationResult {
|
|||||||
error: unknown;
|
error: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useCredentialsChecker() {
|
export function useCredentialsCheckerOld() {
|
||||||
const { request } = useApiContext();
|
const { request } = useApiContext();
|
||||||
const baseUrl = getInitialBackendBaseURL();
|
const baseUrl = getInitialBackendBaseURL();
|
||||||
//check against account details endpoint
|
//check against account details endpoint
|
||||||
|
130
packages/demobank-ui/src/hooks/useCredentialsChecker.ts
Normal file
130
packages/demobank-ui/src/hooks/useCredentialsChecker.ts
Normal file
@ -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;
|
||||||
|
}
|
@ -19,11 +19,12 @@ import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-ut
|
|||||||
import { Fragment, VNode, h } from "preact";
|
import { Fragment, VNode, h } from "preact";
|
||||||
import { useEffect, useRef, useState } from "preact/hooks";
|
import { useEffect, useRef, useState } from "preact/hooks";
|
||||||
import { useBackendContext } from "../context/backend.js";
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useCredentialsChecker } from "../hooks/backend.js";
|
|
||||||
import { bankUiSettings } from "../settings.js";
|
import { bankUiSettings } from "../settings.js";
|
||||||
import { undefinedIfEmpty } from "../utils.js";
|
import { undefinedIfEmpty } from "../utils.js";
|
||||||
import { USERNAME_REGEX } from "./RegistrationPage.js";
|
import { USERNAME_REGEX } from "./RegistrationPage.js";
|
||||||
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.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.
|
* Collect and submit login data.
|
||||||
@ -33,7 +34,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
|||||||
const [username, setUsername] = useState<string | undefined>();
|
const [username, setUsername] = useState<string | undefined>();
|
||||||
const [password, setPassword] = useState<string | undefined>();
|
const [password, setPassword] = useState<string | undefined>();
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const testLogin = useCredentialsChecker();
|
// const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
|
||||||
|
|
||||||
|
const testLogin = useCredentialsCheckerOld();
|
||||||
const ref = useRef<HTMLInputElement>(null);
|
const ref = useRef<HTMLInputElement>(null);
|
||||||
useEffect(function focusInput() {
|
useEffect(function focusInput() {
|
||||||
ref.current?.focus();
|
ref.current?.focus();
|
||||||
|
Loading…
Reference in New Issue
Block a user