Procházet zdrojové kódy

Handle more errors in FB login

Tulir Asokan před 2 roky
rodič
revize
eceb9aed17

+ 2 - 0
mauigpapi/errors/__init__.py

@@ -13,7 +13,9 @@ from .response import (
     IGChallengeWrongCodeError,
     IGCheckpointError,
     IGConsentRequiredError,
+    IGFBEmailTaken,
     IGFBNoContactPointFoundError,
+    IGFBSSODisabled,
     IGInactiveUserError,
     IGLoginBadPasswordError,
     IGLoginError,

+ 12 - 1
mauigpapi/errors/response.py

@@ -54,6 +54,9 @@ class IGResponseError(IGError):
         self.response = response
         if "message" in json:
             message = json["message"]
+        if "error_type" in json:
+            error_type = json["error_type"]
+            message = f"{error_type}: {message}"
         type_hint = get_type_hints(type(self)).get("body", JSON)
         if type_hint is not JSON and issubclass(type_hint, Serializable):
             self.body = type_hint.deserialize(json)
@@ -149,9 +152,17 @@ class IGLoginInvalidUserError(IGLoginError):
     pass
 
 
+class IGBad2FACodeError(IGResponseError):
+    pass
+
+
 class IGFBNoContactPointFoundError(IGLoginError):
     pass
 
 
-class IGBad2FACodeError(IGResponseError):
+class IGFBEmailTaken(IGLoginError):
+    pass
+
+
+class IGFBSSODisabled(IGLoginError):
     pass

+ 6 - 0
mauigpapi/http/base.py

@@ -33,7 +33,9 @@ from ..errors import (
     IGChallengeError,
     IGCheckpointError,
     IGConsentRequiredError,
+    IGFBEmailTaken,
     IGFBNoContactPointFoundError,
+    IGFBSSODisabled,
     IGInactiveUserError,
     IGLoginBadPasswordError,
     IGLoginInvalidUserError,
@@ -274,6 +276,10 @@ class BaseAndroidAPI:
             raise IGBad2FACodeError(resp, data)
         elif error_type == "fb_no_contact_point_found":
             raise IGFBNoContactPointFoundError(resp, data)
+        elif error_type == "fb_email_taken":
+            raise IGFBEmailTaken(resp, data)
+        elif error_type == "sso_disabled":
+            raise IGFBSSODisabled(resp, data)
 
         raise IGResponseError(resp, data)
 

+ 2 - 0
mauigpapi/types/error.py

@@ -90,6 +90,7 @@ class LoginRequiredResponse(SerializableAttrs):
 class LoginErrorResponseButton(SerializableAttrs):
     title: str
     action: str
+    username: Optional[str] = None
 
 
 @dataclass
@@ -134,3 +135,4 @@ class LoginErrorResponse(SerializableAttrs):
     # FB login error fields
     account_created: Optional[bool] = None
     dryrun_passed: Optional[bool] = None
+    username: Optional[str] = None

+ 56 - 2
mautrix_instagram/web/provisioning_api.py

@@ -33,7 +33,9 @@ from mauigpapi.errors import (
     IGChallengeWrongCodeError,
     IGCheckpointError,
     IGConsentRequiredError,
+    IGFBEmailTaken,
     IGFBNoContactPointFoundError,
+    IGFBSSODisabled,
     IGLoginBadPasswordError,
     IGLoginInvalidUserError,
     IGLoginRequiredError,
@@ -630,9 +632,12 @@ class ProvisioningAPI:
         except IGFBNoContactPointFoundError as e:
             self.log.debug(
                 "%s sent a valid Facebook token, "
-                "but it didn't seem to have an Instagram account linked (%s)",
+                "but it didn't seem to have an Instagram account linked",
                 user.mxid,
-                e.body.serialize(),
+            )
+            self.log.debug("Login error body: %s", e.body.serialize())
+            track(
+                user, "$login_failed", {"error": "fb-no-account-found", "after": "facebook auth"}
             )
             return web.json_response(
                 data={
@@ -642,8 +647,57 @@ class ProvisioningAPI:
                 status=403,
                 headers=self._acao_headers,
             )
+        except IGFBEmailTaken as e:
+            maybe_username = None
+            for button in e.body.buttons or []:
+                if button.title == "username_log_in":
+                    maybe_username = button.username
+                    break
+            self.log.debug(
+                "%s sent a valid Facebook token, but it didn't seem to have an Instagram account "
+                "linked, and the email is already taken by %s",
+                user.mxid,
+                maybe_username,
+            )
+            self.log.debug("Login error body: %s", e.body.serialize())
+            track(
+                user,
+                "$login_failed",
+                {"error": "fb-no-account-found-email-taken", "after": "facebook auth"},
+            )
+            return web.json_response(
+                data={
+                    "error": "You don't have an Instagram account linked to that Facebook account",
+                    "username": maybe_username,
+                    "status": e.body.error_type,
+                },
+                status=403,
+                headers=self._acao_headers,
+            )
+        except IGFBSSODisabled as e:
+            self.log.debug(
+                "%s sent a valid Facebook token for %s, but it doesn't have SSO enabled",
+                user.mxid,
+                e.body.username,
+            )
+            self.log.debug("Login error body: %s", e.body.serialize())
+            track(user, "$login_failed", {"error": "fb-sso-disabled", "after": "facebook auth"})
+            return web.json_response(
+                data={
+                    "error": (
+                        "You haven't enabled sign-in using Facebook "
+                        f"in your Instagram account ({e.body.username})"
+                    ),
+                    "username": e.body.username,
+                    "status": e.body.error_type,
+                },
+                status=403,
+                headers=self._acao_headers,
+            )
         except IGCheckpointError as e:
             return self._checkpoint_error(user, "<facebook credentials>", e, after="facebook auth")
         except IGConsentRequiredError as e:
             return self._consent_error(user, "<facebook credentials>", e, after="facebook auth")
+        except Exception as e:
+            return self._unknown_error(user, "<facebook credentials>", e, after="facebook auth")
         return await self._finish_login(user, state, api, login_resp=resp, after="facebook auth")