parent
171d070a83
commit
1374b37d26
@ -39,15 +39,15 @@ export const Backup = createExample(TestedComponent, {
|
|||||||
...reducerStatesExample.backupAttributeEditing,
|
...reducerStatesExample.backupAttributeEditing,
|
||||||
required_attributes: [
|
required_attributes: [
|
||||||
{
|
{
|
||||||
name: "first name",
|
name: "full_name",
|
||||||
label: "first",
|
label: "Full name",
|
||||||
type: "string",
|
type: "string",
|
||||||
uuid: "asdasdsa1",
|
uuid: "asdasdsa1",
|
||||||
widget: "wid",
|
widget: "wid",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "last name",
|
name: "birthplace",
|
||||||
label: "second",
|
label: "Birthplace",
|
||||||
type: "string",
|
type: "string",
|
||||||
uuid: "asdasdsa2",
|
uuid: "asdasdsa2",
|
||||||
widget: "wid",
|
widget: "wid",
|
||||||
@ -66,15 +66,15 @@ export const Recovery = createExample(TestedComponent, {
|
|||||||
...reducerStatesExample.recoveryAttributeEditing,
|
...reducerStatesExample.recoveryAttributeEditing,
|
||||||
required_attributes: [
|
required_attributes: [
|
||||||
{
|
{
|
||||||
name: "first",
|
name: "full_name",
|
||||||
label: "first",
|
label: "Full name",
|
||||||
type: "string",
|
type: "string",
|
||||||
uuid: "asdasdsa1",
|
uuid: "asdasdsa1",
|
||||||
widget: "wid",
|
widget: "wid",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pepe",
|
name: "birthplace",
|
||||||
label: "second",
|
label: "Birthplace",
|
||||||
type: "string",
|
type: "string",
|
||||||
uuid: "asdasdsa2",
|
uuid: "asdasdsa2",
|
||||||
widget: "wid",
|
widget: "wid",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { UserAttributeSpec, validators } from "@gnu-taler/anastasis-core";
|
import { UserAttributeSpec, validators } from "@gnu-taler/anastasis-core";
|
||||||
import { isAfter, parse } from "date-fns";
|
import { isAfter, parse } from "date-fns";
|
||||||
import { h, VNode } from "preact";
|
import { Fragment, h, VNode } from "preact";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { DateInput } from "../../components/fields/DateInput";
|
import { DateInput } from "../../components/fields/DateInput";
|
||||||
import { PhoneNumberInput } from "../../components/fields/NumberInput";
|
import { PhoneNumberInput } from "../../components/fields/NumberInput";
|
||||||
@ -122,7 +122,7 @@ for (let i = 0; i < 100; i++) {
|
|||||||
}
|
}
|
||||||
function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ marginTop: 16 }}>
|
||||||
{props.spec.type === "date" && (
|
{props.spec.type === "date" && (
|
||||||
<DateInput
|
<DateInput
|
||||||
grabFocus={props.isFirst}
|
grabFocus={props.isFirst}
|
||||||
@ -151,6 +151,17 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
|||||||
bind={[props.value, props.setValue]}
|
bind={[props.value, props.setValue]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{props.spec.type === "string" && (
|
||||||
|
<div>
|
||||||
|
This field is case-sensitive. You must enter exactly the same value
|
||||||
|
during recovery.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.spec.name === "full_name" && (
|
||||||
|
<div>
|
||||||
|
If possible, use "LASTNAME, Firstname(s)" without abbreviations.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
This stays private
|
This stays private
|
||||||
<span class="icon is-right">
|
<span class="icon is-right">
|
||||||
|
@ -75,11 +75,15 @@ export function SecretEditorScreen(): VNode {
|
|||||||
<div class="block">
|
<div class="block">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Secret name:"
|
label="Secret name:"
|
||||||
tooltip="The secret name allows you to identify your secret when restoring it. It is a label that you can choose freely."
|
tooltip="This allows you to uniquely identify a secret if you have made multiple back ups. The value entered here will NOT be protected by the authentication checks!"
|
||||||
grabFocus
|
grabFocus
|
||||||
onConfirm={goNextIfNoErrors}
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[secretName, setSecretName]}
|
bind={[secretName, setSecretName]}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
Names should be unique, so that you can easily identify your secret
|
||||||
|
later.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<TextInput
|
<TextInput
|
||||||
|
@ -12,7 +12,28 @@ import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
|||||||
import { AuthMethodSolveProps } from "./index";
|
import { AuthMethodSolveProps } from "./index";
|
||||||
|
|
||||||
export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
||||||
const [answer, setAnswer] = useState("A-");
|
const [answer, _setAnswer] = useState("A-");
|
||||||
|
|
||||||
|
function setAnswer(str: string): void {
|
||||||
|
//A-12345-678-1234-5678
|
||||||
|
const unformatted = str
|
||||||
|
.replace(/^A-/, "")
|
||||||
|
.replace(/-/g, "")
|
||||||
|
.toLocaleUpperCase();
|
||||||
|
|
||||||
|
let result = `A-${unformatted.substring(0, 5)}`;
|
||||||
|
if (unformatted.length > 5) {
|
||||||
|
result += `-${unformatted.substring(5, 8)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 8) {
|
||||||
|
result += `-${unformatted.substring(8, 12)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 12) {
|
||||||
|
result += `-${unformatted.substring(12, 16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAnswer(result);
|
||||||
|
}
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
@ -77,7 +98,9 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
const feedback = challengeFeedback[selectedUuid];
|
const feedback = challengeFeedback[selectedUuid];
|
||||||
|
|
||||||
async function onNext(): Promise<void> {
|
async function onNext(): Promise<void> {
|
||||||
return reducer?.transition("solve_challenge", { answer });
|
return reducer?.transition("solve_challenge", {
|
||||||
|
answer: `A-${answer.replace(/^A-/, "").replace(/-/g, "").trim()}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function onCancel(): void {
|
function onCancel(): void {
|
||||||
reducer?.back();
|
reducer?.back();
|
||||||
@ -127,7 +150,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
grabFocus
|
grabFocus
|
||||||
onConfirm={onNext}
|
onConfirm={onNext}
|
||||||
bind={[answer, setAnswer]}
|
bind={[answer, setAnswer]}
|
||||||
placeholder="A-1234567812345678"
|
placeholder="A-12345-678-1234-5678"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -12,7 +12,28 @@ import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
|||||||
import { AuthMethodSolveProps } from "./index";
|
import { AuthMethodSolveProps } from "./index";
|
||||||
|
|
||||||
export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
||||||
const [answer, setAnswer] = useState("A-");
|
const [answer, _setAnswer] = useState("A-");
|
||||||
|
|
||||||
|
function setAnswer(str: string): void {
|
||||||
|
//A-12345-678-1234-5678
|
||||||
|
const unformatted = str
|
||||||
|
.replace(/^A-/, "")
|
||||||
|
.replace(/-/g, "")
|
||||||
|
.toLocaleUpperCase();
|
||||||
|
|
||||||
|
let result = `A-${unformatted.substring(0, 5)}`;
|
||||||
|
if (unformatted.length > 5) {
|
||||||
|
result += `-${unformatted.substring(5, 8)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 8) {
|
||||||
|
result += `-${unformatted.substring(8, 12)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 12) {
|
||||||
|
result += `-${unformatted.substring(12, 16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAnswer(result);
|
||||||
|
}
|
||||||
|
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
if (!reducer) {
|
if (!reducer) {
|
||||||
@ -76,7 +97,9 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
const feedback = challengeFeedback[selectedUuid];
|
const feedback = challengeFeedback[selectedUuid];
|
||||||
|
|
||||||
async function onNext(): Promise<void> {
|
async function onNext(): Promise<void> {
|
||||||
return reducer?.transition("solve_challenge", { answer });
|
return reducer?.transition("solve_challenge", {
|
||||||
|
answer: `A-${answer.replace(/^A-/, "").replace(/-/g, "").trim()}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function onCancel(): void {
|
function onCancel(): void {
|
||||||
reducer?.back();
|
reducer?.back();
|
||||||
@ -96,6 +119,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
onConfirm={onNext}
|
onConfirm={onNext}
|
||||||
label="Answer"
|
label="Answer"
|
||||||
grabFocus
|
grabFocus
|
||||||
|
placeholder="A-12345-678-1234-5678"
|
||||||
bind={[answer, setAnswer]}
|
bind={[answer, setAnswer]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -5,12 +5,18 @@ import { AuthMethodSetupProps } from ".";
|
|||||||
import { PhoneNumberInput } from "../../../components/fields/NumberInput";
|
import { PhoneNumberInput } from "../../../components/fields/NumberInput";
|
||||||
import { AnastasisClientFrame } from "../index";
|
import { AnastasisClientFrame } from "../index";
|
||||||
|
|
||||||
|
const REGEX_JUST_NUMBERS = /^\+[0-9 ]*$/;
|
||||||
|
|
||||||
|
function isJustNumbers(str: string): boolean {
|
||||||
|
return REGEX_JUST_NUMBERS.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
export function AuthMethodSmsSetup({
|
export function AuthMethodSmsSetup({
|
||||||
addAuthMethod,
|
addAuthMethod,
|
||||||
cancel,
|
cancel,
|
||||||
configured,
|
configured,
|
||||||
}: AuthMethodSetupProps): VNode {
|
}: AuthMethodSetupProps): VNode {
|
||||||
const [mobileNumber, setMobileNumber] = useState("");
|
const [mobileNumber, setMobileNumber] = useState("+");
|
||||||
const addSmsAuth = (): void => {
|
const addSmsAuth = (): void => {
|
||||||
addAuthMethod({
|
addAuthMethod({
|
||||||
authentication_method: {
|
authentication_method: {
|
||||||
@ -24,7 +30,13 @@ export function AuthMethodSmsSetup({
|
|||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
const errors = !mobileNumber ? "Add a mobile number" : undefined;
|
const errors = !mobileNumber
|
||||||
|
? "Add a mobile number"
|
||||||
|
: !mobileNumber.startsWith("+")
|
||||||
|
? "Mobile number should start with '+'"
|
||||||
|
: !isJustNumbers(mobileNumber)
|
||||||
|
? "Mobile number can't have other than numbers"
|
||||||
|
: undefined;
|
||||||
function goNextIfNoErrors(): void {
|
function goNextIfNoErrors(): void {
|
||||||
if (!errors) addSmsAuth();
|
if (!errors) addSmsAuth();
|
||||||
}
|
}
|
||||||
@ -41,9 +53,13 @@ export function AuthMethodSmsSetup({
|
|||||||
label="Mobile number"
|
label="Mobile number"
|
||||||
placeholder="Your mobile number"
|
placeholder="Your mobile number"
|
||||||
onConfirm={goNextIfNoErrors}
|
onConfirm={goNextIfNoErrors}
|
||||||
|
error={errors}
|
||||||
grabFocus
|
grabFocus
|
||||||
bind={[mobileNumber, setMobileNumber]}
|
bind={[mobileNumber, setMobileNumber]}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
Enter mobile number including +CC international dialing prefix.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{configured.length > 0 && (
|
{configured.length > 0 && (
|
||||||
<section class="section">
|
<section class="section">
|
||||||
|
@ -50,14 +50,15 @@ export const WithoutFeedback = createExample(
|
|||||||
cost: "USD:1",
|
cost: "USD:1",
|
||||||
instructions: "SMS to +54 11 2233 4455",
|
instructions: "SMS to +54 11 2233 4455",
|
||||||
type: "question",
|
type: "question",
|
||||||
uuid: "uuid-1",
|
uuid: "AHCC4ZJ3Z1AF8TWBKGVGEKCQ3R7HXHJ51MJ45NHNZMHYZTKJ9NW0",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
policies: [],
|
policies: [],
|
||||||
},
|
},
|
||||||
selected_challenge_uuid: "uuid-1",
|
selected_challenge_uuid:
|
||||||
|
"AHCC4ZJ3Z1AF8TWBKGVGEKCQ3R7HXHJ51MJ45NHNZMHYZTKJ9NW0",
|
||||||
} as ReducerState,
|
} as ReducerState,
|
||||||
{
|
{
|
||||||
id: "uuid-1",
|
id: "AHCC4ZJ3Z1AF8TWBKGVGEKCQ3R7HXHJ51MJ45NHNZMHYZTKJ9NW0",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,28 @@ import { SolveOverviewFeedbackDisplay } from "../SolveScreen";
|
|||||||
import { AuthMethodSolveProps } from "./index";
|
import { AuthMethodSolveProps } from "./index";
|
||||||
|
|
||||||
export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
||||||
const [answer, setAnswer] = useState("A-");
|
const [answer, _setAnswer] = useState("A-");
|
||||||
|
|
||||||
|
function setAnswer(str: string): void {
|
||||||
|
//A-12345-678-1234-5678
|
||||||
|
const unformatted = str
|
||||||
|
.replace(/^A-/, "")
|
||||||
|
.replace(/-/g, "")
|
||||||
|
.toLocaleUpperCase();
|
||||||
|
|
||||||
|
let result = `A-${unformatted.substring(0, 5)}`;
|
||||||
|
if (unformatted.length > 5) {
|
||||||
|
result += `-${unformatted.substring(5, 8)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 8) {
|
||||||
|
result += `-${unformatted.substring(8, 12)}`;
|
||||||
|
}
|
||||||
|
if (unformatted.length > 12) {
|
||||||
|
result += `-${unformatted.substring(12, 16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAnswer(result);
|
||||||
|
}
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
@ -77,7 +98,9 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
const feedback = challengeFeedback[selectedUuid];
|
const feedback = challengeFeedback[selectedUuid];
|
||||||
|
|
||||||
async function onNext(): Promise<void> {
|
async function onNext(): Promise<void> {
|
||||||
return reducer?.transition("solve_challenge", { answer });
|
return reducer?.transition("solve_challenge", {
|
||||||
|
answer: `A-${answer.replace(/^A-/, "").replace(/-/g, "").trim()}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function onCancel(): void {
|
function onCancel(): void {
|
||||||
reducer?.back();
|
reducer?.back();
|
||||||
@ -127,7 +150,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
grabFocus
|
grabFocus
|
||||||
onConfirm={onNext}
|
onConfirm={onNext}
|
||||||
bind={[answer, setAnswer]}
|
bind={[answer, setAnswer]}
|
||||||
placeholder="A-1234567812345678"
|
placeholder="A-12345-678-1234-5678"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -55,13 +55,19 @@ export function AuthMethodTotpSetup({
|
|||||||
<QR text={totpURL} />
|
<QR text={totpURL} />
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
After scanning the code with your TOTP App, test it in the input below.
|
Confirm that your TOTP App works by entering the current 8-digit TOTP
|
||||||
|
code here:
|
||||||
</p>
|
</p>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Test code"
|
label="Test code"
|
||||||
onConfirm={goNextIfNoErrors}
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[test, setTest]}
|
bind={[test, setTest]}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
We note that Google's implementation of TOTP is incomplete and will not
|
||||||
|
work. We recommend using FreeOTP+.
|
||||||
|
</div>
|
||||||
|
|
||||||
{configured.length > 0 && (
|
{configured.length > 0 && (
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="block">Your TOTP numbers:</div>
|
<div class="block">Your TOTP numbers:</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user