update how access token management is handled

This commit is contained in:
Sebastian 2023-09-14 12:14:21 -03:00
parent dd25740c91
commit 1653130de8
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
5 changed files with 80 additions and 58 deletions

View File

@ -114,7 +114,7 @@ export function ApplicationReadyRoutes(): VNode {
<NotificationCard
notification={{
message: i18n.str`Access denied`,
description: i18n.str`Check your token is valid 1`,
description: i18n.str`Check your token is valid`,
type: "ERROR",
}}
/>

View File

@ -266,7 +266,7 @@ export function useBackendBaseRequest(): useBackendBaseRequestType {
endpoint: string,
options: RequestOptions = {},
): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, endpoint, { token, ...options }).then(res => {
return requestHandler<T>(backend, endpoint, { ...options, token }).then(res => {
return res
}).catch(err => {
throw err

View File

@ -36,8 +36,8 @@ interface InstanceAPI {
data: MerchantBackend.Instances.InstanceReconfigurationMessage,
) => Promise<void>;
deleteInstance: () => Promise<void>;
clearToken: () => Promise<void>;
setNewToken: (token: AccessToken) => Promise<void>;
clearAccessToken: (currentToken: AccessToken | undefined) => Promise<void>;
setNewAccessToken: (currentToken: AccessToken | undefined, token: AccessToken) => Promise<void>;
}
export function useAdminAPI(): AdminAPI {
@ -111,18 +111,20 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
mutateAll(/\/management\/instances/);
};
const clearToken = async (): Promise<void> => {
const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
await request(`/management/instances/${instanceId}/auth`, {
method: "POST",
token: currentToken,
data: { method: "external" },
});
mutateAll(/\/management\/instances/);
};
const setNewToken = async (newToken: AccessToken): Promise<void> => {
const setNewAccessToken = async (currentToken: AccessToken | undefined, newToken: AccessToken): Promise<void> => {
await request(`/management/instances/${instanceId}/auth`, {
method: "POST",
token: currentToken,
data: { method: "token", token: newToken },
});
@ -137,7 +139,7 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
mutateAll(/\/management\/instances/);
};
return { updateInstance, deleteInstance, setNewToken, clearToken };
return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
}
export function useInstanceAPI(): InstanceAPI {
@ -172,18 +174,20 @@ export function useInstanceAPI(): InstanceAPI {
mutate([`/private/`], null);
};
const clearToken = async (): Promise<void> => {
const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
await request(`/private/auth`, {
method: "POST",
token: currentToken,
data: { method: "external" },
});
mutate([`/private/`], null);
};
const setNewToken = async (newToken: AccessToken): Promise<void> => {
const setNewAccessToken = async (currentToken: AccessToken | undefined, newToken: AccessToken): Promise<void> => {
await request(`/private/auth`, {
method: "POST",
token: currentToken,
data: { method: "token", token: newToken },
});
@ -198,7 +202,7 @@ export function useInstanceAPI(): InstanceAPI {
mutate([`/private/`], null);
};
return { updateInstance, deleteInstance, setNewToken, clearToken };
return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
}
export function useInstanceDetails(): HttpResponse<

View File

@ -30,13 +30,13 @@ import { AccessToken } from "../../../declaration.js";
interface Props {
instanceId: string;
currentToken: string | undefined;
onClearToken: () => void;
onNewToken: (s: AccessToken) => void;
hasToken: boolean | undefined;
onClearToken: (c: AccessToken | undefined) => void;
onNewToken: (c: AccessToken | undefined, s: AccessToken) => void;
onBack?: () => void;
}
export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewToken, onClearToken }: Props): VNode {
export function DetailPage({ instanceId, hasToken, onBack, onNewToken, onClearToken }: Props): VNode {
type State = { old_token: string; new_token: string; repeat_token: string };
const [form, setValue] = useState<Partial<State>>({
old_token: "",
@ -45,11 +45,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
});
const { i18n } = useTranslationContext();
const hasOldtoken = !!oldToken
const hasInputTheCorrectOldToken = hasOldtoken && oldToken !== form.old_token;
const errors = {
old_token: hasInputTheCorrectOldToken
? i18n.str`is not the same as the current access token`
old_token: hasToken && !form.old_token
? i18n.str`you need your access token to perform the operation`
: undefined,
new_token: !form.new_token
? i18n.str`cannot be empty`
@ -72,8 +70,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
async function submitForm() {
if (hasErrors) return;
const ot = hasToken ? `secret-token:${form.old_token}` as AccessToken : undefined;
const nt = `secret-token:${form.new_token}` as AccessToken;
onNewToken(nt)
onNewToken(ot, nt)
}
return (
@ -98,32 +97,38 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
<div class="column" />
<div class="column is-four-fifths">
<FormProvider errors={errors} object={form} valueHandler={setValue}>
{hasOldtoken && (
<Input<State>
name="old_token"
label={i18n.str`Current access token`}
tooltip={i18n.str`access token currently in use`}
inputType="password"
/>
)}
{!hasInputTheCorrectOldToken && <Fragment>
{hasOldtoken && <Fragment>
<p>
<i18n.Translate>
Clearing the access token will mean public access to the instance.
</i18n.Translate>
</p>
<div class="buttons is-right mt-5">
<button
disabled={!!hasInputTheCorrectOldToken}
class="button"
onClick={onClearToken}
>
<i18n.Translate>Clear token</i18n.Translate>
</button>
</div>
</Fragment>
}
<Fragment>
{hasToken && (
<Fragment>
<Input<State>
name="old_token"
label={i18n.str`Current access token`}
tooltip={i18n.str`access token currently in use`}
inputType="password"
/>
<p>
<i18n.Translate>
Clearing the access token will mean public access to the instance.
</i18n.Translate>
</p>
<div class="buttons is-right mt-5">
<button
class="button"
onClick={() => {
if (hasToken) {
const ot = `secret-token:${form.old_token}` as AccessToken;
onClearToken(ot)
} else {
onClearToken(undefined)
}
}}
>
<i18n.Translate>Clear token</i18n.Translate>
</button>
</div>
</Fragment>
)}
<Input<State>
name="new_token"
@ -137,7 +142,7 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
tooltip={i18n.str`confirm the same access token`}
inputType="password"
/>
</Fragment>}
</Fragment>
</FormProvider>
<div class="buttons is-right mt-5">
{onBack && (

View File

@ -33,8 +33,6 @@ interface Props {
onNotFound: () => VNode;
}
const PREFIX = "secret-token:"
export default function Token({
onLoadError,
onChange,
@ -44,21 +42,36 @@ export default function Token({
const { i18n } = useTranslationContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { clearToken, setNewToken } = useInstanceAPI();
const { token: rootToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { clearAccessToken, setNewAccessToken } = useInstanceAPI();
const { id } = useInstanceContext();
const result = useInstanceDetails()
if (result.loading) return <Loading />;
if (!result.ok) {
if (
result.type === ErrorType.CLIENT &&
result.status === HttpStatusCode.Unauthorized
)
return onUnauthorized();
if (
result.type === ErrorType.CLIENT &&
result.status === HttpStatusCode.NotFound
)
return onNotFound();
return onLoadError(result);
}
const hasToken = result.data.auth.method === "token"
const currentToken = !admin ? rootToken : instanceToken
const hasPrefix = currentToken !== undefined && currentToken.token.startsWith(PREFIX)
return (
<Fragment>
<NotificationCard notification={notif} />
<DetailPage
instanceId={id}
currentToken={hasPrefix ? currentToken.token.substring(PREFIX.length) : currentToken?.token}
onClearToken={async (): Promise<void> => {
hasToken={hasToken}
onClearToken={async (currentToken): Promise<void> => {
try {
await clearToken();
await clearAccessToken(currentToken);
onChange();
} catch (error) {
if (error instanceof Error) {
@ -70,9 +83,9 @@ export default function Token({
}
}
}}
onNewToken={async (newToken): Promise<void> => {
onNewToken={async (currentToken, newToken): Promise<void> => {
try {
await setNewToken(newToken);
await setNewAccessToken(currentToken, newToken);
onChange();
} catch (error) {
if (error instanceof Error) {