Implements 2fa authentication
parent
e8f47fcc21
commit
32b3ad753c
|
@ -15,6 +15,7 @@ F95_PASSWORD = YOUR_PASSWORD
|
|||
"use strict";
|
||||
|
||||
// Public modules from npm
|
||||
import inquirer from "inquirer";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
// Modules from file
|
||||
|
@ -33,6 +34,24 @@ dotenv.config();
|
|||
|
||||
main();
|
||||
|
||||
/**
|
||||
* Ask the user to enter the OTP code
|
||||
* necessary to authenticate on the server.
|
||||
*/
|
||||
async function insert2faCode(): Promise<number> {
|
||||
const questions = [
|
||||
{
|
||||
type: "input",
|
||||
name: "code",
|
||||
message: "Insert 2FA code:"
|
||||
}
|
||||
];
|
||||
|
||||
// Prompt the user to insert the code
|
||||
const answers = await inquirer.prompt(questions);
|
||||
return answers.code as number;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Local variables
|
||||
const gameList = ["City of broken dreamers", "Seeds of chaos", "MIST"];
|
||||
|
@ -41,7 +60,8 @@ async function main() {
|
|||
console.log("Authenticating...");
|
||||
const result = await login(
|
||||
process.env.F95_USERNAME,
|
||||
process.env.F95_PASSWORD
|
||||
process.env.F95_PASSWORD,
|
||||
insert2faCode
|
||||
);
|
||||
console.log(`Authentication result: ${result.message}\n`);
|
||||
|
||||
|
|
38
src/index.ts
38
src/index.ts
|
@ -8,7 +8,12 @@
|
|||
// Modules from file
|
||||
import shared from "./scripts/shared.js";
|
||||
import search from "./scripts/search.js";
|
||||
import { authenticate, urlExists, isF95URL } from "./scripts/network-helper.js";
|
||||
import {
|
||||
authenticate,
|
||||
urlExists,
|
||||
isF95URL,
|
||||
send2faCode
|
||||
} from "./scripts/network-helper.js";
|
||||
import fetchLatestHandiworkURLs from "./scripts/fetch-data/fetch-latest.js";
|
||||
import fetchPlatformData from "./scripts/fetch-data/fetch-platform-data.js";
|
||||
import getHandiworkInformation from "./scripts/scrape-data/handiwork-parse.js";
|
||||
|
@ -30,6 +35,7 @@ const USER_NOT_LOGGED = "User not authenticated, unable to continue";
|
|||
//#endregion
|
||||
|
||||
//#region Re-export classes
|
||||
|
||||
export { default as Animation } from "./scripts/classes/handiwork/animation.js";
|
||||
export { default as Asset } from "./scripts/classes/handiwork/asset.js";
|
||||
export { default as Comic } from "./scripts/classes/handiwork/comic.js";
|
||||
|
@ -44,9 +50,11 @@ export { default as UserProfile } from "./scripts/classes/mapping/user-profile.j
|
|||
export { default as HandiworkSearchQuery } from "./scripts/classes/query/handiwork-search-query.js";
|
||||
export { default as LatestSearchQuery } from "./scripts/classes/query/latest-search-query.js";
|
||||
export { default as ThreadSearchQuery } from "./scripts/classes/query/thread-search-query.js";
|
||||
|
||||
//#endregion Re-export classes
|
||||
|
||||
//#region Export properties
|
||||
|
||||
/**
|
||||
* Set the logger level for module debugging.
|
||||
*/
|
||||
|
@ -60,6 +68,7 @@ shared.logger.level = "warn"; // By default log only the warn messages
|
|||
export function isLogged(): boolean {
|
||||
return shared.isLogged;
|
||||
}
|
||||
|
||||
//#endregion Export properties
|
||||
|
||||
//#region Export methods
|
||||
|
@ -68,10 +77,15 @@ export function isLogged(): boolean {
|
|||
* Log in to the F95Zone platform.
|
||||
*
|
||||
* This **must** be the first operation performed before accessing any other script functions.
|
||||
*
|
||||
* @param cb2fa
|
||||
* Callback used if two-factor authentication is required for the profile.
|
||||
* It must return he OTP code to use for the login.
|
||||
*/
|
||||
export async function login(
|
||||
username: string,
|
||||
password: string
|
||||
password: string,
|
||||
cb2fa?: () => Promise<number>
|
||||
): Promise<LoginResult> {
|
||||
// Try to load a previous session
|
||||
await shared.session.load();
|
||||
|
@ -93,17 +107,25 @@ export async function login(
|
|||
await creds.fetchToken();
|
||||
|
||||
shared.logger.trace(`Authentication for ${username}`);
|
||||
const result = await authenticate(creds);
|
||||
let result = await authenticate(creds);
|
||||
shared.setIsLogged(result.success);
|
||||
|
||||
if (result.success) {
|
||||
// Load platform data
|
||||
await fetchPlatformData();
|
||||
// 2FA Authentication is required, fetch OTP
|
||||
if (result.message === "Two-factor authentication is needed to continue") {
|
||||
const code = await cb2fa();
|
||||
const response2fa = await send2faCode(code, creds.token);
|
||||
if (response2fa.isSuccess()) result = response2fa.value;
|
||||
else throw response2fa.value;
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
// Recreate the session, overwriting the old one
|
||||
shared.session.create(username, password, creds.token);
|
||||
await shared.session.save();
|
||||
|
||||
// Load platform data
|
||||
await fetchPlatformData();
|
||||
|
||||
shared.logger.info("User logged in through the platform");
|
||||
} else shared.logger.warn(`Error during authentication: ${result.message}`);
|
||||
|
||||
|
@ -147,7 +169,7 @@ export async function checkIfHandiworkHasUpdate(
|
|||
*/
|
||||
export async function searchHandiwork<T extends IBasic>(
|
||||
query: HandiworkSearchQuery,
|
||||
limit = 10
|
||||
limit: number = 10
|
||||
): Promise<T[]> {
|
||||
// Check if the user is logged
|
||||
if (!shared.isLogged) throw new UserNotLogged(USER_NOT_LOGGED);
|
||||
|
@ -203,7 +225,7 @@ export async function getUserData(): Promise<UserProfile> {
|
|||
*/
|
||||
export async function getLatestUpdates<T extends IBasic>(
|
||||
query: LatestSearchQuery,
|
||||
limit = 10
|
||||
limit: number = 10
|
||||
): Promise<T[]> {
|
||||
// Check limit value
|
||||
if (limit <= 0) throw new Error("limit must be greater than 0");
|
||||
|
|
Loading…
Reference in New Issue