This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
app.post('/login', (req, res) => { | |
let authenticatedUser; | |
const { body } = req; | |
if (body.username) { | |
// try password login | |
const user = getUser(body.username); | |
if (user && user.password === body.password) { | |
authenticatedUser = user; | |
// create session |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react'; | |
import ReactDOM from 'react-dom'; | |
import { login } from './api'; | |
import App from './App'; | |
(async () => { | |
// try to login with cookie | |
const { user, requireMfa } = await login(); | |
// init app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import Login from './Login'; | |
import OneTimePassword from './OneTimePassword'; | |
export default function App({ user, requireMfa }) { | |
// User enabled MFA but did not verify code, show OTP form | |
if (requireMfa) { | |
return <OneTimePassword enabled={true} />; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export async function login(username, password) { | |
const res = await fetch('/login', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify({ username, password }), | |
}); | |
if (res.status === 401) { | |
return { requireMfa: false }; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import express from 'express'; | |
import cookieSession from 'cookie-session'; | |
import bodyParser from 'body-parser'; | |
import { initStorage, getUser, setUser } from './storage'; | |
import crypto from 'crypto'; | |
import util from 'util'; | |
import qrcode from 'qrcode'; | |
import base32Encode from 'base32-encode'; | |
import { verifyTOTP } from './otp'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useCallback, useState } from 'react'; | |
import Login from './Login'; | |
import OneTimePassword from './OneTimePassword'; | |
export default function App({ user }) { | |
// user not logged in, show login form | |
if (!user) return <Login />; | |
return ( | |
<div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export async function verifyOtp(code) { | |
const res = await fetch('/verify_otp', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify({ code }), | |
}); | |
return res.json(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useCallback, useState } from 'react'; | |
import { verifyOtp } from './api'; | |
import Input from './Input'; | |
export default function OneTimePassword({ enabled }) { | |
const [verificationCode, setVerificationCode] = useState(''); | |
const [invalidCode, setInvalidCode] = useState(false); | |
const handleSubmit = useCallback( | |
async (e) => { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from 'crypto'; | |
import base32Decode from 'base32-decode'; | |
export function generateHOTP(secret, counter) { | |
const decodedSecret = base32Decode(secret, 'RFC4648'); | |
const buffer = Buffer.alloc(8); | |
for (let i = 0; i < 8; i++) { | |
buffer[7 - i] = counter & 0xff; | |
counter = counter >> 8; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react'; | |
import ReactDOM from 'react-dom'; | |
import { login } from './api'; | |
import App from './App'; | |
(async () => { | |
// try to login with cookie | |
const res = await login(); | |
// init app |
NewerOlder