totp in database
parent
6b496cc045
commit
a1784f5cb2
|
|
@ -12,6 +12,7 @@
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-timeago": "^4.1.3",
|
"@types/react-timeago": "^4.1.3",
|
||||||
|
"@types/totp-generator": "^0.0.4",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@vitejs/plugin-react": "^2.0.0",
|
"@vitejs/plugin-react": "^2.0.0",
|
||||||
|
|
@ -27,6 +28,8 @@
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.4.0",
|
||||||
"react-time-ago": "^7.2.1",
|
"react-time-ago": "^7.2.1",
|
||||||
"react-timeago": "^7.1.0",
|
"react-timeago": "^7.1.0",
|
||||||
|
"reactjs-popup": "^2.0.5",
|
||||||
|
"totp-generator": "^0.0.13",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vue": "3.2.37",
|
"vue": "3.2.37",
|
||||||
"ws": "^8.8.1"
|
"ws": "^8.8.1"
|
||||||
|
|
@ -1111,6 +1114,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
||||||
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ=="
|
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/totp-generator": {
|
||||||
|
"version": "0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/totp-generator/-/totp-generator-0.0.4.tgz",
|
||||||
|
"integrity": "sha512-rs5nfg//Q6zXGRiIqZ6UUhh65lBgEM5/s8knGw3fO2TBa0i1GlSLx4+P45vXNTrp3uNnqdSS1z4I33Oi3egDDg=="
|
||||||
|
},
|
||||||
"node_modules/@types/uuid": {
|
"node_modules/@types/uuid": {
|
||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||||
|
|
@ -6833,6 +6841,14 @@
|
||||||
"extsprintf": "^1.2.0"
|
"extsprintf": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jssha": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsx-ast-utils": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
|
||||||
|
|
@ -8643,6 +8659,18 @@
|
||||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reactjs-popup": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16",
|
||||||
|
"react-dom": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read": {
|
"node_modules/read": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
||||||
|
|
@ -9878,6 +9906,14 @@
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/totp-generator": {
|
||||||
|
"version": "0.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/totp-generator/-/totp-generator-0.0.13.tgz",
|
||||||
|
"integrity": "sha512-/Sg4eXvaLfaXNJJWv/OLPQm1GVKQXOe6mbDNXFBRZdYnaN9iY6Grk6uPYLph9MDn+cGn6GLNaqpak34jfu1CQQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"jssha": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tough-cookie": {
|
"node_modules/tough-cookie": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||||
|
|
@ -11701,6 +11737,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
||||||
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ=="
|
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ=="
|
||||||
},
|
},
|
||||||
|
"@types/totp-generator": {
|
||||||
|
"version": "0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/totp-generator/-/totp-generator-0.0.4.tgz",
|
||||||
|
"integrity": "sha512-rs5nfg//Q6zXGRiIqZ6UUhh65lBgEM5/s8knGw3fO2TBa0i1GlSLx4+P45vXNTrp3uNnqdSS1z4I33Oi3egDDg=="
|
||||||
|
},
|
||||||
"@types/uuid": {
|
"@types/uuid": {
|
||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||||
|
|
@ -15931,6 +15972,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jssha": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q=="
|
||||||
|
},
|
||||||
"jsx-ast-utils": {
|
"jsx-ast-utils": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
|
||||||
|
|
@ -17280,6 +17326,12 @@
|
||||||
"integrity": "sha512-rouF7MiEm55fH791Y8cg+VobIJgx8gtNJ+gjr86R4ZqO1WKPkXiXjdT/lRzrvEkUzsxT1exHqV2V+Zdi114H3A==",
|
"integrity": "sha512-rouF7MiEm55fH791Y8cg+VobIJgx8gtNJ+gjr86R4ZqO1WKPkXiXjdT/lRzrvEkUzsxT1exHqV2V+Zdi114H3A==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"reactjs-popup": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"read": {
|
"read": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
||||||
|
|
@ -18214,6 +18266,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
||||||
},
|
},
|
||||||
|
"totp-generator": {
|
||||||
|
"version": "0.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/totp-generator/-/totp-generator-0.0.13.tgz",
|
||||||
|
"integrity": "sha512-/Sg4eXvaLfaXNJJWv/OLPQm1GVKQXOe6mbDNXFBRZdYnaN9iY6Grk6uPYLph9MDn+cGn6GLNaqpak34jfu1CQQ==",
|
||||||
|
"requires": {
|
||||||
|
"jssha": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-timeago": "^4.1.3",
|
"@types/react-timeago": "^4.1.3",
|
||||||
|
"@types/totp-generator": "^0.0.4",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@vitejs/plugin-react": "^2.0.0",
|
"@vitejs/plugin-react": "^2.0.0",
|
||||||
|
|
@ -76,6 +77,8 @@
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.4.0",
|
||||||
"react-time-ago": "^7.2.1",
|
"react-time-ago": "^7.2.1",
|
||||||
"react-timeago": "^7.1.0",
|
"react-timeago": "^7.1.0",
|
||||||
|
"reactjs-popup": "^2.0.5",
|
||||||
|
"totp-generator": "^0.0.13",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vue": "3.2.37",
|
"vue": "3.2.37",
|
||||||
"ws": "^8.8.1"
|
"ws": "^8.8.1"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ export {
|
||||||
getClientId,
|
getClientId,
|
||||||
setClientId,
|
setClientId,
|
||||||
getHomeServer,
|
getHomeServer,
|
||||||
setHomeServer,
|
setHomeServer
|
||||||
} from './settings';
|
} from './settings';
|
||||||
export {versions} from './versions';
|
export {versions} from './versions';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
|
import 'reactjs-popup/dist/index.css';
|
||||||
|
import { useApi } from '../lib/useApi';
|
||||||
|
import { ClientIdContext } from '../pages/App';
|
||||||
|
import QR from 'qrcode';
|
||||||
|
|
||||||
|
function usePrevious(value: any) {
|
||||||
|
// The ref object is a generic container whose current property is mutable ...
|
||||||
|
// ... and can hold any value, similar to an instance property on a class
|
||||||
|
const ref = useRef<any>();
|
||||||
|
// Store current value in ref
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = value;
|
||||||
|
}, [value]); // Only re-run if value changes
|
||||||
|
// Return previous value (happens before update in useEffect above)
|
||||||
|
return ref.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Totp () {
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const previousOpen = usePrevious(open);
|
||||||
|
const { clientId } = useContext(ClientIdContext);
|
||||||
|
const [qr, setQr] = useState<string | null>(null);
|
||||||
|
const [key, setKey] = useState<string>('');
|
||||||
|
|
||||||
|
const { send } = useApi({
|
||||||
|
'totp:propose'(key: string) {
|
||||||
|
setKey(key);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onOpen = useCallback(() => {
|
||||||
|
send('totp:propose', clientId);
|
||||||
|
}, [send, clientId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(open && !previousOpen) {
|
||||||
|
onOpen();
|
||||||
|
}
|
||||||
|
}, [open, onOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(key === null) return;
|
||||||
|
if(clientId === null) return;
|
||||||
|
(async () => {
|
||||||
|
setQr(await QR.toDataURL(
|
||||||
|
'otpauth://totp/Valerie?secret=' +
|
||||||
|
key +
|
||||||
|
'&issuer=corner'
|
||||||
|
));
|
||||||
|
})()
|
||||||
|
}, [key, clientId])
|
||||||
|
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const submit = useCallback(() => {
|
||||||
|
if(inputRef.current === null) return;
|
||||||
|
send('totp:confirm', {
|
||||||
|
clientId,
|
||||||
|
code: inputRef.current.value
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<button onClick={() => setOpen(!open)}>TOTP</button>
|
||||||
|
{open && (
|
||||||
|
<div>
|
||||||
|
<img src={qr ?? undefined} />
|
||||||
|
<input ref={inputRef}></input>
|
||||||
|
<button onClick={submit}>CHECK</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
@ -81,8 +81,6 @@ export default function App() {
|
||||||
// color: #f8f8f2;
|
// color: #f8f8f2;
|
||||||
// background: #282a36;
|
// background: #282a36;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClientIdContext.Provider value={clientIdContextValue}>
|
<ClientIdContext.Provider value={clientIdContextValue}>
|
||||||
<ChannelContext.Provider value={channelContextValue}>
|
<ChannelContext.Provider value={channelContextValue}>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useApi } from '../lib/useApi';
|
||||||
import type { IMessage } from './Message';
|
import type { IMessage } from './Message';
|
||||||
import NameTextbox from './NameTextbox';
|
import NameTextbox from './NameTextbox';
|
||||||
import LoginQR from './LoginQR';
|
import LoginQR from './LoginQR';
|
||||||
|
import Totp from '../components/Totp';
|
||||||
|
|
||||||
interface IChannel {
|
interface IChannel {
|
||||||
uid: string;
|
uid: string;
|
||||||
|
|
@ -143,6 +144,7 @@ export default function Channels() {
|
||||||
<NameTextbox></NameTextbox><br></br>
|
<NameTextbox></NameTextbox><br></br>
|
||||||
<button onClick={() => setHomeServer(null)}>leave</button><br></br>
|
<button onClick={() => setHomeServer(null)}>leave</button><br></br>
|
||||||
<LoginQR></LoginQR>
|
<LoginQR></LoginQR>
|
||||||
|
<Totp></Totp>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE `clients`
|
||||||
|
ADD `totp` varchar(16) COLLATE 'utf8mb4_general_ci' NULL;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE clients
|
||||||
|
SET totp=?
|
||||||
|
WHERE uid=?;
|
||||||
|
|
@ -4,6 +4,7 @@ import { expose } from './lib/WebSocketServer';
|
||||||
import message from './routers/message';
|
import message from './routers/message';
|
||||||
import channel from './routers/channel';
|
import channel from './routers/channel';
|
||||||
import client from './routers/client';
|
import client from './routers/client';
|
||||||
|
import totp from './routers/totp';
|
||||||
|
|
||||||
const api = router({
|
const api = router({
|
||||||
up() {
|
up() {
|
||||||
|
|
@ -15,6 +16,7 @@ const api = router({
|
||||||
channels: channel,
|
channels: channel,
|
||||||
client: client,
|
client: client,
|
||||||
clients: client,
|
clients: client,
|
||||||
|
totp: totp,
|
||||||
});
|
});
|
||||||
|
|
||||||
expose(api, 3000);
|
expose(api, 3000);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import router from "../lib/router"
|
||||||
|
import { reply } from "../lib/WebSocketServer"
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
import getToken from 'totp-generator';
|
||||||
|
import query from "../db/query";
|
||||||
|
import confirm from '../db/snippets/totp/confirm.sql';
|
||||||
|
// 0 1 2 3 4
|
||||||
|
// | | | | | |
|
||||||
|
// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0
|
||||||
|
// 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0
|
||||||
|
// | | | | | | | | |
|
||||||
|
// a b c d e f g h
|
||||||
|
const mask = (len: number) => Math.pow(2, len) - 1;
|
||||||
|
|
||||||
|
const manipulate = (b: number, start: number, len: number, end: number) =>
|
||||||
|
(((b >> start) & mask(len)) << end) & (mask(len) << end)
|
||||||
|
|
||||||
|
const dict = (n: number): string => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.at(n) as string;
|
||||||
|
|
||||||
|
function rb32() {
|
||||||
|
const bytes = randomBytes(5);
|
||||||
|
|
||||||
|
const a = manipulate(bytes[0], 3, 5, 0);
|
||||||
|
const b = manipulate(bytes[0], 0, 3, 2) | manipulate(bytes[1], 6, 2, 0);
|
||||||
|
const c = manipulate(bytes[1], 1, 5, 0);
|
||||||
|
const d = manipulate(bytes[1], 0, 1, 4) | manipulate(bytes[2], 4, 4, 0);
|
||||||
|
const e = manipulate(bytes[2], 0, 4, 1) | manipulate(bytes[3], 7, 1, 0);
|
||||||
|
const f = manipulate(bytes[3], 2, 5, 0);
|
||||||
|
const g = manipulate(bytes[3], 0, 2, 3) | manipulate(bytes[4], 5, 3, 0);
|
||||||
|
const h = manipulate(bytes[4], 0, 5, 0);
|
||||||
|
|
||||||
|
return dict(a) + dict(b) + dict(c) + dict(d) + dict(e) + dict(f) + dict(g) + dict(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
const totpKey = () => rb32() + rb32();
|
||||||
|
|
||||||
|
const proposals: any = {}
|
||||||
|
|
||||||
|
export default router({
|
||||||
|
'propose'(clientId: string) {
|
||||||
|
if(clientId in proposals) return reply(proposals[clientId]);
|
||||||
|
const key = totpKey();
|
||||||
|
proposals[clientId] = key;
|
||||||
|
console.log(clientId, proposals)
|
||||||
|
setTimeout(() => {
|
||||||
|
delete proposals[clientId];
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
console.log('created totp proposal');
|
||||||
|
return reply(key)
|
||||||
|
},
|
||||||
|
async 'confirm'(data: any) {
|
||||||
|
const { clientId, code } = data;
|
||||||
|
const key = proposals[clientId];
|
||||||
|
const trueCode = getToken(key);
|
||||||
|
if(trueCode !== code) return reply({
|
||||||
|
err: 'codes did not match!'
|
||||||
|
});
|
||||||
|
|
||||||
|
// add to database!
|
||||||
|
|
||||||
|
const response = await query(confirm, key, clientId);
|
||||||
|
if(response === null) return reply({
|
||||||
|
err: 'unknown database error, contact server admin.'
|
||||||
|
});
|
||||||
|
|
||||||
|
return reply({
|
||||||
|
err: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"types" : ["node"],
|
"types" : ["node"],
|
||||||
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
|
|
||||||
Reference in New Issue