Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Allow for identifier dicts in User Interactive Auth dicts #7438

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,93 @@
from synapse.module_api import ModuleApi
from synapse.push.mailer import load_jinja2_templates
from synapse.types import Requester, UserID
from synapse.util.msisdn import phone_number_to_msisdn

from ._base import BaseHandler

logger = logging.getLogger(__name__)


def client_dict_convert_legacy_fields_to_identifier(
submission: Dict[str, Union[str, Dict]]
):
"""Take a legacy-formatted login submission or User-Interactive Authentication dict and
updates it to feature an identifier dict instead.
Providing user-identifying information at the top-level of a login or UIA submission is
now deprecated and replaced with identifiers:
https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types
Args:
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
submission: The client dict to convert. Passed by reference and modified
Raises:
SynapseError: if the dict contains a "medium" parameter that is anything other than
"email"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems wrong? It raises if the format is invalid.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that was a check I added, as that's technically required by the spec for login although it's a bit of a weird requirement if identifier is allowed to be m.id.phone.

Will remove the comment though.

"""
if "user" in submission:
submission["identifier"] = {"type": "m.id.user", "user": submission["user"]}
del submission["user"]
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

if "medium" in submission and "address" in submission:
submission["identifier"] = {
"type": "m.id.thirdparty",
"medium": submission["medium"],
"address": submission["address"],
}
del submission["medium"]
del submission["address"]

# We've converted valid, legacy login submissions to an identifier. If the
# dict still doesn't have an identifier, it's invalid
if "identifier" not in submission:
raise SynapseError(
400,
"Missing 'identifier' parameter in login submission",
errcode=Codes.MISSING_PARAM,
)
clokep marked this conversation as resolved.
Show resolved Hide resolved

# Ensure the identifier has a type
if "type" not in submission["identifier"]:
raise SynapseError(
400, "'identifier' dict has no key 'type'", errcode=Codes.MISSING_PARAM,
)


def login_id_phone_to_thirdparty(identifier: Dict[str, str]):
"""Convert a phone login identifier type to a generic threepid identifier. Modifies
the identifier dict in place
clokep marked this conversation as resolved.
Show resolved Hide resolved
Args:
identifier: Login identifier dict of type 'm.id.phone'
"""
if "type" not in identifier:
raise SynapseError(
400, "Invalid phone-type identifier", errcode=Codes.MISSING_PARAM
)

if "country" not in identifier or (
# XXX: We used to require `number` instead of `phone`. The spec
# defines `phone`. So accept both
"phone" not in identifier
and "number" not in identifier
):
raise SynapseError(
400, "Invalid phone-type identifier", errcode=Codes.INVALID_PARAM
)

# Accept both "phone" and "number" as valid keys in m.id.phone
phone_number = identifier.get("phone", identifier.get("number"))

# Convert user-provided phone number to a consistent representation
msisdn = phone_number_to_msisdn(identifier["country"], phone_number)

# Modify the passed dictionary by reference
del identifier["country"]
identifier.pop("number", None)
identifier.pop("phone", None)

identifier["type"] = "m.id.thirdparty"
identifier["medium"] = "msisdn"
identifier["address"] = msisdn


class AuthHandler(BaseHandler):
SESSION_EXPIRE_MS = 48 * 60 * 60 * 1000

Expand Down
46 changes: 7 additions & 39 deletions synapse/rest/client/v1/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

from synapse.api.errors import Codes, LoginError, SynapseError
from synapse.api.ratelimiting import Ratelimiter
from synapse.handlers.auth import (
client_dict_convert_legacy_fields_to_identifier,
login_id_phone_to_thirdparty,
)
from synapse.http.server import finish_request
from synapse.http.servlet import (
RestServlet,
Expand All @@ -27,47 +31,10 @@
from synapse.rest.client.v2_alpha._base import client_patterns
from synapse.rest.well_known import WellKnownBuilder
from synapse.types import UserID
from synapse.util.msisdn import phone_number_to_msisdn

logger = logging.getLogger(__name__)


def login_submission_legacy_convert(submission):
"""
If the input login submission is an old style object
(ie. with top-level user / medium / address) convert it
to a typed object.
"""
if "user" in submission:
submission["identifier"] = {"type": "m.id.user", "user": submission["user"]}
del submission["user"]

if "medium" in submission and "address" in submission:
submission["identifier"] = {
"type": "m.id.thirdparty",
"medium": submission["medium"],
"address": submission["address"],
}
del submission["medium"]
del submission["address"]


def login_id_thirdparty_from_phone(identifier):
"""
Convert a phone login identifier type to a generic threepid identifier
Args:
identifier(dict): Login identifier dict of type 'm.id.phone'

Returns: Login identifier dict of type 'm.id.threepid'
"""
if "country" not in identifier or "number" not in identifier:
raise SynapseError(400, "Invalid phone-type identifier")

msisdn = phone_number_to_msisdn(identifier["country"], identifier["number"])

return {"type": "m.id.thirdparty", "medium": "msisdn", "address": msisdn}


class LoginRestServlet(RestServlet):
PATTERNS = client_patterns("/login$", v1=True)
CAS_TYPE = "m.login.cas"
Expand Down Expand Up @@ -174,7 +141,8 @@ async def _do_other_login(self, login_submission):
login_submission.get("address"),
login_submission.get("user"),
)
login_submission_legacy_convert(login_submission)
# Convert deprecated authdict formats to the current scheme
client_dict_convert_legacy_fields_to_identifier(login_submission)

if "identifier" not in login_submission:
raise SynapseError(400, "Missing param: identifier")
Expand All @@ -185,7 +153,7 @@ async def _do_other_login(self, login_submission):

# convert phone type identifiers to generic threepids
if identifier["type"] == "m.id.phone":
identifier = login_id_thirdparty_from_phone(identifier)
identifier = login_id_phone_to_thirdparty(identifier)

# convert threepid identifiers to user IDs
if identifier["type"] == "m.id.thirdparty":
Expand Down