accesstoken in memory and better login when switching between accounts

This commit is contained in:
Sebastian 2023-08-04 13:32:08 -03:00
parent 44aeaba7b4
commit 37d0f9438e
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
4 changed files with 111 additions and 19 deletions

View File

@ -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>
}

View File

@ -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>&quot;{instance}&quot;</b>.{" "} You are viewing the instance <b>&quot;{instance}&quot;</b>.{" "}

View File

@ -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];
} }

View File

@ -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&apos;t exist.</p> <p>That page doesn&apos;t exist.</p>
<Link href="/"> <Link href="/">
<h4>Back to Home</h4> <h4>Back to Home</h4>