|
import { authApiToken, authMiddleware } from "../../utils/auth.js"; |
|
import { addCorsHeaders } from "../../utils/cors.js"; |
|
import { chromium } from 'playwright'; |
|
import { getVerificationCode } from '../../utils/emailVerification.js'; |
|
|
|
|
|
export const onRequest = async (context: RouteContext): Promise<Response> => { |
|
const request = context.request; |
|
const env: Env = context.env; |
|
|
|
|
|
const authResponse = await authMiddleware(request, env); |
|
const apiResponse = await authApiToken(request, env); |
|
if (authResponse && apiResponse) { |
|
return addCorsHeaders(authResponse); |
|
} |
|
|
|
try { |
|
const { email } = await request.json() as any; |
|
if (!email) { |
|
throw new Error("Email is required"); |
|
} |
|
|
|
|
|
const accountsStr = await env.KV.get("accounts"); |
|
const accounts: any[] = accountsStr ? JSON.parse(accountsStr) : []; |
|
const account = accounts.find(a => a.email === email); |
|
if (!account) { |
|
throw new Error("Account not found"); |
|
} |
|
const clientId = env.ENTRA_CLIENT_ID; |
|
const clientSecret = env.ENTRA_CLIENT_SECRET; |
|
const redirectUri = env.AUTH_REDIRECT_URI; |
|
let browser; |
|
let context; |
|
try { |
|
browser = await chromium.launch({ |
|
headless: true, |
|
args: [ |
|
'--no-sandbox', |
|
'--disable-setuid-sandbox', |
|
'--disable-dev-shm-usage', |
|
'--disable-blink-features=AutomationControlled', |
|
'--disable-infobars', |
|
'--window-size=1920,1080' |
|
], |
|
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH, |
|
}); |
|
context = await browser.newContext(); |
|
const page = await context.newPage(); |
|
|
|
const authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?` + |
|
`client_id=${clientId}` + |
|
`&response_type=code` + |
|
`&redirect_uri=${encodeURIComponent(redirectUri)}` + |
|
`&response_mode=query` + |
|
`&scope=offline_access%20IMAP.AccessAsUser.All%20User.Read%20Mail.ReadWrite.Shared%20Mail.Send%20Mail.Read` + |
|
`&prompt=consent` + |
|
`&state=${email}`; |
|
|
|
|
|
console.log(authUrl) |
|
|
|
|
|
await page.goto(authUrl); |
|
await page.fill('input[type="email"]', account.email); |
|
await page.click('input[type="submit"]'); |
|
|
|
try { |
|
await page.waitForSelector('#idA_PWD_SwitchToPassword', { timeout: 3000 }); |
|
await page.click('#idA_PWD_SwitchToPassword'); |
|
} catch (error) { |
|
console.log(`没有切换到密码登录,继续执行: ${error}`); |
|
} |
|
|
|
try { |
|
const timestamp = Math.floor(Date.now() / 1000); |
|
await page.waitForSelector('#idA_PWD_SwitchToCredPicker', { timeout: 3000 }); |
|
|
|
try { |
|
|
|
const proof = [ |
|
{ |
|
"suffix": "godgodgame.com", |
|
"apiUrl": "http://159.138.99.139:89/api/latest-email", |
|
"token": env.PROOF_GODGODGAME_TOKEN |
|
}, |
|
{ |
|
"suffix": "igiven.com", |
|
"apiUrl": "http://159.138.99.139:90/api/latest-email", |
|
"token": env.PROOF_IGIVEN_TOKEN |
|
} |
|
] |
|
|
|
const suffix = email.substring(email.indexOf('@') + 1); |
|
const proofConfig = proof.find(p => p.suffix === suffix)!; |
|
const verificationCode = await getVerificationCode(proofConfig.apiUrl, proofConfig.token!, account.proofEmail, timestamp); |
|
|
|
await page.fill('input[type="tel"]', verificationCode); |
|
await page.click('button[type="submit"]'); |
|
} |
|
catch (error) { |
|
throw new Error(`邮箱验证失败:${error}`) |
|
} |
|
|
|
await page.click('#idA_PWD_SwitchToCredPicker'); |
|
await page.click('#tileList > div:nth-child(2) Button'); |
|
|
|
} catch (error) { |
|
console.log(`没有直接发邮件验证,继续执行: ${error}`); |
|
} |
|
|
|
|
|
await page.waitForURL("https://login.live.com/**") |
|
await page.fill('input[type="password"]', account.password); |
|
await page.click('button[type="submit"]'); |
|
|
|
|
|
await page.waitForURL('https://login.live.com/ppsecure/**') |
|
await page.click('button[type="submit"]#acceptButton'); |
|
|
|
|
|
try { |
|
|
|
await page.waitForURL("https://account.live.com/Consent/**", { timeout: 5000 }) |
|
await page.click('button[type="submit"][data-testid="appConsentPrimaryButton"]'); |
|
} catch (error) { |
|
|
|
console.log("Consent page not found or timeout, skipping..."); |
|
} |
|
await page.waitForURL((url)=>{ |
|
console.log(url) |
|
return url.href.startsWith(redirectUri); |
|
}) |
|
} finally { |
|
if (context) await context.close(); |
|
if (browser) await browser.close(); |
|
} |
|
|
|
|
|
|
|
let code = null; |
|
const maxRetries = 30; |
|
let retries = 0; |
|
|
|
while (!code && retries < maxRetries) { |
|
const codeKey = `code_${email}`; |
|
code = await env.KV.get(codeKey); |
|
|
|
if (!code) { |
|
await new Promise(resolve => setTimeout(resolve, 1000)); |
|
retries++; |
|
} |
|
} |
|
|
|
if (!code) { |
|
throw new Error("Authorization timeout"); |
|
} |
|
|
|
|
|
const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
|
body: new URLSearchParams({ |
|
client_id: clientId, |
|
client_secret: clientSecret, |
|
code: code, |
|
redirect_uri: redirectUri, |
|
grant_type: 'authorization_code' |
|
}) |
|
}); |
|
|
|
const tokenData: any = await tokenResponse.json(); |
|
if (!tokenData.refresh_token) { |
|
throw new Error("Failed to get refresh token"); |
|
} |
|
if (!tokenData.expires_in) { |
|
throw new Error("Missing expires_in in token response"); |
|
} |
|
|
|
|
|
const tokenInfo = { |
|
...tokenData, |
|
timestamp: Date.now() |
|
}; |
|
console.log(tokenInfo); |
|
await env.KV.put(`refresh_token_${email}`, JSON.stringify(tokenInfo)); |
|
|
|
|
|
await env.KV.delete(`code_${email}`); |
|
|
|
return new Response(JSON.stringify({ success: true }), { |
|
status: 200, |
|
headers: { 'Content-Type': 'application/json' } |
|
}); |
|
|
|
} catch (error: any) { |
|
return new Response( |
|
JSON.stringify({ error: error.message }), |
|
{ status: 500 } |
|
); |
|
} |
|
}; |