More preparations for 2 factor authentication

This commit is contained in:
Fabian Stamm 2018-11-10 15:12:38 +01:00
parent 7389c25312
commit 0633311ba1
11 changed files with 62 additions and 119 deletions

3
.gitignore vendored
View File

@ -6,4 +6,5 @@ keys/
logs/
*.sqlite
yarn-error\.log
config.ini
config.ini
.env

View File

@ -1,3 +1,7 @@
[database]
host=localhost
database=openauth
[core]
name = OpenAuthService

103
package-lock.json generated
View File

@ -164,20 +164,12 @@
}
},
"@types/dotenv": {
"version": "4.0.3",
"resolved": "http://registry.npmjs.org/@types/dotenv/-/dotenv-4.0.3.tgz",
"integrity": "sha512-mmhpINC/HcLGQK5ikFJlLXINVvcxhlrV+ZOUJSN7/ottYl+8X4oSXzS9lBtDkmWAl96EGyGyLrNvk9zqdSH8Fw==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.0.tgz",
"integrity": "sha512-gmbNb7V1LbJQA4MmH0hVFgqY1cyKsa6RvKC1Xrq0WBnZ0JuuvXKciXx/s8dN0LVXCJd8xO6wIaSFSyUIoGph9g==",
"dev": true,
"requires": {
"@types/node": "*"
},
"dependencies": {
"@types/node": {
"version": "10.1.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.1.4.tgz",
"integrity": "sha512-GpQxofkdlHYxjHad98UUdNoMO7JrmzQZoAaghtNg14Gwg7YkohcrCoJEcEMSgllx4VIZ+mYw7ZHjfaeIagP/rg==",
"dev": true
}
}
},
"@types/events": {
@ -252,9 +244,9 @@
}
},
"@types/node": {
"version": "10.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.2.tgz",
"integrity": "sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ==",
"version": "10.12.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.5.tgz",
"integrity": "sha512-GzdHjq3t3eGLMv92Al90Iq+EoLL+86mPfQhuglbBFO7HiLdC/rkt+zrzJJumAiBF6nsrBWhou22rPW663AAyFw==",
"dev": true
},
"@types/node-rsa": {
@ -452,15 +444,6 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
@ -539,26 +522,12 @@
"callsite": "1.0.0"
}
},
"big-number": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/big-number/-/big-number-0.3.1.tgz",
"integrity": "sha1-rHMCDApZu3nrF8LOLbd/d9l04BM="
},
"binary-extensions": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz",
"integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==",
"dev": true
},
"bl": {
"version": "1.2.2",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
@ -946,15 +915,11 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"cors": {
"version": "2.8.5",
@ -2540,7 +2505,8 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"isexe": {
"version": "2.0.0",
@ -2971,11 +2937,6 @@
"to-regex": "^3.0.1"
}
},
"native-duplexpair": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz",
"integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@ -3337,7 +3298,8 @@
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true
},
"proxy-addr": {
"version": "2.0.4",
@ -3372,11 +3334,6 @@
"ps-tree": "^1.1.0"
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@ -3425,6 +3382,7 @@
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@ -3451,11 +3409,6 @@
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz",
"integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A=="
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"regex-not": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@ -4026,6 +3979,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
}
@ -4069,32 +4023,6 @@
"csextends": "^1.0.3"
}
},
"tedious": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/tedious/-/tedious-2.6.4.tgz",
"integrity": "sha512-upFZB4QahZydPIV2VK3H/bz8Fsq5FSjqbxDbhhp1c/66ZJB1qCk5p1cXi2p/VUOgAYbmAzVObTg5kaVvmeyN+Q==",
"requires": {
"babel-runtime": "^6.26.0",
"big-number": "0.3.1",
"bl": "^1.2.2",
"depd": "^1.1.2",
"iconv-lite": "^0.4.23",
"native-duplexpair": "^1.0.0",
"punycode": "^2.1.0",
"readable-stream": "^2.3.6",
"sprintf-js": "^1.1.1"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"term-size": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
@ -4399,7 +4327,8 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"utils-merge": {
"version": "1.0.1",

View File

@ -16,14 +16,14 @@
"@types/body-parser": "^1.17.0",
"@types/compression": "^0.0.36",
"@types/cookie-parser": "^1.4.1",
"@types/dotenv": "^4.0.3",
"@types/dotenv": "^6.1.0",
"@types/express": "^4.16.0",
"@types/handlebars": "^4.0.39",
"@types/i18n": "^0.8.3",
"@types/ini": "^1.3.29",
"@types/jsonwebtoken": "^8.3.0",
"@types/mongodb": "^3.1.14",
"@types/node": "^10.12.2",
"@types/node": "^10.12.5",
"@types/node-rsa": "^0.4.3",
"@types/uuid": "^3.4.4",
"concurrently": "^4.0.1",
@ -48,7 +48,6 @@
"mongodb": "^3.1.9",
"node-rsa": "^1.0.1",
"reflect-metadata": "^0.1.12",
"tedious": "^2.6.4",
"uuid": "^3.3.2"
}
}

View File

@ -21,11 +21,11 @@ export function GetUserMiddleware(json = false, special_token: boolean = false,
}
try {
let { login, special } = req.cookies
if (!login) invalid()
let token = await LoginToken.findOne({ token: login, valid: true })
if (!token) invalid()
if (!token.validated) invalid();
let user = await User.findById(token.user);
if (!user) {
@ -43,7 +43,7 @@ export function GetUserMiddleware(json = false, special_token: boolean = false,
if (special) {
Logging.debug("Special found")
let st = await LoginToken.findOne({ token: special, special: true, valid: true })
if (st && st.valid && st.user.toHexString() === token.user.toHexString()) {
if (st && st.validated && st.valid && st.user.toHexString() === token.user.toHexString()) {
if (st.validTill.getTime() < new Date().getTime()) { //Token expired
Logging.debug("Special expired")
st.valid = false;

View File

@ -1,5 +1,5 @@
import { Request, Response } from "express"
import User, { IUser } from "../../models/user";
import User, { IUser, TokenTypes } from "../../models/user";
import { randomBytes } from "crypto";
import moment = require("moment");
import LoginToken from "../../models/login_token";
@ -19,14 +19,16 @@ const Login = promiseMiddleware(async (req: Request, res: Response) => {
return;
}
const sendToken = async (user: IUser) => {
const sendToken = async (user: IUser, tfa?: TokenTypes[]) => {
let token_str = randomBytes(16).toString("hex");
let tfa_exp = moment().add(5, "minutes").toDate()
let token_exp = moment().add(6, "months").toDate()
let token = LoginToken.new({
token: token_str,
valid: true,
validTill: token_exp,
user: user._id
validTill: tfa ? tfa_exp : token_exp,
user: user._id,
validated: tfa ? false : true
});
await LoginToken.save(token);
@ -35,15 +37,17 @@ const Login = promiseMiddleware(async (req: Request, res: Response) => {
let special = LoginToken.new({
token: special_str,
valid: true,
validTill: special_exp,
validTill: tfa ? tfa_exp : special_exp,
special: true,
user: user._id
user: user._id,
validated: tfa ? false : true
});
await LoginToken.save(special);
res.json({
login: { token: token_str, expires: token_exp.toUTCString() },
special: { token: special_str, expires: special_exp.toUTCString() }
login: { token: token_str, expires: token.validTill.toUTCString() },
special: { token: special_str, expires: special.validTill.toUTCString() },
tfa
});
}
@ -61,12 +65,8 @@ const Login = promiseMiddleware(async (req: Request, res: Response) => {
} else {
if (user.twofactor && user.twofactor.length > 0) {
let types = user.twofactor.map(f => {
return { type: f.type };
})
res.json({
types: types
});
let types = user.twofactor.filter(f => f.valid).map(f => f.type)
await sendToken(user, types);
} else {
await sendToken(user);
}

View File

@ -1,11 +1,6 @@
export interface DatabaseConfig {
host: string
database: string
dialect: "sqlite" | "mysql" | "postgres" | "mssql"
username: string
password: string
storage: string
benchmark: "true" | "false" | undefined
}
export interface WebConfig {
@ -37,7 +32,7 @@ import { Logging } from "@hibas123/nodelogging";
dotenv.config();
const config: Config = ini.parse(readFileSync("./config.ini").toString())
if (config.dev) config.dev = Boolean(config.dev);
if (process.env.DEV === "true") {
config.dev = true;
Logging.warning("DEV mode active. This can cause major performance issues, data loss and vulnerabilities! ")

View File

@ -1,3 +1,11 @@
import SafeMongo from "@hibas123/safe_mongo";
const DB = new SafeMongo("mongodb://localhost", "openauth");
import Config from "./config"
let dbname = "openauth"
let host = "localhost"
if (Config.database) {
if (Config.database.database) dbname = Config.database.database;
if (Config.database.host) host = Config.database.host;
}
if (Config.dev) dbname += "_dev";
const DB = new SafeMongo("mongodb://" + host, dbname);
export default DB;

View File

@ -8,6 +8,7 @@ export interface ILoginToken extends ModelDataBase {
user: ObjectID;
validTill: Date;
valid: boolean;
validated: boolean;
}
const LoginToken = DB.addModel<ILoginToken>({
name: "login_token",
@ -20,6 +21,16 @@ const LoginToken = DB.addModel<ILoginToken>({
validTill: { type: Date },
valid: { type: Boolean }
}
}, {
migration: (doc: ILoginToken) => { doc.validated = true; },
schema: {
token: { type: String },
special: { type: Boolean, default: () => false },
user: { type: ObjectID },
validTill: { type: Date },
valid: { type: Boolean },
validated: { type: Boolean, default: false }
}
}]
})

View File

@ -9,7 +9,6 @@ import * as cookieparser from "cookie-parser"
import * as i18n from "i18n"
import * as compression from "compression";
import { BADHINTS } from "dns";
import ApiRouter from "./api/api";
import ViewRouter from "./views/views";
import RequestError, { HttpStatusCode } from "./helper/request_error";

View File

@ -75,10 +75,7 @@ loginbutton.onclick = async () => {
loading();
let pw = sha(salt + passwordinput.value);
try {
let {
login,
special
} = await fetch("/api/user/login?type=password", {
let { login, special, tfa } = await fetch("/api/user/login?type=password", {
method: "POST",
body: JSON.stringify({
username: usernameinput.value,