update how access token management is handled
This commit is contained in:
parent
dd25740c91
commit
1653130de8
@ -114,7 +114,7 @@ export function ApplicationReadyRoutes(): VNode {
|
|||||||
<NotificationCard
|
<NotificationCard
|
||||||
notification={{
|
notification={{
|
||||||
message: i18n.str`Access denied`,
|
message: i18n.str`Access denied`,
|
||||||
description: i18n.str`Check your token is valid 1`,
|
description: i18n.str`Check your token is valid`,
|
||||||
type: "ERROR",
|
type: "ERROR",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -266,7 +266,7 @@ export function useBackendBaseRequest(): useBackendBaseRequestType {
|
|||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestOptions = {},
|
options: RequestOptions = {},
|
||||||
): Promise<HttpResponseOk<T>> {
|
): Promise<HttpResponseOk<T>> {
|
||||||
return requestHandler<T>(backend, endpoint, { token, ...options }).then(res => {
|
return requestHandler<T>(backend, endpoint, { ...options, token }).then(res => {
|
||||||
return res
|
return res
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
throw err
|
throw err
|
||||||
|
@ -36,8 +36,8 @@ interface InstanceAPI {
|
|||||||
data: MerchantBackend.Instances.InstanceReconfigurationMessage,
|
data: MerchantBackend.Instances.InstanceReconfigurationMessage,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
deleteInstance: () => Promise<void>;
|
deleteInstance: () => Promise<void>;
|
||||||
clearToken: () => Promise<void>;
|
clearAccessToken: (currentToken: AccessToken | undefined) => Promise<void>;
|
||||||
setNewToken: (token: AccessToken) => Promise<void>;
|
setNewAccessToken: (currentToken: AccessToken | undefined, token: AccessToken) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAdminAPI(): AdminAPI {
|
export function useAdminAPI(): AdminAPI {
|
||||||
@ -111,18 +111,20 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
|
|||||||
mutateAll(/\/management\/instances/);
|
mutateAll(/\/management\/instances/);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearToken = async (): Promise<void> => {
|
const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
|
||||||
await request(`/management/instances/${instanceId}/auth`, {
|
await request(`/management/instances/${instanceId}/auth`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
token: currentToken,
|
||||||
data: { method: "external" },
|
data: { method: "external" },
|
||||||
});
|
});
|
||||||
|
|
||||||
mutateAll(/\/management\/instances/);
|
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`, {
|
await request(`/management/instances/${instanceId}/auth`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
token: currentToken,
|
||||||
data: { method: "token", token: newToken },
|
data: { method: "token", token: newToken },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,7 +139,7 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
|
|||||||
mutateAll(/\/management\/instances/);
|
mutateAll(/\/management\/instances/);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { updateInstance, deleteInstance, setNewToken, clearToken };
|
return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useInstanceAPI(): InstanceAPI {
|
export function useInstanceAPI(): InstanceAPI {
|
||||||
@ -172,18 +174,20 @@ export function useInstanceAPI(): InstanceAPI {
|
|||||||
mutate([`/private/`], null);
|
mutate([`/private/`], null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearToken = async (): Promise<void> => {
|
const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
|
||||||
await request(`/private/auth`, {
|
await request(`/private/auth`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
token: currentToken,
|
||||||
data: { method: "external" },
|
data: { method: "external" },
|
||||||
});
|
});
|
||||||
|
|
||||||
mutate([`/private/`], null);
|
mutate([`/private/`], null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setNewToken = async (newToken: AccessToken): Promise<void> => {
|
const setNewAccessToken = async (currentToken: AccessToken | undefined, newToken: AccessToken): Promise<void> => {
|
||||||
await request(`/private/auth`, {
|
await request(`/private/auth`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
token: currentToken,
|
||||||
data: { method: "token", token: newToken },
|
data: { method: "token", token: newToken },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -198,7 +202,7 @@ export function useInstanceAPI(): InstanceAPI {
|
|||||||
mutate([`/private/`], null);
|
mutate([`/private/`], null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { updateInstance, deleteInstance, setNewToken, clearToken };
|
return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useInstanceDetails(): HttpResponse<
|
export function useInstanceDetails(): HttpResponse<
|
||||||
|
@ -30,13 +30,13 @@ import { AccessToken } from "../../../declaration.js";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
currentToken: string | undefined;
|
hasToken: boolean | undefined;
|
||||||
onClearToken: () => void;
|
onClearToken: (c: AccessToken | undefined) => void;
|
||||||
onNewToken: (s: AccessToken) => void;
|
onNewToken: (c: AccessToken | undefined, s: AccessToken) => void;
|
||||||
onBack?: () => 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 };
|
type State = { old_token: string; new_token: string; repeat_token: string };
|
||||||
const [form, setValue] = useState<Partial<State>>({
|
const [form, setValue] = useState<Partial<State>>({
|
||||||
old_token: "",
|
old_token: "",
|
||||||
@ -45,11 +45,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
|
|||||||
});
|
});
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
const hasOldtoken = !!oldToken
|
|
||||||
const hasInputTheCorrectOldToken = hasOldtoken && oldToken !== form.old_token;
|
|
||||||
const errors = {
|
const errors = {
|
||||||
old_token: hasInputTheCorrectOldToken
|
old_token: hasToken && !form.old_token
|
||||||
? i18n.str`is not the same as the current access token`
|
? i18n.str`you need your access token to perform the operation`
|
||||||
: undefined,
|
: undefined,
|
||||||
new_token: !form.new_token
|
new_token: !form.new_token
|
||||||
? i18n.str`cannot be empty`
|
? i18n.str`cannot be empty`
|
||||||
@ -72,8 +70,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
|
|||||||
|
|
||||||
async function submitForm() {
|
async function submitForm() {
|
||||||
if (hasErrors) return;
|
if (hasErrors) return;
|
||||||
|
const ot = hasToken ? `secret-token:${form.old_token}` as AccessToken : undefined;
|
||||||
const nt = `secret-token:${form.new_token}` as AccessToken;
|
const nt = `secret-token:${form.new_token}` as AccessToken;
|
||||||
onNewToken(nt)
|
onNewToken(ot, nt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -98,16 +97,15 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
|
|||||||
<div class="column" />
|
<div class="column" />
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<FormProvider errors={errors} object={form} valueHandler={setValue}>
|
<FormProvider errors={errors} object={form} valueHandler={setValue}>
|
||||||
{hasOldtoken && (
|
<Fragment>
|
||||||
|
{hasToken && (
|
||||||
|
<Fragment>
|
||||||
<Input<State>
|
<Input<State>
|
||||||
name="old_token"
|
name="old_token"
|
||||||
label={i18n.str`Current access token`}
|
label={i18n.str`Current access token`}
|
||||||
tooltip={i18n.str`access token currently in use`}
|
tooltip={i18n.str`access token currently in use`}
|
||||||
inputType="password"
|
inputType="password"
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{!hasInputTheCorrectOldToken && <Fragment>
|
|
||||||
{hasOldtoken && <Fragment>
|
|
||||||
<p>
|
<p>
|
||||||
<i18n.Translate>
|
<i18n.Translate>
|
||||||
Clearing the access token will mean public access to the instance.
|
Clearing the access token will mean public access to the instance.
|
||||||
@ -115,15 +113,22 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
|
|||||||
</p>
|
</p>
|
||||||
<div class="buttons is-right mt-5">
|
<div class="buttons is-right mt-5">
|
||||||
<button
|
<button
|
||||||
disabled={!!hasInputTheCorrectOldToken}
|
|
||||||
class="button"
|
class="button"
|
||||||
onClick={onClearToken}
|
onClick={() => {
|
||||||
|
if (hasToken) {
|
||||||
|
const ot = `secret-token:${form.old_token}` as AccessToken;
|
||||||
|
onClearToken(ot)
|
||||||
|
} else {
|
||||||
|
onClearToken(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<i18n.Translate>Clear token</i18n.Translate>
|
<i18n.Translate>Clear token</i18n.Translate>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<Input<State>
|
<Input<State>
|
||||||
name="new_token"
|
name="new_token"
|
||||||
@ -137,7 +142,7 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo
|
|||||||
tooltip={i18n.str`confirm the same access token`}
|
tooltip={i18n.str`confirm the same access token`}
|
||||||
inputType="password"
|
inputType="password"
|
||||||
/>
|
/>
|
||||||
</Fragment>}
|
</Fragment>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
<div class="buttons is-right mt-5">
|
<div class="buttons is-right mt-5">
|
||||||
{onBack && (
|
{onBack && (
|
||||||
|
@ -33,8 +33,6 @@ interface Props {
|
|||||||
onNotFound: () => VNode;
|
onNotFound: () => VNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PREFIX = "secret-token:"
|
|
||||||
|
|
||||||
export default function Token({
|
export default function Token({
|
||||||
onLoadError,
|
onLoadError,
|
||||||
onChange,
|
onChange,
|
||||||
@ -44,21 +42,36 @@ export default function Token({
|
|||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
const [notif, setNotif] = useState<Notification | undefined>(undefined);
|
const [notif, setNotif] = useState<Notification | undefined>(undefined);
|
||||||
const { clearToken, setNewToken } = useInstanceAPI();
|
const { clearAccessToken, setNewAccessToken } = useInstanceAPI();
|
||||||
const { token: rootToken } = useBackendContext();
|
const { id } = useInstanceContext();
|
||||||
const { token: instanceToken, id, admin } = 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 (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<NotificationCard notification={notif} />
|
<NotificationCard notification={notif} />
|
||||||
<DetailPage
|
<DetailPage
|
||||||
instanceId={id}
|
instanceId={id}
|
||||||
currentToken={hasPrefix ? currentToken.token.substring(PREFIX.length) : currentToken?.token}
|
hasToken={hasToken}
|
||||||
onClearToken={async (): Promise<void> => {
|
onClearToken={async (currentToken): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await clearToken();
|
await clearAccessToken(currentToken);
|
||||||
onChange();
|
onChange();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof 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 {
|
try {
|
||||||
await setNewToken(newToken);
|
await setNewAccessToken(currentToken, newToken);
|
||||||
onChange();
|
onChange();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user