FEATURE: add ability to have multiple totp factors (#7626)

Adds a second factor landing page that centralizes a user's second factor configuration.

This contains both TOTP and Backup, and also allows multiple TOTP tokens to be registered and organized by a name. Access to this page is authenticated via password, and cached for 30 minutes via a secure session.
This commit is contained in:
Jeff Wong
2019-06-26 16:58:06 -07:00
committed by GitHub
parent b2a033e92b
commit 88ef5e55fe
25 changed files with 793 additions and 549 deletions

View File

@@ -1,7 +1,15 @@
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
acceptance("Enforce Second Factor", {
loggedIn: true
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
password_required: "true"
});
});
}
});
QUnit.test("as an admin", async assert => {

View File

@@ -1,18 +1,26 @@
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers";
import selectKit from "helpers/select-kit-helper";
import { acceptance } from "helpers/qunit-helpers";
import User from "discourse/models/user";
acceptance("User Preferences", {
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
password_required: "true"
});
});
server.post("/u/create_second_factor_totp.json", () => {
return helper.response({
key: "rcyryaqage3jexfj",
qr: '<div id="test-qr">qr-code</div>'
});
});
server.put("/u/second_factor.json", () => {
server.post("/u/enable_second_factor_totp.json", () => {
return helper.response({ error: "invalid token" });
});
@@ -215,12 +223,13 @@ QUnit.test("second factor", async assert => {
await fillIn("#password", "secrets");
await click(".user-preferences .btn-primary");
assert.ok(exists("#test-qr"), "shows qr code");
assert.notOk(exists("#password"), "it hides the password input");
await click(".new-totp");
assert.ok(exists("#test-qr"), "shows qr code");
await fillIn("#second-factor-token", "111111");
await click(".btn-primary");
await click(".add-totp");
assert.ok(
find(".alert-error")
@@ -230,20 +239,6 @@ QUnit.test("second factor", async assert => {
);
});
QUnit.test("second factor backup", async assert => {
await visit("/u/eviltrout/preferences/second-factor-backup");
assert.ok(
exists("#second-factor-token"),
"it has a authentication token input"
);
await fillIn("#second-factor-token", "111111");
await click(".user-preferences .btn-primary");
assert.ok(exists(".backup-codes-area"), "shows backup codes");
});
QUnit.test("default avatar selector", async assert => {
await visit("/u/eviltrout/preferences");
@@ -259,6 +254,40 @@ QUnit.test("default avatar selector", async assert => {
);
});
acceptance("Second Factor Backups", {
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
totps: [{ id: 1, name: "one of them" }]
});
});
server.put("/u/second_factors_backup.json", () => {
return helper.response({
backup_codes: ["dsffdsd", "fdfdfdsf", "fddsds"]
});
});
server.get("/u/eviltrout/activity.json", () => {
return helper.response({});
});
}
});
QUnit.test("second factor backup", async assert => {
replaceCurrentUser({ second_factor_enabled: true });
await visit("/u/eviltrout/preferences/second-factor");
await click(".edit-2fa-backup");
assert.ok(
exists(".second-factor-backup-preferences"),
"shows the 2fa backup panel"
);
await click(".second-factor-backup-preferences .btn-primary");
assert.ok(exists(".backup-codes-area"), "shows backup codes");
});
acceptance("Avatar selector when selectable avatars is enabled", {
loggedIn: true,
settings: { selectable_avatars_enabled: true },