accesstoken in memory and better login when switching between accounts
This commit is contained in:
parent
44aeaba7b4
commit
37d0f9438e
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useTranslationContext } from "@gnu-taler/web-util/browser";
|
import { useTranslationContext } from "@gnu-taler/web-util/browser";
|
||||||
import { h, VNode } from "preact";
|
import { ComponentChildren, h, VNode } from "preact";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { useBackendContext } from "../../context/backend.js";
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useInstanceContext } from "../../context/instance.js";
|
import { useInstanceContext } from "../../context/instance.js";
|
||||||
@ -40,7 +40,7 @@ function getTokenValuePart(t: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeToken(r: string): string {
|
function normalizeToken(r: string): string {
|
||||||
return `secret-token:${encodeURIComponent(r)}`;
|
return `secret-token:${r}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanUp(s: string): string {
|
function cleanUp(s: string): string {
|
||||||
@ -53,7 +53,7 @@ function cleanUp(s: string): string {
|
|||||||
|
|
||||||
export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
||||||
const { url: backendUrl, token: baseToken } = useBackendContext();
|
const { url: backendUrl, token: baseToken } = useBackendContext();
|
||||||
const { admin, token: instanceToken } = useInstanceContext();
|
const { admin, token: instanceToken, id } = useInstanceContext();
|
||||||
const testLogin = useCredentialsChecker();
|
const testLogin = useCredentialsChecker();
|
||||||
const currentToken = getTokenValuePart(
|
const currentToken = getTokenValuePart(
|
||||||
(!admin ? baseToken : instanceToken) ?? "",
|
(!admin ? baseToken : instanceToken) ?? "",
|
||||||
@ -63,6 +63,78 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
|||||||
const [url, setURL] = useState(cleanUp(backendUrl));
|
const [url, setURL] = useState(cleanUp(backendUrl));
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
|
if (admin && id !== "default") {
|
||||||
|
//admin trying to access another instance
|
||||||
|
return (<div class="columns is-centered" style={{ margin: "auto" }}>
|
||||||
|
<div class="column is-two-thirds ">
|
||||||
|
<div class="modal-card" style={{ width: "100%", margin: 0 }}>
|
||||||
|
<header
|
||||||
|
class="modal-card-head"
|
||||||
|
style={{ border: "1px solid", borderBottom: 0 }}
|
||||||
|
>
|
||||||
|
<p class="modal-card-title">{i18n.str`Login required`}</p>
|
||||||
|
</header>
|
||||||
|
<section
|
||||||
|
class="modal-card-body"
|
||||||
|
style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<i18n.Translate>Need the access token for the instance.</i18n.Translate>
|
||||||
|
</p>
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">
|
||||||
|
<i18n.Translate>Access Token</i18n.Translate>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="password"
|
||||||
|
placeholder={"set new access token"}
|
||||||
|
name="token"
|
||||||
|
onKeyPress={(e) =>
|
||||||
|
e.keyCode === 13
|
||||||
|
? onConfirm(url, normalizeToken(token))
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
value={token}
|
||||||
|
onInput={(e): void => setToken(e?.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer
|
||||||
|
class="modal-card-foot "
|
||||||
|
style={{
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
border: "1px solid",
|
||||||
|
borderTop: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AsyncButton
|
||||||
|
onClick={async () => {
|
||||||
|
const secretToken = normalizeToken(token);
|
||||||
|
const { valid, cause } = await testLogin(`${url}/instances/${id}`, secretToken);
|
||||||
|
if (valid) {
|
||||||
|
onConfirm(url, secretToken);
|
||||||
|
} else {
|
||||||
|
onConfirm(url);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i18n.Translate>Confirm</i18n.Translate>
|
||||||
|
</AsyncButton>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="columns is-centered" style={{ margin: "auto" }}>
|
<div class="columns is-centered" style={{ margin: "auto" }}>
|
||||||
<div class="column is-two-thirds ">
|
<div class="column is-two-thirds ">
|
||||||
@ -137,8 +209,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
|||||||
borderTop: 0,
|
borderTop: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<AsyncButton
|
||||||
class="button is-info"
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const secretToken = normalizeToken(token);
|
const secretToken = normalizeToken(token);
|
||||||
const { valid, cause } = await testLogin(url, secretToken);
|
const { valid, cause } = await testLogin(url, secretToken);
|
||||||
@ -150,10 +221,24 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i18n.Translate>Confirm</i18n.Translate>
|
<i18n.Translate>Confirm</i18n.Translate>
|
||||||
</button>
|
</AsyncButton>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AsyncButton({onClick, children}:{onClick: () => Promise<void>, children: ComponentChildren}):VNode {
|
||||||
|
const [running, setRunning] = useState(false)
|
||||||
|
return <button class="button is-info" disabled={running} onClick={() => {
|
||||||
|
setRunning(true)
|
||||||
|
onClick().then(() => {
|
||||||
|
setRunning(false)
|
||||||
|
}).catch(() => {
|
||||||
|
setRunning(false)
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
@ -130,7 +130,12 @@ export function Menu({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{mimic && (
|
{mimic && (
|
||||||
<nav class="level">
|
<nav class="level" style={{
|
||||||
|
zIndex: 100,
|
||||||
|
position:"fixed",
|
||||||
|
width:"50%",
|
||||||
|
marginLeft: "20%"
|
||||||
|
}}>
|
||||||
<div class="level-item has-text-centered has-background-warning">
|
<div class="level-item has-text-centered has-background-warning">
|
||||||
<p class="is-size-5">
|
<p class="is-size-5">
|
||||||
You are viewing the instance <b>"{instance}"</b>.{" "}
|
You are viewing the instance <b>"{instance}"</b>.{" "}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import { StateUpdater, useCallback, useState } from "preact/hooks";
|
import { StateUpdater, useCallback, useState } from "preact/hooks";
|
||||||
import { ValueOrFunction } from "../utils/types.js";
|
import { ValueOrFunction } from "../utils/types.js";
|
||||||
|
import { useMemoryStorage } from "@gnu-taler/web-util/browser";
|
||||||
|
|
||||||
const calculateRootPath = () => {
|
const calculateRootPath = () => {
|
||||||
const rootPath =
|
const rootPath =
|
||||||
@ -52,14 +53,15 @@ export function useBackendURL(
|
|||||||
|
|
||||||
export function useBackendDefaultToken(
|
export function useBackendDefaultToken(
|
||||||
initialValue?: string,
|
initialValue?: string,
|
||||||
): [string | undefined, StateUpdater<string | undefined>] {
|
): [string | undefined, ((d:string | undefined) => void)] {
|
||||||
return useLocalStorage("backend-token", initialValue);
|
const {update, value} = useMemoryStorage(`backend-token`, initialValue)
|
||||||
|
return [value, update];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBackendInstanceToken(
|
export function useBackendInstanceToken(
|
||||||
id: string,
|
id: string,
|
||||||
): [string | undefined, StateUpdater<string | undefined>] {
|
): [string | undefined, ((d:string | undefined) => void)] {
|
||||||
const [token, setToken] = useLocalStorage(`backend-token-${id}`);
|
const {update:setToken, value:token, reset} = useMemoryStorage(`backend-token-${id}`)
|
||||||
const [defaultToken, defaultSetToken] = useBackendDefaultToken();
|
const [defaultToken, defaultSetToken] = useBackendDefaultToken();
|
||||||
|
|
||||||
// instance named 'default' use the default token
|
// instance named 'default' use the default token
|
||||||
@ -67,15 +69,16 @@ export function useBackendInstanceToken(
|
|||||||
return [defaultToken, defaultSetToken];
|
return [defaultToken, defaultSetToken];
|
||||||
}
|
}
|
||||||
function updateToken(
|
function updateToken(
|
||||||
value:
|
value: (string | undefined)
|
||||||
| (string | undefined)
|
|
||||||
| ((s: string | undefined) => string | undefined),
|
|
||||||
): void {
|
): void {
|
||||||
setToken((p) => {
|
console.log("seeting token", value)
|
||||||
const toStore = value instanceof Function ? value(p) : value;
|
if (value === undefined) {
|
||||||
return toStore;
|
reset()
|
||||||
});
|
} else {
|
||||||
|
setToken(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
console.log("token", token)
|
||||||
|
|
||||||
return [token, updateToken];
|
return [token, updateToken];
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import { Link } from "preact-router";
|
|||||||
export default function NotFoundPage(): VNode {
|
export default function NotFoundPage(): VNode {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Error 404</h1>
|
|
||||||
<p>That page doesn't exist.</p>
|
<p>That page doesn't exist.</p>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<h4>Back to Home</h4>
|
<h4>Back to Home</h4>
|
||||||
|
Loading…
Reference in New Issue
Block a user