Yahoo IDトークンの取得用のRestクライアントをPythonで実装してみた。
先ずは、以下URLにアクセスし、でクライアントID・クライアントシークレット・コールバックURLの設定を行う。
Yahooディベロッパーネットワーク:
https://developer.yahoo.co.jp/yconnect
ログインの際にPOSTするパラメータは、ログイン画面からスクレイピングで取得することとなるが、どれが必須パラメータか不明だったので、とりあえず全てを対象にした。
※「.albatross」に関しては、ログインのサブミットの際に、Javascriptにより生成される値なので、Javascript実行箇所のパラメータをPOSTすることに注意。
#!/usr/bin/env python
# coding: utf-8
import requests
import base64
import random
import json
from bs4 import BeautifulSoup
from time import sleep
class GetAccessToken:
def __init__(self):
self.client_id = '*****' #クライアントID
self.client_secret = '*****' #クライアントシークレット
self.callback_url = '*****' #コールバックURL
self.auth_base_url = 'https://auth.login.yahoo.co.jp'
self.token_url = self.auth_base_url + '/yconnect/v2/token'
self.login_base_url = 'https://login.yahoo.co.jp/config/login'
self.account_edit_url = 'https://account.edit.yahoo.co.jp'
self.nonce = random.randrange(10**31,10**32)
base64_id_secret = self.client_id + ':' + self.client_secret
base64_id_secret_byte = base64.b64encode(base64_id_secret.encode('utf-8'))
self.auth_header = 'Basic ' + base64_id_secret_byte.decode('ascii')
self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
self.http_version = 'HTTP/1.1'
self.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
self.user_name = '*****' #メールアドレス
self.passwd = '*****' #パスワード
self.default_cookie = 'YJTC=ZwNLYKOz0Je_CyW9SGrcmya6TuK.jMk-; YLS=v=2&p=0&n=1;'
self.yjbfp_items = 'uaMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36langjascreen_height1080screen_width1920timezone_offset-540pluginsChrome PDF Plugin|Chrome PDF Viewer|Native Clientcanvas_imageiVBORw0KGgoAAAANSUhEUgAAABAAAAAWCAYAAADJqhx8AAAByUlEQVQ4T9WUv0vDUBSFv1AsaHUSxCylEmsddXGTgiJ2UhcFsY4OpaCIuLo4OQn+Dw61ILgFpJPoIDg4OLTWWlJKgqBYtFGMEnlP6s9apYrgnV6Sdw/3OznvKfywFNHvuhtuvTr/SGB9HXZ2YHUVGhtfgP8eYX/fxDCuGBnR8Ho9PE+wvOxSLCpvRnw99ubmFwi7uy7xOCQSCqEQ3NzA/Dz098PUFHzpwfm52OgSDucJBh26uzUWFjysrUGpZKLrVxSLGtPTZ1hWFQThq8AoFC7p6TFoaOjk4MDHysoD29snGEYLmYxKNGp+LpBOw9zcHUNDGdLpVsJhlcHBMrqex3ECUrCmQIVbVU85OnJYWtKw7TPpum1r7O15agsIDGFWPF5mYiLP4qKfw0MTv7+F42NVBqnmBEJAYExO3jM2lmNgoIlstkQkEiCV8n1fYHYWYjGTQsFCVX0yNMmk53sCAiGXc5mZsdnaytLb20Zfnyr/kKjhYat6Eis5ePJBwXGe3A+FAsRiTbJZhExkomqU318oIvMXF7dEIh1vPun6qXyuvP9wGq+v70gmM3LT+HgXzc1eubasskQSNTraSXu7T65/5zjXex+KvkcATSsyIKf0dAAAAABJRU5ErkJggg=='
def get_az_code(self):
s = requests.Session()
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'User-Agent': self.user_agent,
'HTTP-Version': self.http_version,
'Content-Type': self.content_type,
'Connection': 'Keep-Alive',
}
url = self.auth_base_url + '/yconnect/v2/authorization?response_type=code&client_id=' + self.client_id + '&redirect_uri=' + self.callback_url + '&scope=openid+profile&nonce=' + str(self.nonce)
res = s.get(url, headers=headers)
session_str = '%26session%3D'
session_idx = res.url.find(session_str)
session_id = res.url[session_idx+13:session_idx+21]
res_redirect = s.get(res.url, headers=headers, cookies=s.cookies.get_dict())
login_form_html = BeautifulSoup(res_redirect.text, 'html.parser')
albatross_str = 'document.getElementsByName(".albatross")[0].value ='
albatross_idx = login_form_html.text.find(albatross_str)
albatross = login_form_html.text[albatross_idx+53: albatross_idx+109]
bcrumb_id_check = login_form_html.find(attrs={'name': 'bcrumb_id_check'}).get('value')
bcrumb_send_sms = login_form_html.find(attrs={'name': 'bcrumb_send_sms'}).get('value')
bcrumb_send_mail = login_form_html.find(attrs={'name': 'bcrumb_send_mail'}).get('value')
u = login_form_html.find(attrs={'name': 'u'}).get('value')
done_url = self.auth_base_url + '/yconnect/v2/authorization?.scrumb=0&from=login&session=' + session_id + '&display=page'
reg_url = self.account_edit_url + '/registration?src=yconnectv2&done=https%3A%2F%2Fauth.login.yahoo.co.jp%2Fyconnect%2Fv2%2Fauthorization%3F.scrumb%3D0%26from%3Dlogin%26session%3' + session_id + '%26display%3Dpage&ckey=' + self.client_id + '&.display=page'
data = {
'.ct': '',
'.display': 'page',
'.done': done_url,
'.keep': '',
'.reg': reg_url,
'.src': 'yconnectv2',
'.suppreg_skip': '',
'.yby': '',
'auth_lv': 'pin',
'card_cushion_skip': '',
'ckey': self.client_id,
'nolink': '',
'nonotice': '',
'noreg': '',
'referrer': '',
't_cushion': '',
'.albatross': albatross,
'.requiredPsCheckBox': '',
'.slogin': '',
'.tries': '1',
'ls_autocomp': '',
'showpw_status': '',
'u': u,
'inactive_pw': '',
'send_sms_counts': '1',
'send_mail_counts': '1',
'version': '',
'auth_method': 'pwd',
'pwless_captchaid': '',
'sms_token': '',
'mail_token': '',
'masked_dest': '',
'bcrumb_id_check': bcrumb_id_check,
'bcrumb_send_sms': bcrumb_send_sms,
'bcrumb_send_mail': bcrumb_send_mail,
'user_name': self.user_name,
'assertionInfo': '',
'webauthn': '',
'fido': '0',
'auth_list': 'pwd',
'login': self.user_name,
'passwd': self.passwd,
'code': '',
'btnSubmit': '',
'presistent': 'y',
'yjbfp_items': self.yjbfp_items
}
res_login = s.post(self.login_base_url, data=data, headers=headers, cookies=s.cookies.get_dict())
code_str = '?code='
code_idx = res_login.url.find(code_str)
az_code = res_login.url[code_idx+6:code_idx+14]
return az_code
def get_access_token(self, az_code):
headers = {
'HTTP-Version': self.http_version,
'Content-Type': self.content_type,
'Authorization': self.auth_header
}
data = {
'grant_type': 'authorization_code',
'code': az_code,
'redirect_uri': self.callback_url
}
response = requests.post(self.token_url, data=data, headers=headers)
access_token = response.json()['access_token']
refresh_token = response.json()['refresh_token']
return access_token, refresh_token
def update_access_token(self, refresh_token):
headers = {
'HTTP-Version': self.http_version,
'Content-Type': self.content_type,
'Authorization': self.auth_header
}
data = {
'grant_type': 'refresh_token',
'refresh_token': refresh_token
}
response = requests.post(self.token_url, data=data, headers=headers)
u_access_token = response.json()['access_token']
return u_access_token