mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-18 03:29:14 +00:00
Auth Strategies (#1257)
* auth strategies * default to no auth * rename base auth strategy * rename base strategy cont. * refactor auth strategy methods and LocalAuth * activate old session options even if is falsy value * move restartOnAuthFail to LegacyAuthStrategy option * add link to guide item * update example/shell * types
This commit is contained in:
106
src/Client.js
106
src/Client.js
@@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const EventEmitter = require('events');
|
||||
const puppeteer = require('puppeteer');
|
||||
const moduleRaid = require('@pedroslopez/moduleraid/moduleraid');
|
||||
@@ -13,23 +11,24 @@ const { ExposeStore, LoadUtils } = require('./util/Injected');
|
||||
const ChatFactory = require('./factories/ChatFactory');
|
||||
const ContactFactory = require('./factories/ContactFactory');
|
||||
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List } = require('./structures');
|
||||
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
||||
const NoAuth = require('./authStrategies/NoAuth');
|
||||
|
||||
/**
|
||||
* Starting point for interacting with the WhatsApp Web API
|
||||
* @extends {EventEmitter}
|
||||
* @param {object} options - Client options
|
||||
* @param {AuthStrategy} options.authStrategy - Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used.
|
||||
* @param {number} options.authTimeoutMs - Timeout for authentication selector in puppeteer
|
||||
* @param {object} options.puppeteer - Puppeteer launch options. View docs here: https://github.com/puppeteer/puppeteer/
|
||||
* @param {number} options.qrMaxRetries - How many times should the qrcode be refreshed before giving up
|
||||
* @param {string} options.restartOnAuthFail - Restart client with a new session (i.e. use null 'session' var) if authentication fails
|
||||
* @param {boolean} options.useDeprecatedSessionAuth - Enable JSON-based authentication. This is deprecated due to not being supported by MultiDevice, and will be removed in a future version.
|
||||
* @param {object} options.session - This is deprecated due to not being supported by MultiDevice, and will be removed in a future version.
|
||||
* @param {string} options.restartOnAuthFail - @deprecated This option should be set directly on the LegacySessionAuth.
|
||||
* @param {object} options.session - @deprecated Only here for backwards-compatibility. You should move to using LocalAuth, or set the authStrategy to LegacySessionAuth explicitly.
|
||||
* @param {number} options.takeoverOnConflict - If another whatsapp web session is detected (another browser), take over the session in the current browser
|
||||
* @param {number} options.takeoverTimeoutMs - How much time to wait before taking over the session
|
||||
* @param {string} options.dataPath - Change the default path for saving session files, default is: "./WWebJS/"
|
||||
* @param {string} options.userAgent - User agent to use in puppeteer
|
||||
* @param {string} options.ffmpegPath - Ffmpeg path to use when formating videos to webp while sending stickers
|
||||
* @param {boolean} options.bypassCSP - Sets bypassing of page's Content-Security-Policy.
|
||||
* @param {string} options.clientId - Client id to distinguish instances if you are using multiple, otherwise keep null if you are using only one instance
|
||||
*
|
||||
* @fires Client#qr
|
||||
* @fires Client#authenticated
|
||||
@@ -52,20 +51,28 @@ class Client extends EventEmitter {
|
||||
super();
|
||||
|
||||
this.options = Util.mergeDefault(DefaultOptions, options);
|
||||
|
||||
if(!this.options.authStrategy) {
|
||||
if(Object.prototype.hasOwnProperty.call(this.options, 'session')) {
|
||||
process.emitWarning(
|
||||
'options.session is deprecated and will be removed in a future release due to incompatibility with multi-device. ' +
|
||||
'Use the LocalAuth authStrategy, don\'t pass in a session as an option, or suppress this warning by using the LegacySessionAuth strategy explicitly (see https://wwebjs.dev/guide/authentication.html#legacysessionauth-strategy).',
|
||||
'DeprecationWarning'
|
||||
);
|
||||
|
||||
this.id = this.options.clientId;
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const foldernameRegex = /^(?!.{256,})(?!(aux|clock\$|con|nul|prn|com[1-9]|lpt[1-9])(?:$|\.))[^ ][ \.\w-$()+=[\];#@~,&']+[^\. ]$/i;
|
||||
if (this.id && !foldernameRegex.test(this.id)) throw Error('Invalid client ID. Make sure you abide by the folder naming rules of your operating system.');
|
||||
|
||||
if (!this.options.useDeprecatedSessionAuth) {
|
||||
this.dataDir = this.options.puppeteer.userDataDir;
|
||||
const dirPath = path.join(process.cwd(), this.options.dataPath, this.id ? 'session-' + this.id : 'session');
|
||||
if (!this.dataDir) this.dataDir = dirPath;
|
||||
fs.mkdirSync(this.dataDir, { recursive: true });
|
||||
this.authStrategy = new LegacySessionAuth({
|
||||
session: this.options.session,
|
||||
restartOnAuthFail: this.options.restartOnAuthFail
|
||||
});
|
||||
} else {
|
||||
this.authStrategy = new NoAuth();
|
||||
}
|
||||
} else {
|
||||
this.authStrategy = this.options.authStrategy;
|
||||
}
|
||||
|
||||
this.authStrategy.setup(this);
|
||||
|
||||
this.pupBrowser = null;
|
||||
this.pupPage = null;
|
||||
|
||||
@@ -78,10 +85,9 @@ class Client extends EventEmitter {
|
||||
async initialize() {
|
||||
let [browser, page] = [null, null];
|
||||
|
||||
const puppeteerOpts = {
|
||||
...this.options.puppeteer,
|
||||
userDataDir: this.options.useDeprecatedSessionAuth ? undefined : this.dataDir
|
||||
};
|
||||
await this.authStrategy.beforeBrowserInitialized();
|
||||
|
||||
const puppeteerOpts = this.options.puppeteer;
|
||||
if (puppeteerOpts && puppeteerOpts.browserWSEndpoint) {
|
||||
browser = await puppeteer.connect(puppeteerOpts);
|
||||
page = await browser.newPage();
|
||||
@@ -91,27 +97,12 @@ class Client extends EventEmitter {
|
||||
}
|
||||
|
||||
await page.setUserAgent(this.options.userAgent);
|
||||
if (this.options.bypassCSP) await page.setBypassCSP(true);
|
||||
|
||||
this.pupBrowser = browser;
|
||||
this.pupPage = page;
|
||||
|
||||
if (this.options.useDeprecatedSessionAuth && this.options.session) {
|
||||
await page.evaluateOnNewDocument(session => {
|
||||
if (document.referrer === 'https://whatsapp.com/') {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('WABrowserId', session.WABrowserId);
|
||||
localStorage.setItem('WASecretBundle', session.WASecretBundle);
|
||||
localStorage.setItem('WAToken1', session.WAToken1);
|
||||
localStorage.setItem('WAToken2', session.WAToken2);
|
||||
}
|
||||
|
||||
localStorage.setItem('remember-me', 'true');
|
||||
}, this.options.session);
|
||||
}
|
||||
|
||||
if (this.options.bypassCSP) {
|
||||
await page.setBypassCSP(true);
|
||||
}
|
||||
await this.authStrategy.afterBrowserInitialized();
|
||||
|
||||
await page.goto(WhatsWebURL, {
|
||||
waitUntil: 'load',
|
||||
@@ -141,18 +132,17 @@ class Client extends EventEmitter {
|
||||
|
||||
// Scan-qrcode selector was found. Needs authentication
|
||||
if (needAuthentication) {
|
||||
if(this.options.session) {
|
||||
const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded();
|
||||
if(failed) {
|
||||
/**
|
||||
* Emitted when there has been an error while trying to restore an existing session
|
||||
* @event Client#auth_failure
|
||||
* @param {string} message
|
||||
* @deprecated
|
||||
*/
|
||||
this.emit(Events.AUTHENTICATION_FAILURE, 'Unable to log in. Are the session details valid?');
|
||||
this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload);
|
||||
await this.destroy();
|
||||
if (this.options.restartOnAuthFail) {
|
||||
if (restart) {
|
||||
// session restore failed so try again but without session to force new authentication
|
||||
this.options.session = null;
|
||||
return this.initialize();
|
||||
}
|
||||
return;
|
||||
@@ -224,20 +214,7 @@ class Client extends EventEmitter {
|
||||
}
|
||||
|
||||
await page.evaluate(ExposeStore, moduleRaid.toString());
|
||||
let authEventPayload = undefined;
|
||||
if (this.options.useDeprecatedSessionAuth) {
|
||||
// Get session tokens
|
||||
const localStorage = JSON.parse(await page.evaluate(() => {
|
||||
return JSON.stringify(window.localStorage);
|
||||
}));
|
||||
|
||||
authEventPayload = {
|
||||
WABrowserId: localStorage.WABrowserId,
|
||||
WASecretBundle: localStorage.WASecretBundle,
|
||||
WAToken1: localStorage.WAToken1,
|
||||
WAToken2: localStorage.WAToken2
|
||||
};
|
||||
}
|
||||
const authEventPayload = await this.authStrategy.getAuthEventPayload();
|
||||
|
||||
/**
|
||||
* Emitted when authentication is successful
|
||||
@@ -248,23 +225,14 @@ class Client extends EventEmitter {
|
||||
// Check window.Store Injection
|
||||
await page.waitForFunction('window.Store != undefined');
|
||||
|
||||
const isMD = await page.evaluate(() => {
|
||||
return window.Store.Features.features.MD_BACKEND;
|
||||
});
|
||||
|
||||
await page.evaluate(async () => {
|
||||
// safely unregister service workers
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (let registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (this.options.useDeprecatedSessionAuth && isMD) {
|
||||
throw new Error('Authenticating via JSON session is not supported for MultiDevice-enabled WhatsApp accounts.');
|
||||
}
|
||||
|
||||
//Load util functions (serializers, helper functions)
|
||||
await page.evaluate(LoadUtils);
|
||||
|
||||
@@ -518,9 +486,7 @@ class Client extends EventEmitter {
|
||||
return window.Store.AppState.logout();
|
||||
});
|
||||
|
||||
if (this.dataDir) {
|
||||
return (fs.rmSync ? fs.rmSync : fs.rmdirSync).call(this.dataDir, { recursive: true });
|
||||
}
|
||||
await this.authStrategy.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user