add 2fa start and change secret api key

This commit is contained in:
kinou-p 2023-05-22 17:02:24 +02:00
parent fafecdacc3
commit e5e77f264b
9 changed files with 437 additions and 5 deletions

View File

@ -16,11 +16,14 @@
"@nestjs/platform-express": "^9.0.0",
"@nestjs/typeorm": "^9.0.1",
"axios": "^1.4.0",
"base32-decode": "^1.0.0",
"base32-encode": "^2.0.0",
"express-session": "^1.17.3",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pg": "^8.10.0",
"qrcode": "^1.5.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"typeorm": "^0.3.15",
@ -3065,6 +3068,22 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base32-decode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base32-decode/-/base32-decode-1.0.0.tgz",
"integrity": "sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g=="
},
"node_modules/base32-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-2.0.0.tgz",
"integrity": "sha512-mlmkfc2WqdDtMl/id4qm3A7RjW6jxcbAoMjdRmsPiwQP0ufD4oXItYMnPgVHe80lnAIy+1xwzhHE1s4FoIceSw==",
"dependencies": {
"to-data-view": "^2.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -3310,7 +3329,6 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -3741,6 +3759,14 @@
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
@ -3836,6 +3862,11 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -3903,6 +3934,11 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encode-utf8": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz",
"integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@ -6669,7 +6705,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -6784,7 +6819,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -7039,6 +7073,14 @@
"node": ">=4"
}
},
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
@ -7205,6 +7247,132 @@
}
]
},
"node_modules/qrcode": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
"integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==",
"dependencies": {
"dijkstrajs": "^1.0.1",
"encode-utf8": "^1.0.3",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/qrcode/node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/qrcode/node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/qrcode/node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"node_modules/qrcode/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@ -7356,6 +7524,11 @@
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"node_modules/resolve": {
"version": "1.22.2",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
@ -7669,6 +7842,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@ -8142,6 +8320,14 @@
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
"dev": true
},
"node_modules/to-data-view": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-2.0.0.tgz",
"integrity": "sha512-RGEM5KqlPHr+WVTPmGNAXNeFEmsBnlkxXaIfEpUYV0AST2Z5W1EGq9L/MENFrMMmL2WQr1wjkmZy/M92eKhjYA==",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@ -8855,6 +9041,11 @@
"node": ">= 8"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
},
"node_modules/windows-release": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",

View File

@ -27,11 +27,14 @@
"@nestjs/platform-express": "^9.0.0",
"@nestjs/typeorm": "^9.0.1",
"axios": "^1.4.0",
"base32-decode": "^1.0.0",
"base32-encode": "^2.0.0",
"express-session": "^1.17.3",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pg": "^8.10.0",
"qrcode": "^1.5.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"typeorm": "^0.3.15",

View File

@ -7,6 +7,10 @@ import { ChatService } from './chat/chat.service';
import { UsersService } from './users/users.service';
import { MatchLog } from './model/user.entity'
import { generate } from 'rxjs';
import { generateQRcode } from './users/2fa';
// import { initStorage, getUser, setUser } from './storage';
// import { AuthGuard } from '@nestjs/passport';
// import { Login42 } from './auth/login42'
@ -100,6 +104,22 @@ export class AppController {
return user.rank;
}
@UseGuards(JwtAuthGuard)
@Get('/2fa')
async get2fa(@Request() req)
{
const user = await this.userService.findOne(req.user.username);
return user.doubleAuth;
}
@UseGuards(JwtAuthGuard)
@Get('/QRcode')
async createQrCode(@Request() req)
{
return (await generateQRcode(req));
}
@UseGuards(JwtAuthGuard)
@Post('/quit')
async setOffline(@Request() req) {

View File

@ -19,7 +19,7 @@ export class AuthService {
return null;
}
async login(user: any) {
async login(user) {
const myJSON = JSON.stringify(user);
// console.log(`in login all user= ${myJSON}`)
// console.log(`in login user= ${user.username}`)

View File

@ -25,7 +25,7 @@ export class loginClass {
const data = {
grant_type: 'authorization_code',
client_id: 'u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41',
client_secret: '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',
client_secret: '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',
code: code,
redirect_uri: 'http://localhost:80/api/auth/login',
};
@ -66,6 +66,7 @@ export class loginClass {
userId: userId,
children: null,
status: 1,
doubleAuth: 0
};
await this.usersService.create(user);
}

View File

@ -43,6 +43,9 @@ export class User {
@Column({ default: 0 })
userId: number;
@Column({ default: 0 })
doubleAuth: number;
@OneToMany(() => MatchLog, child => child.parent)
children: MatchLog[];

View File

@ -0,0 +1,128 @@
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;
// }
// // Step 1: Generate an HMAC-SHA-1 value
// const hmac = crypto.createHmac('sha1', Buffer.from(decodedSecret));
// hmac.update(buffer);
// const hmacResult = hmac.digest();
// // Step 2: Generate a 4-byte string (Dynamic Truncation)
// const offset = hmacResult[hmacResult.length - 1] & 0xf;
// const code =
// ((hmacResult[offset] & 0x7f) << 24) |
// ((hmacResult[offset + 1] & 0xff) << 16) |
// ((hmacResult[offset + 2] & 0xff) << 8) |
// (hmacResult[offset + 3] & 0xff);
// // Step 3: Compute an HOTP value
// return `${code % 10 ** 6}`.padStart(6, '0');
// }
type QRcode = any
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;
}
// Step 1: Generate an HMAC-SHA-1 value
const hmac = crypto.createHmac('sha1', Buffer.from(decodedSecret));
hmac.update(buffer);
const hmacResult = hmac.digest();
// Step 2: Generate a 4-byte string (Dynamic Truncation)
const offset = hmacResult[hmacResult.length - 1] & 0xf;
const code =
((hmacResult[offset] & 0x7f) << 24) |
((hmacResult[offset + 1] & 0xff) << 16) |
((hmacResult[offset + 2] & 0xff) << 8) |
(hmacResult[offset + 3] & 0xff);
// Step 3: Compute an HOTP value
return code % 10 ** 6;
}
export function generateTOTP(secret, window = 0)
{
const counter = Math.floor(Date.now() / 30000);
return generateHOTP(secret, counter + window);
}
export function verifyTOTP(token, secret, window = 1)
{
for (let errorWindow = -window; errorWindow <= +window; errorWindow++)
{
const totp = generateTOTP(secret, errorWindow);
if (token === totp)
return true;
}
return false;
}
// import { initStorage, getUser, setUser } from './storage';
// import util from 'util';
// import qrcode from 'qrcode';
// // import base32Encode from 'base32-encode';
// import * as util from 'util';
// import * as qrcode from 'qrcode';
// import * as base32Encode from 'base32-encode';
// import * as util from 'util';
// import * as qrcode from 'qrcode';
// import * as crypto from 'crypto';
// import { Response } from 'express';
// import { Readable } from 'stream';
// import * as base32Encode from 'base32-encode';
export async function generateQRcode(req)
{
// const base32Encode = await import('base32-encode');
const base32Encode = (await import('base32-encode'));
const util = (await import('util'));
const qrcode = (await import('qrcode'));
const user = req.user;
let res: QRcode;
// For security, we no longer show the QR code after is verified
// if (user.mfaEnabled) return res.status(404).end();
// if (!user.mfaSecret) { //to do
// generate unique secret for user
// this secret will be used to check the verification code sent by user
const buffer = await util.promisify(crypto.randomBytes)(14);
user.mfaSecret = base32Encode(buffer, 'RFC4648', { padding: false });
// setUser(user); // to do !!
// }
const issuer = 'Google';
const algorithm = 'SHA1';
const digits = '6';
const period = '30';
const otpType = 'totp';
const configUri = `otpauth://${otpType}/${issuer}:${user.username}?algorithm=${algorithm}&digits=${digits}&period=${period}&issuer=${issuer}&secret=${user.mfaSecret}`;
// res.setHeader('Content-Type', 'image/png');
qrcode.toFileStream(res, configUri);
console.log(`QRcode done`);
return res;
}

View File

@ -14,6 +14,8 @@ import {AnimatePresence} from "framer-motion";
import SuccessToken from '../script/tokenSuccess'
import DoubleAuth from "../pages/2fa.js";
function AnimatedRoute () {
const location = useLocation();
return (
@ -22,6 +24,9 @@ function AnimatedRoute () {
<Route exact path="/" element={<HomeLogin/>}/>
<Route exact path="/profile" element={<Home/>}/>
<Route exact path="/2fa" element={<DoubleAuth/>}/>
<Route exact path="/token" element={<SuccessToken />}/>
<Route path="/game" element={<PlayButton />}/>
<Route exact path="/pong" element={<PlayButton />}/>

View File

@ -0,0 +1,81 @@
import React, { useCallback, useState, useEffect } from 'react';
import api from '../script/axiosApi';
function DoubleAuth() {
// const enabled = await api.get("/2fa");
// const response = await api.get("/2fa");
// const enabled = response.data;
// console.log(`enable= ${enabled.data}`)
// const enabled = 0;
let enabled;
useEffect(() => {
async function get2fa()
{
const response = await api.get("/2fa");
const enabled = response.data;
console.log(`enable= ${enabled.data}`)
}
// const enabled = 0;
}, [])
// const [verificationCode, setVerificationCode] = useState('');
// const [invalidCode, setInvalidCode] = useState(false);
const handleSubmit = () => {
// async (e) => {
// e.preventDefault();
// const result = await verifyOtp(verificationCode);
// if (result) return (window.location = '/');
// setInvalidCode(true);
// },
// [verificationCode]
};
let sourceCode
if (!enabled)
{
api.get('/QRcode')
.then(response => {
sourceCode = response.data;
console.log(sourceCode);
})
.catch(error => {
console.error(error);
});
}
return (
<div>
{!enabled && (
<div>
<p>Scan the QR code on your authenticator app</p>
<img src={sourceCode} />
</div>
)}
<form onSubmit={handleSubmit}>
{/* <Input
id="verificationCode"
label="Verification code"
type="text"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
/> */}
<button type="submit">Confirm</button>
{/* {invalidCode && <p>Invalid verification code</p>} */}
</form>
</div>
);
}
export default DoubleAuth;