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