feat: Add two-factor authentication via TOTP
Adds TOTP setup with QR code, login challenge, recovery codes and trusted devices that let a browser skip the second factor for 30 days. Admins can reset another user's second factor by confirming with their own password.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const bcrypt = require('bcrypt');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const { TRUST_DEVICE_EXPIRES_IN_DAYS } = require('../../../constants');
|
||||
|
||||
const TOKEN_BYTES = 32;
|
||||
const BCRYPT_ROUNDS = 10;
|
||||
|
||||
const buildUserAgentSummary = (userAgent) => {
|
||||
if (!userAgent) return null;
|
||||
return userAgent.length > 200 ? userAgent.slice(0, 200) : userAgent;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
userAgent: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const plainToken = crypto.randomBytes(TOKEN_BYTES).toString('base64url');
|
||||
const tokenHash = await bcrypt.hash(plainToken, BCRYPT_ROUNDS);
|
||||
|
||||
const expiresAt = new Date(
|
||||
Date.now() + TRUST_DEVICE_EXPIRES_IN_DAYS * 24 * 60 * 60 * 1000,
|
||||
).toISOString();
|
||||
|
||||
const fingerprint = sails.helpers.utils.parseUserAgent.with({ userAgent: inputs.userAgent });
|
||||
|
||||
const record = await TrustedDevice.qm.createOne({
|
||||
userId: inputs.userId,
|
||||
tokenHash,
|
||||
userAgentSummary: buildUserAgentSummary(inputs.userAgent),
|
||||
browserName: fingerprint.browserName,
|
||||
browserVersion: fingerprint.browserVersion,
|
||||
osName: fingerprint.osName,
|
||||
osVersion: fingerprint.osVersion,
|
||||
deviceType: fingerprint.deviceType,
|
||||
deviceVendor: fingerprint.deviceVendor,
|
||||
deviceModel: fingerprint.deviceModel,
|
||||
expiresAt,
|
||||
lastUsedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return { record, plainToken };
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user