mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-11 02:46:17 +00:00
140 lines
5.4 KiB
TypeScript
140 lines
5.4 KiB
TypeScript
import type { Page } from 'patchright'
|
|
import { randomBytes } from 'crypto'
|
|
import { URLSearchParams } from 'url'
|
|
|
|
import type { MicrosoftRewardsBot } from '../../../index'
|
|
|
|
export class MobileAccessLogin {
|
|
private clientId = '0000000040170455'
|
|
private authUrl = 'https://login.live.com/oauth20_authorize.srf'
|
|
private redirectUrl = 'https://login.live.com/oauth20_desktop.srf'
|
|
private tokenUrl = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token'
|
|
private scope = 'service::prod.rewardsplatform.microsoft.com::MBI_SSL'
|
|
private maxTimeout = 180_000 // 3min
|
|
|
|
constructor(
|
|
private bot: MicrosoftRewardsBot,
|
|
private page: Page
|
|
) {}
|
|
|
|
async get(email: string): Promise<string> {
|
|
try {
|
|
const authorizeUrl = new URL(this.authUrl)
|
|
authorizeUrl.searchParams.append('response_type', 'code')
|
|
authorizeUrl.searchParams.append('client_id', this.clientId)
|
|
authorizeUrl.searchParams.append('redirect_uri', this.redirectUrl)
|
|
authorizeUrl.searchParams.append('scope', this.scope)
|
|
authorizeUrl.searchParams.append('state', randomBytes(16).toString('hex'))
|
|
authorizeUrl.searchParams.append('access_type', 'offline_access')
|
|
authorizeUrl.searchParams.append('login_hint', email)
|
|
|
|
this.bot.logger.debug(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`Auth URL constructed: ${authorizeUrl.origin}${authorizeUrl.pathname}`
|
|
)
|
|
|
|
await this.bot.browser.utils.disableFido(this.page)
|
|
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', 'Navigating to OAuth authorize URL')
|
|
|
|
await this.page.goto(authorizeUrl.href).catch(err => {
|
|
this.bot.logger.debug(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`page.goto() failed: ${err instanceof Error ? err.message : String(err)}`
|
|
)
|
|
})
|
|
|
|
this.bot.logger.info(this.bot.isMobile, 'LOGIN-APP', 'Waiting for mobile OAuth code...')
|
|
|
|
const start = Date.now()
|
|
let code = ''
|
|
let lastUrl = ''
|
|
|
|
while (Date.now() - start < this.maxTimeout) {
|
|
const currentUrl = this.page.url()
|
|
|
|
// Log only when URL changes (high signal, no spam)
|
|
if (currentUrl !== lastUrl) {
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', `OAuth poll URL changed → ${currentUrl}`)
|
|
lastUrl = currentUrl
|
|
}
|
|
|
|
try {
|
|
const url = new URL(currentUrl)
|
|
|
|
if (url.hostname === 'login.live.com' && url.pathname === '/oauth20_desktop.srf') {
|
|
code = url.searchParams.get('code') || ''
|
|
|
|
if (code) {
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', 'OAuth code detected in redirect URL')
|
|
break
|
|
}
|
|
}
|
|
} catch (err) {
|
|
this.bot.logger.debug(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`Invalid URL while polling: ${String(currentUrl)}`
|
|
)
|
|
}
|
|
|
|
await this.bot.utils.wait(1000)
|
|
}
|
|
|
|
if (!code) {
|
|
this.bot.logger.warn(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`Timed out waiting for OAuth code after ${Math.round((Date.now() - start) / 1000)}s`
|
|
)
|
|
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', `Final page URL: ${this.page.url()}`)
|
|
|
|
return ''
|
|
}
|
|
|
|
const data = new URLSearchParams()
|
|
data.append('grant_type', 'authorization_code')
|
|
data.append('client_id', this.clientId)
|
|
data.append('code', code)
|
|
data.append('redirect_uri', this.redirectUrl)
|
|
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', 'Exchanging OAuth code for access token')
|
|
|
|
const response = await this.bot.axios.request({
|
|
url: this.tokenUrl,
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
data: data.toString()
|
|
})
|
|
|
|
const token = (response?.data?.access_token as string) ?? ''
|
|
|
|
if (!token) {
|
|
this.bot.logger.warn(this.bot.isMobile, 'LOGIN-APP', 'No access_token in token response')
|
|
this.bot.logger.debug(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`Token response payload: ${JSON.stringify(response?.data)}`
|
|
)
|
|
return ''
|
|
}
|
|
|
|
this.bot.logger.info(this.bot.isMobile, 'LOGIN-APP', 'Mobile access token received')
|
|
return token
|
|
} catch (error) {
|
|
this.bot.logger.error(
|
|
this.bot.isMobile,
|
|
'LOGIN-APP',
|
|
`MobileAccess error: ${error instanceof Error ? error.stack || error.message : String(error)}`
|
|
)
|
|
return ''
|
|
} finally {
|
|
this.bot.logger.debug(this.bot.isMobile, 'LOGIN-APP', 'Returning to base URL')
|
|
await this.page.goto(this.bot.config.baseURL, { timeout: 10000 }).catch(() => {})
|
|
}
|
|
}
|
|
}
|