From 7454c9383d0b57f7b35e828bc1780b2d938c4689 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Beno=C3=AEt=20Fontaine?= Date: Thu, 12 Dec 2024 22:28:48 +0100 Subject: [PATCH] Fix auth --- getmyancestors/__init__.py | 2 +- getmyancestors/classes/session.py | 73 ++++++++++++++++++------------- getmyancestors/classes/tree.py | 2 +- getmyancestors/getmyancestors.py | 61 ++++++++++++++++---------- 4 files changed, 81 insertions(+), 57 deletions(-) diff --git a/getmyancestors/__init__.py b/getmyancestors/__init__.py index 79d2b16..7d8a30e 100644 --- a/getmyancestors/__init__.py +++ b/getmyancestors/__init__.py @@ -3,4 +3,4 @@ from . import getmyancestors from . import mergemyancestors -__version__ = "1.0.6" +__version__ = "1.1.0" diff --git a/getmyancestors/classes/session.py b/getmyancestors/classes/session.py index dba41d5..03fa619 100644 --- a/getmyancestors/classes/session.py +++ b/getmyancestors/classes/session.py @@ -2,6 +2,7 @@ import sys import time from urllib.parse import urlparse, parse_qs +import webbrowser import requests from fake_useragent import UserAgent @@ -18,10 +19,21 @@ class Session(requests.Session): :param timeout: time before retry a request """ - def __init__(self, username, password, verbose=False, logfile=False, timeout=60): + def __init__( + self, + username, + password, + client_id, + redirect_uri, + verbose=False, + logfile=False, + timeout=60, + ): super().__init__() self.username = username self.password = password + self.client_id = client_id + self.redirect_uri = redirect_uri self.verbose = verbose self.logfile = logfile self.timeout = timeout @@ -53,7 +65,7 @@ class Session(requests.Session): self.get(url, headers=self.headers) xsrf = self.cookies["XSRF-TOKEN"] url = "https://ident.familysearch.org/login" - self.write_log("Downloading: " + url) + self.write_log("Logging in: " + url) res = self.post( url, data={ @@ -63,37 +75,37 @@ class Session(requests.Session): }, headers=self.headers, ) - try: - data = res.json() - except ValueError: - self.write_log("Invalid auth request") - continue - if "loginError" in data: - self.write_log(data["loginError"]) - return - if "redirectUrl" not in data: - self.write_log(res.text) - continue - - url = data["redirectUrl"] - self.write_log("Downloading: " + url) - res = self.get(url, headers=self.headers) res.raise_for_status() - url = f"https://ident.familysearch.org/cis-web/oauth2/v3/authorization?response_type=code&scope=openid profile email qualifies_for_affiliate_account country&client_id=a02j000000KTRjpAAH&redirect_uri=https://misbach.github.io/fs-auth/index_raw.html&username={self.username}" - self.write_log("Downloading: " + url) - response = self.get(url, allow_redirects=False, headers=self.headers) - location = response.headers["location"] - code = parse_qs(urlparse(location).query).get("code") + url = f"https://ident.familysearch.org/cis-web/oauth2/v3/authorization" + params = { + "response_type": "code", + "scope": "profile email qualifies_for_affiliate_account country", + "client_id": self.client_id, + "redirect_uri": self.redirect_uri, + "username": self.username, + } + self.write_log("Getting an authorization code: " + url) + response = self.get(url, headers=self.headers, params=params) + response.raise_for_status() + try: + code = parse_qs(urlparse(response.url).query).get("code")[0] + except Exception as e: + webbrowser.open(response.url) + print( + "Please log in to the web page that just opened and try again." + ) + sys.exit(2) + url = "https://ident.familysearch.org/cis-web/oauth2/v3/token" - self.write_log("Downloading: " + url) + self.write_log("Exchanging for an access token: " + url) res = self.post( url, data={ "grant_type": "authorization_code", - "client_id": "a02j000000KTRjpAAH", + "client_id": self.client_id, "code": code, - "redirect_uri": "https://misbach.github.io/fs-auth/index_raw.html", + "redirect_uri": self.redirect_uri, }, headers=self.headers, ) @@ -133,20 +145,19 @@ class Session(requests.Session): self.set_current() break - def get_url(self, url, headers=None): + def get_url(self, url, headers=None, no_api=False): """retrieve JSON structure from a FamilySearch URL""" self.counter += 1 if headers is None: headers = {"Accept": "application/x-gedcomx-v1+json"} headers.update(self.headers) + base = "https://api.familysearch.org" + if no_api: + base = "https://familysearch.org" while True: try: self.write_log("Downloading: " + url) - r = self.get( - "https://api.familysearch.org" + url, - timeout=self.timeout, - headers=headers, - ) + r = self.get(base + url, timeout=self.timeout, headers=headers) except requests.exceptions.ReadTimeout: self.write_log("Read timed out") continue diff --git a/getmyancestors/classes/tree.py b/getmyancestors/classes/tree.py index 8ad2eed..1b005f1 100644 --- a/getmyancestors/classes/tree.py +++ b/getmyancestors/classes/tree.py @@ -410,7 +410,7 @@ class Indi: if self.living: return res, famc url = "/service/tree/tree-data/reservations/person/%s/ordinances" % self.fid - data = self.tree.fs.get_url(url, {}) + data = self.tree.fs.get_url(url, {}, no_api=True) if data: for key, o in data["data"].items(): if key == "baptism": diff --git a/getmyancestors/getmyancestors.py b/getmyancestors/getmyancestors.py index 7fbbdf3..b72c55e 100644 --- a/getmyancestors/getmyancestors.py +++ b/getmyancestors/getmyancestors.py @@ -14,6 +14,9 @@ import argparse from getmyancestors.classes.tree import Tree from getmyancestors.classes.session import Session +DEFAULT_CLIENT_ID = "a02j000000KTRjpAAH" +DEFAULT_REDIRECT_URI = "https://misbach.github.io/fs-auth/index_raw.html" + def main(): parser = argparse.ArgumentParser( @@ -99,27 +102,28 @@ def main(): default=False, help="Save settings into file [False]", ) - try: - parser.add_argument( - "-o", - "--outfile", - metavar="", - type=argparse.FileType("w", encoding="UTF-8"), - default=sys.stdout, - help="output GEDCOM file [stdout]", - ) - parser.add_argument( - "-l", - "--logfile", - metavar="", - type=argparse.FileType("w", encoding="UTF-8"), - default=False, - help="output log file [stderr]", - ) - except TypeError: - sys.stderr.write("Python >= 3.4 is required to run this script\n") - sys.stderr.write("(see https://docs.python.org/3/whatsnew/3.4.html#argparse)\n") - sys.exit(2) + parser.add_argument( + "-o", + "--outfile", + metavar="", + type=argparse.FileType("w", encoding="UTF-8"), + default=sys.stdout, + help="output GEDCOM file [stdout]", + ) + parser.add_argument( + "-l", + "--logfile", + metavar="", + type=argparse.FileType("w", encoding="UTF-8"), + default=False, + help="output log file [stderr]", + ) + parser.add_argument( + "--client_id", metavar="", type=str, help="Use Specific Client ID" + ) + parser.add_argument( + "--redirect_uri", metavar="", type=str, help="Use Specific Redirect Uri" + ) # extract arguments from the command line try: @@ -173,7 +177,15 @@ def main(): # initialize a FamilySearch session and a family tree object print("Login to FamilySearch...", file=sys.stderr) - fs = Session(args.username, args.password, args.verbose, args.logfile, args.timeout) + fs = Session( + args.username, + args.password, + args.client_id or DEFAULT_CLIENT_ID, + args.redirect_uri or DEFAULT_REDIRECT_URI, + args.verbose, + args.logfile, + args.timeout, + ) if not fs.logged: sys.exit(2) _ = fs._ @@ -182,9 +194,10 @@ def main(): # check LDS account if args.get_ordinances: test = fs.get_url( - "/service/tree/tree-data/reservations/person/%s/ordinances" % fs.fid, {} + "/service/tree/tree-data/reservations/person/%s/ordinances" % fs.fid, {}, no_api=True ) - if test["status"] != "OK": + if not test or test["status"] != "OK": + print("Need an LDS account") sys.exit(2) try: -- 2.52.0