Skip to content

Commit 6d1e1c1

Browse files
author
Dave MacFarlane
committed
fix static tests
1 parent 7e1114b commit 6d1e1c1

File tree

1 file changed

+125
-82
lines changed

1 file changed

+125
-82
lines changed

modules/login/jsx/mfaPrompt.tsx

Lines changed: 125 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,139 @@
11
import {createRoot} from 'react-dom/client';
22
import {useState, useEffect, useCallback} from 'react';
33

4-
function Digit(props: {value: number|null|string, onChange: (newvalue: number) => boolean}) {
5-
return <input style={{flex: 1,
6-
width: "1em",
7-
fontSize: "3em",
8-
marginLeft: "0.5ex",
9-
textAlign: "center"
10-
}}
11-
type="text"
12-
readOnly={true}
13-
onKeyDown={(e: React.KeyboardEvent) => {
14-
e.preventDefault();
15-
if(e.keyCode >= 48 /* '0' */ && e.keyCode <= 57 /* '9' */) {
16-
if(props.onChange(e.keyCode-48)) {
17-
const target = e.target as HTMLElement;
18-
(target.nextSibling as HTMLElement)?.focus();
19-
}
20-
return;
21-
}
22-
if(e.key == "ArrowLeft") {
23-
const target = e.target as HTMLElement;
24-
(target.previousSibling as HTMLElement)?.focus();
25-
} if(e.key == "ArrowRight") {
26-
const target = e.target as HTMLElement;
27-
(target.nextSibling as HTMLElement)?.focus();
28-
}
29-
}
30-
}
31-
value={props.value || ""}
32-
/>;
4+
/**
5+
* Render a single digit of an MFA prompt
6+
*
7+
* @param props - React props
8+
* @param props.value - The current value of the digit to display
9+
* @param props.onChange - A callback to call when the number is changed
10+
*/
11+
function Digit(props: {
12+
value: number|null|string,
13+
onChange: (newvalue: number) => boolean}
14+
) {
15+
return <input style={{flex: 1,
16+
width: '1em',
17+
fontSize: '3em',
18+
marginLeft: '0.5ex',
19+
textAlign: 'center',
20+
}}
21+
type="text"
22+
readOnly={true}
23+
onKeyDown={(e: React.KeyboardEvent) => {
24+
e.preventDefault();
25+
if (e.keyCode >= 48 /* '0' */ && e.keyCode <= 57 /* '9' */) {
26+
if (props.onChange(e.keyCode-48)) {
27+
const target = e.target as HTMLElement;
28+
(target.nextSibling as HTMLElement)?.focus();
29+
}
30+
return;
31+
}
32+
if (e.key == 'ArrowLeft') {
33+
const target = e.target as HTMLElement;
34+
(target.previousSibling as HTMLElement)?.focus();
35+
} if (e.key == 'ArrowRight') {
36+
const target = e.target as HTMLElement;
37+
(target.nextSibling as HTMLElement)?.focus();
38+
}
39+
}
40+
}
41+
value={props.value || ''}
42+
/>;
3343
}
44+
/**
45+
*
46+
* @param props - React prop
47+
* @param props.onValidate - Callback when a valid code is entered
48+
*/
3449
function CodePrompt(props: {onValidate: () => void}) {
35-
const [code, setCode] = useState<[number|null, number|null, number|null, number|null, number|null, number|null]>([null, null, null, null, null, null]);
36-
const digitCallback = useCallback( (index: number, value: number): boolean => {
37-
if(value >= 0 && value <= 9) {
38-
code[index] = value;
39-
setCode([...code]);
40-
return true;
41-
}
42-
return false;
43-
}, []);
44-
useEffect( () => {
45-
if(code.indexOf(null) >= 0) {
46-
console.log("not yet entered");
47-
return;
48-
}
49-
fetch("/login/mfa",
50-
{
51-
method: "POST",
52-
body: JSON.stringify({"code": code.join("")}),
53-
credentials: 'same-origin',
54-
55-
}
56-
).then( (resp) => {
57-
if(!resp.ok) {
58-
console.warn("invalid response");
59-
}
60-
return resp.json();
61-
}).then( (json) => {
62-
if(json["success"]) {
63-
window.location.reload();
64-
}
65-
console.log(json);
66-
}).catch( (e) => {
67-
console.error("error validating code");
68-
});
69-
70-
console.log("validate code")
71-
//call onValidate
72-
}, [code]);
50+
const [code, setCode] = useState<[
51+
number|null,
52+
number|null,
53+
number|null,
54+
number|null,
55+
number|null,
56+
number|null]>([null, null, null, null, null, null]);
57+
const digitCallback = useCallback(
58+
(index: number, value: number): boolean => {
59+
if (value >= 0 && value <= 9) {
60+
code[index] = value;
61+
setCode([...code]);
62+
return true;
63+
}
64+
return false;
65+
},
66+
[]
67+
);
68+
useEffect( () => {
69+
if (code.indexOf(null) >= 0) {
70+
return;
71+
}
72+
fetch('/login/mfa',
73+
{
74+
method: 'POST',
75+
body: JSON.stringify({'code': code.join('')}),
76+
credentials: 'same-origin',
77+
}
78+
).then( (resp) => {
79+
if (!resp.ok) {
80+
console.warn('invalid response');
81+
}
82+
return resp.json();
83+
}).then( (json) => {
84+
if (json['success']) {
85+
props.onValidate();
86+
}
87+
}).catch( () => {
88+
console.error('error validating code');
89+
});
90+
},
91+
[code]
92+
);
7393

7494

75-
76-
return <div style={{display: "flex"}}>
77-
<Digit value={code[0] === 0 ? "0" : code[0]} onChange={(newval: number) => digitCallback(0, newval)}/>
78-
<Digit value={code[1] === 0 ? "0" : code[1]} onChange={(newval: number) => digitCallback(1, newval)}/>
79-
<Digit value={code[2] === 0 ? "0" : code[2]} onChange={(newval: number) => digitCallback(2, newval)}/>
80-
<Digit value={code[3] === 0 ? "0" : code[3]} onChange={(newval: number) => digitCallback(3, newval)}/>
81-
<Digit value={code[4] === 0 ? "0" : code[4]} onChange={(newval: number) => digitCallback(4, newval)}/>
82-
<Digit value={code[5] === 0 ? "0" : code[5]} onChange={(newval: number) => digitCallback(5, newval)}/>
83-
</div>;
95+
// nb. React treats the number 0 as falsey and doesn't display it when passed
96+
// to an input value but *does* display the string "0".
97+
return <div style={{display: 'flex'}}>
98+
<Digit
99+
value={code[0] === 0 ? '0' : code[0]}
100+
onChange={(newval: number) => digitCallback(0, newval)}
101+
/>
102+
<Digit
103+
value={code[1] === 0 ? '0' : code[1]}
104+
onChange={(newval: number) => digitCallback(1, newval)}
105+
/>
106+
<Digit
107+
value={code[2] === 0 ? '0' : code[2]}
108+
onChange={(newval: number) => digitCallback(2, newval)}
109+
/>
110+
<Digit
111+
value={code[3] === 0 ? '0' : code[3]}
112+
onChange={(newval: number) => digitCallback(3, newval)}
113+
/>
114+
<Digit
115+
value={code[4] === 0 ? '0' : code[4]}
116+
onChange={(newval: number) => digitCallback(4, newval)}
117+
/>
118+
<Digit
119+
value={code[5] === 0 ? '0' : code[5]}
120+
onChange={(newval: number) => digitCallback(5, newval)}
121+
/>
122+
</div>;
84123
}
85124

125+
/**
126+
*
127+
*/
86128
function MFAPrompt() {
87-
return (<div>
88-
<h2>Multifactor authentication required</h2>
89-
<p>Enter the code from your authenticator app below to proceed.</p>
90-
<CodePrompt onValidate={() => {window.location.reload()}}/>
91-
92-
</div>)
129+
return (<div>
130+
<h2>Multifactor authentication required</h2>
131+
<p>Enter the code from your authenticator app below to proceed.</p>
132+
<CodePrompt onValidate={() => {
133+
window.location.reload();
134+
}}/>
93135

136+
</div>);
94137
}
95138

96139
declare const loris: any;

0 commit comments

Comments
 (0)