未コミットを一括コミット
This commit is contained in:
@@ -1,190 +1,190 @@
|
||||
/**
|
||||
* auth4.js - Amazon Japan OpenID フローを正しく再現するカスタム認証スクリプト
|
||||
* alexa-cookie2 の古いエンドポイント問題を回避して直接フォームを処理する
|
||||
*/
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EMAIL = process.env.AMAZON_EMAIL;
|
||||
const PASSWORD = process.env.AMAZON_PASSWORD;
|
||||
|
||||
if (!EMAIL || !PASSWORD) {
|
||||
console.error('[ERROR] 環境変数 AMAZON_EMAIL と AMAZON_PASSWORD を設定してください');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ALEXA_LOGIN_URL =
|
||||
'https://www.amazon.co.jp/ap/signin?' +
|
||||
new URLSearchParams({
|
||||
'openid.assoc_handle': 'amzn_dp_project_dee_jp',
|
||||
'openid.mode': 'checkid_setup',
|
||||
'openid.ns': 'http://specs.openid.net/auth/2.0',
|
||||
'openid.claimed_id': 'http://specs.openid.net/auth/2.0/identifier_select',
|
||||
'openid.identity': 'http://specs.openid.net/auth/2.0/identifier_select',
|
||||
'openid.return_to': 'https://alexa.amazon.co.jp/api/apps/v1/token',
|
||||
'pageId': 'amzn_dp_project_dee_jp',
|
||||
}).toString();
|
||||
|
||||
const USER_AGENT =
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36';
|
||||
|
||||
let cookieJar = {};
|
||||
|
||||
function setCookies(setCookieHeaders) {
|
||||
if (!setCookieHeaders) return;
|
||||
const headers = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
|
||||
for (const h of headers) {
|
||||
const [kv] = h.split(';');
|
||||
const [k, v] = kv.trim().split('=');
|
||||
if (k && v !== undefined) cookieJar[k.trim()] = v.trim();
|
||||
}
|
||||
}
|
||||
|
||||
function getCookieHeader() {
|
||||
return Object.entries(cookieJar)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
function request(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parsed = new URL(url);
|
||||
const reqOpts = {
|
||||
hostname: parsed.hostname,
|
||||
path: parsed.pathname + parsed.search,
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept-Language': 'ja-JP,ja;q=0.9',
|
||||
'Accept': 'text/html,application/xhtml+xml,*/*;q=0.8',
|
||||
'Cookie': getCookieHeader(),
|
||||
...(options.headers || {}),
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(reqOpts, (res) => {
|
||||
setCookies(res.headers['set-cookie']);
|
||||
let body = '';
|
||||
res.on('data', (d) => (body += d));
|
||||
res.on('end', () => {
|
||||
resolve({ status: res.statusCode, headers: res.headers, body });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (options.body) req.write(options.body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// HTML の hidden フィールドを抽出
|
||||
function extractHiddenFields(html) {
|
||||
const fields = {};
|
||||
const re = /<input[^>]+type=["']?hidden["']?[^>]*>/gi;
|
||||
let match;
|
||||
while ((match = re.exec(html)) !== null) {
|
||||
const tag = match[0];
|
||||
const name = (tag.match(/name=["']([^"']+)["']/) || [])[1];
|
||||
const value = (tag.match(/value=["']([^"']*)["']/) || ['', ''])[1];
|
||||
if (name) fields[name] = value;
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
// フォームの action URL を抽出
|
||||
function extractFormAction(html) {
|
||||
const m = html.match(/id="ap_login_form"[^>]+action="([^"]+)"/);
|
||||
if (m) return m[1].replace(/&/g, '&');
|
||||
const m2 = html.match(/name="signIn"[^>]+action="([^"]+)"/);
|
||||
if (m2) return m2[1].replace(/&/g, '&');
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('[1] ログインページ取得中...');
|
||||
const page1 = await request(ALEXA_LOGIN_URL);
|
||||
console.log(` Status: ${page1.status}, Cookies: ${Object.keys(cookieJar).join(', ')}`);
|
||||
|
||||
if (page1.status !== 200) {
|
||||
console.error(`[ERROR] ログインページ取得失敗: ${page1.status}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// フォーム情報を抽出
|
||||
const action = extractFormAction(page1.body);
|
||||
const hidden = extractHiddenFields(page1.body);
|
||||
|
||||
if (!action) {
|
||||
console.error('[ERROR] ログインフォームが見つかりません。HTMLを確認します:');
|
||||
console.error(page1.body.substring(0, 500));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`[2] フォーム送信先: ${action}`);
|
||||
console.log(` Hidden fields: ${Object.keys(hidden).join(', ')}`);
|
||||
|
||||
// フォームデータ構築
|
||||
const formData = new URLSearchParams({
|
||||
...hidden,
|
||||
email: EMAIL,
|
||||
password: PASSWORD,
|
||||
rememberMe: 'true',
|
||||
}).toString();
|
||||
|
||||
console.log('[3] 認証送信中...');
|
||||
const page2 = await request(action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Referer': ALEXA_LOGIN_URL,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
console.log(` Status: ${page2.status}`);
|
||||
console.log(` Location: ${page2.headers.location || '(none)'}`);
|
||||
console.log(` Cookies after login: ${Object.keys(cookieJar).join(', ')}`);
|
||||
|
||||
// リダイレクトをたどる
|
||||
let current = page2;
|
||||
let redirectCount = 0;
|
||||
while (current.status >= 300 && current.status < 400 && current.headers.location && redirectCount < 10) {
|
||||
const loc = current.headers.location;
|
||||
const nextUrl = loc.startsWith('http') ? loc : `https://www.amazon.co.jp${loc}`;
|
||||
console.log(`[${4 + redirectCount}] Redirect → ${nextUrl.substring(0, 80)}...`);
|
||||
current = await request(nextUrl);
|
||||
console.log(` Status: ${current.status}, New Cookies: ${Object.keys(cookieJar).join(', ')}`);
|
||||
redirectCount++;
|
||||
}
|
||||
|
||||
// 成功判定: at-acbjp または session-token が含まれているか
|
||||
const cookie = getCookieHeader();
|
||||
const hasAlexaToken = cookie.includes('at-acbjp') || cookie.includes('session-token');
|
||||
|
||||
if (!hasAlexaToken) {
|
||||
console.error('[ERROR] 認証に失敗しました。取得できたCookieに認証トークンが含まれていません。');
|
||||
console.error('取得済みCookieキー:', Object.keys(cookieJar).join(', '));
|
||||
if (current.body.includes('captcha') || current.body.includes('CAPTCHA')) {
|
||||
console.error('※ CAPTCHA が要求されています。しばらく待ってから再試行してください。');
|
||||
}
|
||||
if (current.body.includes('password') && current.body.includes('error')) {
|
||||
console.error('※ パスワードが間違っている可能性があります。');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// .env に保存
|
||||
const envPath = path.join(__dirname, '.env');
|
||||
fs.writeFileSync(envPath, `ALEXA_COOKIE=${cookie}\n`);
|
||||
|
||||
console.log('\n==============================================');
|
||||
console.log(' 認証成功!');
|
||||
console.log('==============================================');
|
||||
console.log(`.env を保存しました: ${envPath}`);
|
||||
console.log(`Cookie 長さ: ${cookie.length} 文字`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('[FATAL]', err);
|
||||
process.exit(1);
|
||||
});
|
||||
/**
|
||||
* auth4.js - Amazon Japan OpenID フローを正しく再現するカスタム認証スクリプト
|
||||
* alexa-cookie2 の古いエンドポイント問題を回避して直接フォームを処理する
|
||||
*/
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EMAIL = process.env.AMAZON_EMAIL;
|
||||
const PASSWORD = process.env.AMAZON_PASSWORD;
|
||||
|
||||
if (!EMAIL || !PASSWORD) {
|
||||
console.error('[ERROR] 環境変数 AMAZON_EMAIL と AMAZON_PASSWORD を設定してください');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ALEXA_LOGIN_URL =
|
||||
'https://www.amazon.co.jp/ap/signin?' +
|
||||
new URLSearchParams({
|
||||
'openid.assoc_handle': 'amzn_dp_project_dee_jp',
|
||||
'openid.mode': 'checkid_setup',
|
||||
'openid.ns': 'http://specs.openid.net/auth/2.0',
|
||||
'openid.claimed_id': 'http://specs.openid.net/auth/2.0/identifier_select',
|
||||
'openid.identity': 'http://specs.openid.net/auth/2.0/identifier_select',
|
||||
'openid.return_to': 'https://alexa.amazon.co.jp/api/apps/v1/token',
|
||||
'pageId': 'amzn_dp_project_dee_jp',
|
||||
}).toString();
|
||||
|
||||
const USER_AGENT =
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36';
|
||||
|
||||
let cookieJar = {};
|
||||
|
||||
function setCookies(setCookieHeaders) {
|
||||
if (!setCookieHeaders) return;
|
||||
const headers = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
|
||||
for (const h of headers) {
|
||||
const [kv] = h.split(';');
|
||||
const [k, v] = kv.trim().split('=');
|
||||
if (k && v !== undefined) cookieJar[k.trim()] = v.trim();
|
||||
}
|
||||
}
|
||||
|
||||
function getCookieHeader() {
|
||||
return Object.entries(cookieJar)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
function request(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parsed = new URL(url);
|
||||
const reqOpts = {
|
||||
hostname: parsed.hostname,
|
||||
path: parsed.pathname + parsed.search,
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept-Language': 'ja-JP,ja;q=0.9',
|
||||
'Accept': 'text/html,application/xhtml+xml,*/*;q=0.8',
|
||||
'Cookie': getCookieHeader(),
|
||||
...(options.headers || {}),
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(reqOpts, (res) => {
|
||||
setCookies(res.headers['set-cookie']);
|
||||
let body = '';
|
||||
res.on('data', (d) => (body += d));
|
||||
res.on('end', () => {
|
||||
resolve({ status: res.statusCode, headers: res.headers, body });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (options.body) req.write(options.body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// HTML の hidden フィールドを抽出
|
||||
function extractHiddenFields(html) {
|
||||
const fields = {};
|
||||
const re = /<input[^>]+type=["']?hidden["']?[^>]*>/gi;
|
||||
let match;
|
||||
while ((match = re.exec(html)) !== null) {
|
||||
const tag = match[0];
|
||||
const name = (tag.match(/name=["']([^"']+)["']/) || [])[1];
|
||||
const value = (tag.match(/value=["']([^"']*)["']/) || ['', ''])[1];
|
||||
if (name) fields[name] = value;
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
// フォームの action URL を抽出
|
||||
function extractFormAction(html) {
|
||||
const m = html.match(/id="ap_login_form"[^>]+action="([^"]+)"/);
|
||||
if (m) return m[1].replace(/&/g, '&');
|
||||
const m2 = html.match(/name="signIn"[^>]+action="([^"]+)"/);
|
||||
if (m2) return m2[1].replace(/&/g, '&');
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('[1] ログインページ取得中...');
|
||||
const page1 = await request(ALEXA_LOGIN_URL);
|
||||
console.log(` Status: ${page1.status}, Cookies: ${Object.keys(cookieJar).join(', ')}`);
|
||||
|
||||
if (page1.status !== 200) {
|
||||
console.error(`[ERROR] ログインページ取得失敗: ${page1.status}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// フォーム情報を抽出
|
||||
const action = extractFormAction(page1.body);
|
||||
const hidden = extractHiddenFields(page1.body);
|
||||
|
||||
if (!action) {
|
||||
console.error('[ERROR] ログインフォームが見つかりません。HTMLを確認します:');
|
||||
console.error(page1.body.substring(0, 500));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`[2] フォーム送信先: ${action}`);
|
||||
console.log(` Hidden fields: ${Object.keys(hidden).join(', ')}`);
|
||||
|
||||
// フォームデータ構築
|
||||
const formData = new URLSearchParams({
|
||||
...hidden,
|
||||
email: EMAIL,
|
||||
password: PASSWORD,
|
||||
rememberMe: 'true',
|
||||
}).toString();
|
||||
|
||||
console.log('[3] 認証送信中...');
|
||||
const page2 = await request(action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Referer': ALEXA_LOGIN_URL,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
console.log(` Status: ${page2.status}`);
|
||||
console.log(` Location: ${page2.headers.location || '(none)'}`);
|
||||
console.log(` Cookies after login: ${Object.keys(cookieJar).join(', ')}`);
|
||||
|
||||
// リダイレクトをたどる
|
||||
let current = page2;
|
||||
let redirectCount = 0;
|
||||
while (current.status >= 300 && current.status < 400 && current.headers.location && redirectCount < 10) {
|
||||
const loc = current.headers.location;
|
||||
const nextUrl = loc.startsWith('http') ? loc : `https://www.amazon.co.jp${loc}`;
|
||||
console.log(`[${4 + redirectCount}] Redirect → ${nextUrl.substring(0, 80)}...`);
|
||||
current = await request(nextUrl);
|
||||
console.log(` Status: ${current.status}, New Cookies: ${Object.keys(cookieJar).join(', ')}`);
|
||||
redirectCount++;
|
||||
}
|
||||
|
||||
// 成功判定: at-acbjp または session-token が含まれているか
|
||||
const cookie = getCookieHeader();
|
||||
const hasAlexaToken = cookie.includes('at-acbjp') || cookie.includes('session-token');
|
||||
|
||||
if (!hasAlexaToken) {
|
||||
console.error('[ERROR] 認証に失敗しました。取得できたCookieに認証トークンが含まれていません。');
|
||||
console.error('取得済みCookieキー:', Object.keys(cookieJar).join(', '));
|
||||
if (current.body.includes('captcha') || current.body.includes('CAPTCHA')) {
|
||||
console.error('※ CAPTCHA が要求されています。しばらく待ってから再試行してください。');
|
||||
}
|
||||
if (current.body.includes('password') && current.body.includes('error')) {
|
||||
console.error('※ パスワードが間違っている可能性があります。');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// .env に保存
|
||||
const envPath = path.join(__dirname, '.env');
|
||||
fs.writeFileSync(envPath, `ALEXA_COOKIE=${cookie}\n`);
|
||||
|
||||
console.log('\n==============================================');
|
||||
console.log(' 認証成功!');
|
||||
console.log('==============================================');
|
||||
console.log(`.env を保存しました: ${envPath}`);
|
||||
console.log(`Cookie 長さ: ${cookie.length} 文字`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('[FATAL]', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user