anastasis-webui: more auth types

This commit is contained in:
Florian Dold 2021-10-13 11:35:24 +02:00
parent aba71d0782
commit 9d6967dbab
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 194 additions and 21 deletions

View File

@ -46,6 +46,7 @@ export interface ReducerStateRecovery {
continents: any; continents: any;
countries: any; countries: any;
required_attributes: any;
} }
export interface ReducerStateError { export interface ReducerStateError {
@ -74,6 +75,7 @@ export enum BackupStates {
export enum RecoveryStates { export enum RecoveryStates {
ContinentSelecting = "CONTINENT_SELECTING", ContinentSelecting = "CONTINENT_SELECTING",
CountrySelecting = "COUNTRY_SELECTING", CountrySelecting = "COUNTRY_SELECTING",
UserAttributesCollecting = "USER_ATTRIBUTES_COLLECTING",
} }
const reducerBaseUrl = "http://localhost:5000/"; const reducerBaseUrl = "http://localhost:5000/";

View File

@ -1,10 +1,15 @@
import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import {
canonicalJson,
encodeCrock,
stringToBytes,
} from "@gnu-taler/taler-util";
import { FunctionalComponent, h } from "preact"; import { FunctionalComponent, h } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { import {
AnastasisReducerApi, AnastasisReducerApi,
AuthMethod, AuthMethod,
BackupStates, BackupStates,
RecoveryStates,
ReducerStateBackup, ReducerStateBackup,
ReducerStateRecovery, ReducerStateRecovery,
useAnastasisReducer, useAnastasisReducer,
@ -94,14 +99,23 @@ const Home: FunctionalComponent = () => {
} }
console.log("state", reducer.currentReducerState); console.log("state", reducer.currentReducerState);
if (reducerState.backup_state === BackupStates.ContinentSelecting) { if (
reducerState.backup_state === BackupStates.ContinentSelecting ||
reducerState.recovery_state === RecoveryStates.ContinentSelecting
) {
return <ContinentSelection reducer={reducer} reducerState={reducerState} />; return <ContinentSelection reducer={reducer} reducerState={reducerState} />;
} }
if (reducerState.backup_state === BackupStates.CountrySelecting) { if (
reducerState.backup_state === BackupStates.CountrySelecting ||
reducerState.recovery_state === RecoveryStates.CountrySelecting
) {
return <CountrySelection reducer={reducer} reducerState={reducerState} />; return <CountrySelection reducer={reducer} reducerState={reducerState} />;
} }
if (reducerState.backup_state === BackupStates.UserAttributesCollecting) { if (
return <AttributeEntry reducer={reducer} backupState={reducerState} />; reducerState.backup_state === BackupStates.UserAttributesCollecting ||
reducerState.recovery_state === RecoveryStates.UserAttributesCollecting
) {
return <AttributeEntry reducer={reducer} reducerState={reducerState} />;
} }
if (reducerState.backup_state === BackupStates.AuthenticationsEditing) { if (reducerState.backup_state === BackupStates.AuthenticationsEditing) {
return ( return (
@ -305,6 +319,15 @@ interface AuthMethodSetupProps {
function AuthMethodSmsSetup(props: AuthMethodSetupProps) { function AuthMethodSmsSetup(props: AuthMethodSetupProps) {
const [mobileNumber, setMobileNumber] = useState(""); const [mobileNumber, setMobileNumber] = useState("");
const addSmsAuth = () => {
props.addAuthMethod({
authentication_method: {
type: "sms",
instructions: `SMS to ${mobileNumber}`,
challenge: encodeCrock(stringToBytes(mobileNumber)),
},
});
};
return ( return (
<div class={style.home}> <div class={style.home}>
<h1>Add {props.method} authentication</h1> <h1>Add {props.method} authentication</h1>
@ -325,19 +348,7 @@ function AuthMethodSmsSetup(props: AuthMethodSetupProps) {
</label> </label>
<div> <div>
<button onClick={() => props.cancel()}>Cancel</button> <button onClick={() => props.cancel()}>Cancel</button>
<button <button onClick={() => addSmsAuth()}>Add</button>
onClick={() =>
props.addAuthMethod({
authentication_method: {
type: "sms",
instructions: `SMS to ${mobileNumber}`,
challenge: "E1QPPS8A",
},
})
}
>
Add
</button>
</div> </div>
</div> </div>
</div> </div>
@ -400,6 +411,150 @@ function AuthMethodQuestionSetup(props: AuthMethodSetupProps) {
); );
} }
function AuthMethodEmailSetup(props: AuthMethodSetupProps) {
const [email, setEmail] = useState("");
return (
<div class={style.home}>
<h1>Add {props.method} authentication</h1>
<div>
<p>
For email authentication, you need to provid an email address. When
recovering your secret, you need to enter the code you will receive by
email.
</p>
<div>
<label>
Email address
<input
value={email}
autoFocus
onChange={(e) => setEmail((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<button onClick={() => props.cancel()}>Cancel</button>
<button
onClick={() =>
props.addAuthMethod({
authentication_method: {
type: "email",
instructions: `Email to ${email}`,
challenge: encodeCrock(stringToBytes(email)),
},
})
}
>
Add
</button>
</div>
</div>
</div>
);
}
function AuthMethodPostSetup(props: AuthMethodSetupProps) {
const [fullName, setFullName] = useState("");
const [street, setStreet] = useState("");
const [city, setCity] = useState("");
const [postcode, setPostcode] = useState("");
const [country, setCountry] = useState("");
return (
<div class={style.home}>
<h1>Add {props.method} authentication</h1>
<div>
<p>
For postal letter authentication, you need to provide a postal
address. When recovering your secret, you will be asked to enter a
code that you will receive in a letter to that address.
</p>
<div>
<label>
Full Name
<input
value={fullName}
autoFocus
onChange={(e) => setFullName((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<label>
Street
<input
value={street}
autoFocus
onChange={(e) => setStreet((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<label>
City
<input
value={city}
autoFocus
onChange={(e) => setCity((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<label>
Postal Code
<input
value={postcode}
autoFocus
onChange={(e) => setPostcode((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<label>
Country
<input
value={country}
autoFocus
onChange={(e) => setCountry((e.target as any).value)}
type="text"
/>
</label>
</div>
<div>
<button onClick={() => props.cancel()}>Cancel</button>
<button
onClick={() =>
props.addAuthMethod({
authentication_method: {
type: "email",
instructions: `Letter to address in postal code ${postcode}`,
challenge: encodeCrock(
stringToBytes(
canonicalJson({
full_name: fullName,
street,
city,
postcode,
country,
}),
),
),
},
})
}
>
Add
</button>
</div>
</div>
</div>
);
}
function AuthMethodNotImplemented(props: AuthMethodSetupProps) { function AuthMethodNotImplemented(props: AuthMethodSetupProps) {
return ( return (
<div class={style.home}> <div class={style.home}>
@ -452,7 +607,23 @@ function AuthenticationEditor(props: AuthenticationEditorProps) {
<AuthMethodQuestionSetup <AuthMethodQuestionSetup
cancel={cancel} cancel={cancel}
addAuthMethod={addMethod} addAuthMethod={addMethod}
method="sms" method="question"
/>
);
case "email":
return (
<AuthMethodEmailSetup
cancel={cancel}
addAuthMethod={addMethod}
method="email"
/>
);
case "post":
return (
<AuthMethodPostSetup
cancel={cancel}
addAuthMethod={addMethod}
method="post"
/> />
); );
default: default:
@ -525,11 +696,11 @@ function AuthenticationEditor(props: AuthenticationEditorProps) {
export interface AttributeEntryProps { export interface AttributeEntryProps {
reducer: AnastasisReducerApi; reducer: AnastasisReducerApi;
backupState: ReducerStateBackup; reducerState: ReducerStateRecovery | ReducerStateBackup;
} }
function AttributeEntry(props: AttributeEntryProps) { function AttributeEntry(props: AttributeEntryProps) {
const { reducer, backupState } = props; const { reducer, reducerState: backupState } = props;
const [attrs, setAttrs] = useState<Record<string, string>>({}); const [attrs, setAttrs] = useState<Record<string, string>>({});
return ( return (
<div class={style.home}> <div class={style.home}>