feat: auto MFA handling
Co-Authored-By: Yellowy <64450187+TheDevYellowy@users.noreply.github.com>
This commit is contained in:
@@ -710,7 +710,6 @@ class Client extends BaseClient {
|
||||
* Authorize an application.
|
||||
* @param {string} urlOAuth2 Discord Auth URL
|
||||
* @param {OAuth2AuthorizeOptions} [options] Oauth2 options
|
||||
* @param {string|number} [mfaCode = null] The mfa code if you have it enabled
|
||||
* @returns {Promise<{ location: string }>}
|
||||
* @example
|
||||
* client.authorizeURL(`https://discord.com/api/oauth2/authorize?client_id=botID&permissions=8&scope=applications.commands%20bot`, {
|
||||
@@ -719,7 +718,7 @@ class Client extends BaseClient {
|
||||
authorize: true
|
||||
})
|
||||
*/
|
||||
authorizeURL(urlOAuth2, options = {}, mfaCode = null) {
|
||||
authorizeURL(urlOAuth2, options = {}) {
|
||||
// ! throw new Error('METHOD_WARNING');
|
||||
const url = new URL(urlOAuth2);
|
||||
if (!/https:\/\/(canary\.|ptb\.)?discord.com\/api(\/v\d{1,2})?\/oauth2\/authorize\?/.test(urlOAuth2)) {
|
||||
@@ -745,7 +744,6 @@ class Client extends BaseClient {
|
||||
return this.api.oauth2.authorize.post({
|
||||
query: searchParams,
|
||||
data: options,
|
||||
mfaCode,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const RateLimitError = require('./RateLimitError');
|
||||
const {
|
||||
Events: { DEBUG, RATE_LIMIT, INVALID_REQUEST_WARNING, API_RESPONSE, API_REQUEST },
|
||||
} = require('../util/Constants');
|
||||
const TOTP = require('../util/Totp');
|
||||
|
||||
const captchaMessage = [
|
||||
'incorrect-captcha',
|
||||
@@ -385,22 +386,31 @@ class RequestHandler {
|
||||
request.retries++;
|
||||
return this.execute(request, captcha, data.captcha_rqtoken);
|
||||
}
|
||||
// Two factor
|
||||
if (data?.code && data.code == 60003 && request.options.mfaCode && request.retries < 1) {
|
||||
// Two factor handling
|
||||
if (
|
||||
data?.code &&
|
||||
data.code == 60003 && // Two factor is required for this operation
|
||||
data.mfa.methods.find(o => o.type === 'totp') && // TOTP is available
|
||||
typeof this.manager.client.options.TOTPKey === 'string' &&
|
||||
request.options.auth !== false &&
|
||||
request.retries < 1
|
||||
) {
|
||||
// Get mfa code
|
||||
const { otp } = await TOTP.generate(this.options.TOTPKey);
|
||||
this.manager.client.emit(
|
||||
DEBUG,
|
||||
`${data.message}
|
||||
Method : ${request.method}
|
||||
Path : ${request.path}
|
||||
Route : ${request.route}
|
||||
mfaCode : ${request.options.mfaCode}`,
|
||||
mfaCode : ${otp}`,
|
||||
);
|
||||
// Get ticket
|
||||
const mfaData = data.mfa;
|
||||
const mfaPost = await this.manager.client.api.mfa.finish.post({
|
||||
data: {
|
||||
ticket: mfaData.ticket,
|
||||
data: request.options.mfaCode,
|
||||
data: otp,
|
||||
mfa_type: 'totp',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1528,9 +1528,6 @@ class Guild extends AnonymousGuild {
|
||||
* Set the vanity URL to this guild.
|
||||
* Resolves with an object containing the vanity URL invite code and the use count.
|
||||
* @param {string} [code=''] Vanity URL code
|
||||
* @param {string|number} [mfaCode = null] Two-factor authentication code
|
||||
* After you enter the mfaCode, you will receive a `__Secure-recent_mfa` cookie, which is valid for about
|
||||
* 5 minutes, during which you won't need to enter MFA again.
|
||||
* @returns {Promise<Vanity>}
|
||||
* @example
|
||||
* // Set invite code
|
||||
@@ -1540,11 +1537,10 @@ class Guild extends AnonymousGuild {
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async setVanityCode(code = '', mfaCode = null) {
|
||||
async setVanityCode(code = '') {
|
||||
if (typeof code !== 'string') throw new TypeError('INVALID_VANITY_URL_CODE');
|
||||
const data = await this.client.api.guilds(this.id, 'vanity-url').patch({
|
||||
data: { code },
|
||||
mfaCode,
|
||||
});
|
||||
this.vanityURLCode = data.code;
|
||||
this.vanityURLUses = data.uses;
|
||||
|
||||
@@ -52,6 +52,7 @@ const Intents = require('./Intents');
|
||||
* @property {number} [DMChannelVoiceStatusSync=0] The amount of time in milliseconds that the Client to register the event with each DM channel (0=Disable)
|
||||
* @property {number} [captchaRetryLimit=3] Captcha retry limit
|
||||
* @property {CaptchaSolver} [captchaSolver] Captcha Solver
|
||||
* @property {string} [TOTPKey] TOTP key for two-factor authentication
|
||||
* @property {number} [closeTimeout=5000] The amount of time in milliseconds to wait for the close frame to be received
|
||||
* from the WebSocket.
|
||||
* <info>Don't have this too high/low. It's best to have it between 2000-6000 ms.</info>
|
||||
@@ -160,6 +161,7 @@ class Options extends null {
|
||||
DMChannelVoiceStatusSync: 0,
|
||||
captchaRetryLimit: 3,
|
||||
captchaSolver: () => Promise.reject(new Error('CAPTCHA_SOLVER_NOT_IMPLEMENTED')),
|
||||
TOTPKey: null,
|
||||
closeTimeout: 5_000,
|
||||
waitGuildTimeout: 15_000,
|
||||
shardCount: 1,
|
||||
|
||||
Reference in New Issue
Block a user