Basic brute force detection added. Accounts throttle by captcha after 5 failed attempts, and locked out for 24 hours after 200 attempts.
This commit is contained in:
parent
e0f53df176
commit
9c18c23ad5
|
|
@ -14,32 +14,58 @@ GNU Affero General Public License for more details.
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../../../config.json');
|
||||
|
||||
//npm imports
|
||||
const {validationResult, matchedData} = require('express-validator');
|
||||
|
||||
//local imports
|
||||
const accountUtils = require('../../../utils/sessionUtils');
|
||||
const sessionUtils = require('../../../utils/sessionUtils');
|
||||
const {exceptionHandler, errorHandler} = require('../../../utils/loggerUtils');
|
||||
|
||||
const altchaUtils = require('../../../utils/altchaUtils');
|
||||
const session = require('express-session');
|
||||
|
||||
//api account functions
|
||||
module.exports.post = async function(req, res){
|
||||
try{
|
||||
//Check validation results
|
||||
const validResult = validationResult(req);
|
||||
|
||||
//if we don't have errors
|
||||
if(validResult.isEmpty()){
|
||||
const data = matchedData(req);
|
||||
const {user, pass} = data;
|
||||
//Pull sanatzied/validated data
|
||||
const {user, pass} = matchedData(req);
|
||||
|
||||
//try to authenticate the session, and return a successful code if it works
|
||||
await accountUtils.authenticateSession(user, pass, req);
|
||||
await sessionUtils.authenticateSession(user, pass, req);
|
||||
return res.sendStatus(200);
|
||||
}else{
|
||||
res.status(400);
|
||||
res.send({errors: validResult.array()})
|
||||
return res.send({errors: validResult.array()})
|
||||
}
|
||||
}catch(err){
|
||||
exceptionHandler(res, err);
|
||||
//Check validation results
|
||||
const validResult = validationResult(req);
|
||||
|
||||
//if we don't have errors
|
||||
if(validResult.isEmpty()){
|
||||
//Get login attempts for current user
|
||||
const {user} = matchedData(req);
|
||||
const attempts = sessionUtils.getLoginAttempts(user)
|
||||
|
||||
//if we've gone over max attempts and
|
||||
if(attempts.count > sessionUtils.throttleAttempts){
|
||||
//tell client it needs a captcha
|
||||
return res.sendStatus(429);
|
||||
}
|
||||
}else{
|
||||
res.status(400);
|
||||
return res.send({errors: validResult.array()})
|
||||
}
|
||||
|
||||
//
|
||||
return exceptionHandler(res, err);
|
||||
}
|
||||
|
||||
}
|
||||
64
src/controllers/loginController.js
Normal file
64
src/controllers/loginController.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
//NPM Imports
|
||||
const {validationResult, matchedData} = require('express-validator');
|
||||
|
||||
//Local Imports
|
||||
const sessionUtils = require('../utils/sessionUtils');
|
||||
const altchaUtils = require('../utils/altchaUtils');
|
||||
|
||||
//register page functions
|
||||
module.exports.get = async function(req, res){
|
||||
//Check for validation errors
|
||||
const validResult = validationResult(req);
|
||||
|
||||
//If there are none
|
||||
if(validResult.isEmpty()){
|
||||
//Get username from sanatized/validated data
|
||||
const {user} = matchedData(req);
|
||||
const attempts = sessionUtils.getLoginAttempts(user);
|
||||
|
||||
//if we have previous attempts for this user
|
||||
if(attempts != null){
|
||||
if(attempts.count > sessionUtils.maxAttempts){
|
||||
return res.render('lockedAccount', {instance: config.instanceName, user: req.session.user});
|
||||
}
|
||||
|
||||
//If the users login's are being throttled
|
||||
if(attempts.count > sessionUtils.throttleAttempts){
|
||||
//Get diffuculty based on amount of attempts past the max amount
|
||||
const difficulty = attempts.count - sessionUtils.throttleAttempts;
|
||||
//Generate challenge unique to specific user, with difficulty set based on failed login attempts
|
||||
const challenge = await altchaUtils.genCaptcha(difficulty, user);
|
||||
|
||||
//Render page
|
||||
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge});
|
||||
}
|
||||
//otherwise
|
||||
}else{
|
||||
//Render generic page
|
||||
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null});
|
||||
}
|
||||
//if we received invalid input
|
||||
}else{
|
||||
//Render pretend nothing happened, send out a generic page
|
||||
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null});
|
||||
}
|
||||
}
|
||||
30
src/routers/loginRouter.js
Normal file
30
src/routers/loginRouter.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
//npm imports
|
||||
const { Router } = require('express');
|
||||
|
||||
//local imports
|
||||
const loginController = require("../controllers/loginController");
|
||||
const accountValidator = require("../validators/accountValidator");
|
||||
|
||||
//globals
|
||||
const router = Router();
|
||||
|
||||
//routing functions
|
||||
router.get('/', accountValidator.user(), loginController.get);
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -27,14 +27,19 @@ const mongoose = require('mongoose');
|
|||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
|
||||
//Define Local Imports
|
||||
//Application
|
||||
const channelManager = require('./app/channel/channelManager');
|
||||
//Util
|
||||
const scheduler = require('./utils/scheduler');
|
||||
//DB Model
|
||||
const statModel = require('./schemas/statSchema');
|
||||
const flairModel = require('./schemas/flairSchema');
|
||||
const emoteModel = require('./schemas/emoteSchema');
|
||||
const tokeCommandModel = require('./schemas/tokebot/tokeCommandSchema');
|
||||
//Router
|
||||
const indexRouter = require('./routers/indexRouter');
|
||||
const registerRouter = require('./routers/registerRouter');
|
||||
const loginRouter = require('./routers/loginRouter');
|
||||
const profileRouter = require('./routers/profileRouter');
|
||||
const adminPanelRouter = require('./routers/adminPanelRouter');
|
||||
const channelRouter = require('./routers/channelRouter');
|
||||
|
|
@ -95,6 +100,7 @@ io.engine.use(sessionMiddleware);
|
|||
//Humie-Friendly
|
||||
app.use('/', indexRouter);
|
||||
app.use('/register', registerRouter);
|
||||
app.use('/login', loginRouter);
|
||||
app.use('/profile', profileRouter);
|
||||
app.use('/adminPanel', adminPanelRouter);
|
||||
app.use('/c', channelRouter);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const spent = [];
|
|||
//Captcha lifetime in minutes
|
||||
const lifetime = 2;
|
||||
|
||||
module.exports.genCaptcha = async function(){
|
||||
module.exports.genCaptcha = async function(difficulty = 2, uniqueSecret = ''){
|
||||
//Set altcha expiration date
|
||||
const expiration = new Date();
|
||||
|
||||
|
|
@ -34,13 +34,13 @@ module.exports.genCaptcha = async function(){
|
|||
|
||||
//Generate Altcha Challenge
|
||||
return await createChallenge({
|
||||
hmacKey: config.altchaSecret,
|
||||
maxNumber: 200000,
|
||||
hmacKey: [config.altchaSecret, uniqueSecret].join(''),
|
||||
maxNumber: 100000 * difficulty,
|
||||
expires: expiration
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.verify = async function(payload){
|
||||
module.exports.verify = async function(payload, uniqueSecret = ''){
|
||||
//If we already checked this payload
|
||||
if(spent.indexOf(payload) != -1){
|
||||
//Fuck off and die
|
||||
|
|
@ -57,5 +57,5 @@ module.exports.verify = async function(payload){
|
|||
setTimeout(() => {spent.splice(payloadIndex,1);}, lifetime * 60 * 1000);
|
||||
|
||||
//Return verification results
|
||||
return await verifySolution(payload, config.altchaSecret);
|
||||
return await verifySolution(payload, [config.altchaSecret, uniqueSecret].join(''));
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ const cron = require('node-cron');
|
|||
const {userModel} = require('../schemas/userSchema');
|
||||
const userBanModel = require('../schemas/userBanSchema');
|
||||
const channelModel = require('../schemas/channel/channelSchema');
|
||||
const sessionUtils = require('./sessionUtils');
|
||||
|
||||
module.exports.schedule = function(){
|
||||
//Process hashed IP Records that haven't been recorded in a week or more
|
||||
|
|
@ -29,6 +30,8 @@ module.exports.schedule = function(){
|
|||
cron.schedule('0 0 * * *', ()=>{userBanModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired channel bans every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{channelModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired failed login attempts every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{sessionUtils.processExpiredAttempts()},{scheduled: true, timezone: "UTC"});
|
||||
}
|
||||
|
||||
module.exports.kickoff = function(){
|
||||
|
|
|
|||
|
|
@ -17,15 +17,45 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
|||
//local imports
|
||||
const {userModel} = require('../schemas/userSchema');
|
||||
const userBanModel = require('../schemas/userBanSchema')
|
||||
const altchaUtils = require('../utils/altchaUtils');
|
||||
|
||||
//Create failed sign-in cache since it's easier and more preformant to implement it this way than adding extra burdon to the database
|
||||
//Server restarts are far and few between. It would take multiple during a single bruteforce attempt for this to become an issue.
|
||||
const failedAttempts = new Map();
|
||||
const throttleAttempts = 5;
|
||||
const maxAttempts = 200;
|
||||
|
||||
//this module is good for keeping wrappers for userModel and other shit in that does more session handling than database access/modification.
|
||||
|
||||
module.exports.authenticateSession = async function(user, pass, req){
|
||||
//Fuck you yoda
|
||||
try{
|
||||
//Grab previous attempts
|
||||
const attempt = failedAttempts.get(user);
|
||||
|
||||
//If we have failed attempts
|
||||
if(attempt != null){
|
||||
//If we have more failed attempts than allowed
|
||||
if(attempt.count > maxAttempts){
|
||||
throw new Error("This account has been locked for at 24 hours due to a large amount of failed log-in attempts");
|
||||
}
|
||||
|
||||
//If we're throttling logins
|
||||
if(attempt.count > throttleAttempts){
|
||||
//Verification doesnt get sanatized or checked since that would most likely break the cryptography
|
||||
//Since we've already got access to the request and dont need to import anything, why bother getting it from a parameter?
|
||||
if(req.body.verification == null){
|
||||
throw new Error("Verification failed!");
|
||||
}else if(!altchaUtils.verify(req.body.verification, user)){
|
||||
throw new Error("Verification failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Authenticate the session
|
||||
const userDB = await userModel.authenticate(user, pass);
|
||||
const banDB = await userBanModel.checkBanByUserDoc(userDB);
|
||||
|
||||
//If the user is banned
|
||||
if(banDB){
|
||||
//Make the number a little prettier despite the lack of precision since we're not doing calculations here :P
|
||||
const expiration = banDB.getDaysUntilExpiration() < 1 ? 0 : banDB.getDaysUntilExpiration();
|
||||
|
|
@ -51,10 +81,61 @@ module.exports.authenticateSession = async function(user, pass, req){
|
|||
//Tattoo hashed IP address to user account for seven days
|
||||
userDB.tattooIPRecord(req.ip);
|
||||
|
||||
//If we got to here then the log-in was successful. We should clear-out any failed attempts.
|
||||
failedAttempts.delete(user);
|
||||
|
||||
//return user
|
||||
return userDB.user;
|
||||
}catch(err){
|
||||
//Look for previous failed attempts
|
||||
var attempt = failedAttempts.get(user);
|
||||
|
||||
//If this is the first attempt
|
||||
if(attempt == null){
|
||||
//Create new attempt object
|
||||
attempt = {
|
||||
count: 1,
|
||||
lastAttempt: new Date()
|
||||
}
|
||||
}else{
|
||||
//Create updated attempt object
|
||||
attempt = {
|
||||
count: attempt.count + 1,
|
||||
lastAttempt: new Date()
|
||||
}
|
||||
}
|
||||
|
||||
//Commit the failed attempt to the failed sign-in cache
|
||||
failedAttempts.set(user, attempt);
|
||||
|
||||
//y33t
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.killSession = async function(session){
|
||||
session.destroy();
|
||||
}
|
||||
|
||||
module.exports.getLoginAttempts = function(user){
|
||||
//Read the code, i'm not explaining this
|
||||
return failedAttempts.get(user);
|
||||
}
|
||||
|
||||
module.exports.processExpiredAttempts = function(){
|
||||
for(user of failedAttempts.keys()){
|
||||
//Get attempt by user
|
||||
const attempt = failedAttempts.get(user);
|
||||
//Check how long its been
|
||||
const daysSinceLastAttempt = ((new Date() - attempt.lastAttempt) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
|
||||
//If it's been more than a day since anyones tried to log in as this user
|
||||
if(daysSinceLastAttempt >= 1){
|
||||
//Clear out the attempts so that they don't need to fuck with a captcha anymore
|
||||
failedAttempts.delete(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.throttleAttempts = throttleAttempts;
|
||||
module.exports.maxAttempts = maxAttempts;
|
||||
32
src/views/lockedAccount.ejs
Normal file
32
src/views/lockedAccount.ejs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!--Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<%- include('partial/styles', {instance, user}); %>
|
||||
<link rel="stylesheet" type="text/css" href="/css/login.css">
|
||||
<title><%= instance %> - Account Locked!</title>
|
||||
</head>
|
||||
<body>
|
||||
<%- include('partial/navbar', {user}); %>
|
||||
<h3 class="danger-text">Multiple failed attempts detected!</h3>
|
||||
<p class="danger-text">Your account has been locked due to detected brute-force attacks!<br>Your account will be unlocked in 24 hours.</p>
|
||||
</body>
|
||||
<footer>
|
||||
<%- include('partial/scripts', {user}); %>
|
||||
</footer>
|
||||
</html>
|
||||
47
src/views/login.ejs
Normal file
47
src/views/login.ejs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<!--Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<%- include('partial/styles', {instance, user}); %>
|
||||
<link rel="stylesheet" type="text/css" href="/css/login.css">
|
||||
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
|
||||
<title><%= instance %> - Log-In</title>
|
||||
</head>
|
||||
<body>
|
||||
<%- include('partial/navbar', {user}); %>
|
||||
<% if(challenge != null){ %>
|
||||
<h3 class="danger-text">Multiple failed attempts detected!</h3>
|
||||
<p class="danger-text">Please complete verification challenge to continue!</p>
|
||||
<% } %>
|
||||
<form action="javascript:">
|
||||
<label>Username:</label>
|
||||
<input class="login-page-prompt" id="login-page-username" placeholder="Required">
|
||||
<label>Password:</label>
|
||||
<input class="login-page-prompt" id="login-page-password" placeholder="Required" type="password">
|
||||
<% if(challenge != null){ %>
|
||||
<altcha-widget challengejson="<%= JSON.stringify(challenge) %>"></altcha-widget>
|
||||
<% } %>
|
||||
<button id="login-page-button" class='positive-button'>Login</button>
|
||||
</form>
|
||||
</body>
|
||||
<footer>
|
||||
<%- include('partial/scripts', {user}); %>
|
||||
<script src="/js/login.js"></script>
|
||||
<script src="/lib/altcha/altcha.js" type="module"></script>
|
||||
</footer>
|
||||
</html>
|
||||
35
www/css/login.css
Normal file
35
www/css/login.css
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
form{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
margin: 5% 17%;
|
||||
}
|
||||
|
||||
.login-page-prompt{
|
||||
width: 100%
|
||||
}
|
||||
|
||||
#login-page-button{
|
||||
width: 6em;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.danger-text{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -66,8 +66,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
|||
--altcha-color-base: var(--bg1);
|
||||
--altcha-color-border: var(--accent1);
|
||||
--altcha-color-text: var(--accent1);
|
||||
--altcha-color-border-focus: currentColor;
|
||||
--altcha-color-error-text: #f23939;
|
||||
--altcha-color-error-text: var(--danger0);
|
||||
--altcha-max-width: 260px;
|
||||
}
|
||||
|
||||
|
|
@ -118,8 +117,25 @@ button:active{
|
|||
box-shadow: var(--focus-glow0-alt0);
|
||||
}
|
||||
|
||||
input{
|
||||
accent-color: var(--focus0);
|
||||
input:focus, textarea:focus{
|
||||
outline: none;
|
||||
box-shadow: var(--focus-glow0);
|
||||
}
|
||||
|
||||
input:checked{
|
||||
accent-color: var(--focus0-alt0);
|
||||
box-shadow: var(--focus-glow0);
|
||||
}
|
||||
|
||||
/* NOT! -Wayne */
|
||||
input:not([type='checkbox']):not(.navbar-item), textarea {
|
||||
border-radius: 1em;
|
||||
border: none;
|
||||
padding: 0.1em 0.5em;
|
||||
}
|
||||
|
||||
textarea{
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.danger-button{
|
||||
|
|
@ -139,7 +155,7 @@ input{
|
|||
box-shadow: var(--danger-glow0-alt1);
|
||||
}
|
||||
|
||||
.danger-link{
|
||||
.danger-link, .danger-text{
|
||||
color: var(--danger0);
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +216,7 @@ div.control-prompt:focus-within{
|
|||
input.control-prompt, input.control-prompt:focus{
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
70
www/js/login.js
Normal file
70
www/js/login.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
class registerPrompt{
|
||||
constructor(){
|
||||
//Grab user prompt
|
||||
this.user = document.querySelector("#login-page-username");
|
||||
//Grab pass prompts
|
||||
this.pass = document.querySelector("#login-page-password");
|
||||
//Grab register button
|
||||
this.button = document.querySelector("#login-page-button");
|
||||
//Grab altcha widget
|
||||
this.altcha = document.querySelector("altcha-widget");
|
||||
//Setup null property to hold verification payload from altcha widget
|
||||
this.verification = null
|
||||
|
||||
//Run input setup after DOM content has completely loaded to ensure altcha event listeners work
|
||||
document.addEventListener('DOMContentLoaded', this.setupInput.bind(this));
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
//If we need verification
|
||||
if(this.altcha != null){
|
||||
//Add verification event listener to altcha widget
|
||||
this.altcha.addEventListener("verified", this.verify.bind(this));
|
||||
}
|
||||
|
||||
//Add register event listener to register button
|
||||
this.button.addEventListener("click", this.login.bind(this));
|
||||
}
|
||||
|
||||
verify(event){
|
||||
//pull verification payload from event
|
||||
this.verification = event.detail.payload;
|
||||
}
|
||||
|
||||
login(){
|
||||
console.log(this.altcha != null)
|
||||
//If we need verification
|
||||
if(this.altcha != null){
|
||||
//If verification isn't complete
|
||||
if( this.verification == null){
|
||||
//don't bother
|
||||
console.log("not complete");
|
||||
return;
|
||||
}
|
||||
|
||||
//login with verification
|
||||
utils.ajax.login(this.user.value , this.pass.value, this.verification);
|
||||
}else{
|
||||
//login
|
||||
utils.ajax.login(this.user.value, this.pass.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const registerForm = new registerPrompt();
|
||||
|
|
@ -404,17 +404,19 @@ class canopyAjaxUtils{
|
|||
}
|
||||
}
|
||||
|
||||
async login(user, pass){
|
||||
async login(user, pass, verification){
|
||||
var response = await fetch(`/api/account/login`,{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({user, pass})
|
||||
body: JSON.stringify(verification ? {user, pass, verification} : {user, pass})
|
||||
});
|
||||
|
||||
if(response.status == 200){
|
||||
location.reload();
|
||||
}else if(response.status == 429){
|
||||
location = `/login?user=${user}`;
|
||||
}else{
|
||||
utils.ux.displayResponseError(await response.json());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue