preparing for the new token api
This commit is contained in:
parent
0b7bbed99d
commit
af623f5096
@ -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
|
||||
|
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 { 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();
|
||||
|
Loading…
Reference in New Issue
Block a user