Implement MSC3231: Token authenticated registration (#10142)

Signed-off-by: Callum Brown <callum@calcuode.com>

This is part of my GSoC project implementing [MSC3231](https://github.com/matrix-org/matrix-doc/pull/3231).
This commit is contained in:
Callum Brown 2021-08-21 22:14:43 +01:00 committed by GitHub
parent ecd823d766
commit 947dbbdfd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2389 additions and 1 deletions

View file

@ -28,6 +28,7 @@ from synapse.api.errors import (
ThreepidValidationError,
UnrecognizedRequestError,
)
from synapse.api.ratelimiting import Ratelimiter
from synapse.config import ConfigError
from synapse.config.captcha import CaptchaConfig
from synapse.config.consent import ConsentConfig
@ -379,6 +380,55 @@ class UsernameAvailabilityRestServlet(RestServlet):
return 200, {"available": True}
class RegistrationTokenValidityRestServlet(RestServlet):
"""Check the validity of a registration token.
Example:
GET /_matrix/client/unstable/org.matrix.msc3231/register/org.matrix.msc3231.login.registration_token/validity?token=abcd
200 OK
{
"valid": true
}
"""
PATTERNS = client_patterns(
f"/org.matrix.msc3231/register/{LoginType.REGISTRATION_TOKEN}/validity",
releases=(),
unstable=True,
)
def __init__(self, hs):
"""
Args:
hs (synapse.server.HomeServer): server
"""
super().__init__()
self.hs = hs
self.store = hs.get_datastore()
self.ratelimiter = Ratelimiter(
store=self.store,
clock=hs.get_clock(),
rate_hz=hs.config.ratelimiting.rc_registration_token_validity.per_second,
burst_count=hs.config.ratelimiting.rc_registration_token_validity.burst_count,
)
async def on_GET(self, request):
await self.ratelimiter.ratelimit(None, (request.getClientIP(),))
if not self.hs.config.enable_registration:
raise SynapseError(
403, "Registration has been disabled", errcode=Codes.FORBIDDEN
)
token = parse_string(request, "token", required=True)
valid = await self.store.registration_token_is_valid(token)
return 200, {"valid": valid}
class RegisterRestServlet(RestServlet):
PATTERNS = client_patterns("/register$")
@ -686,6 +736,22 @@ class RegisterRestServlet(RestServlet):
)
if registered:
# Check if a token was used to authenticate registration
registration_token = await self.auth_handler.get_session_data(
session_id,
UIAuthSessionDataConstants.REGISTRATION_TOKEN,
)
if registration_token:
# Increment the `completed` counter for the token
await self.store.use_registration_token(registration_token)
# Indicate that the token has been successfully used so that
# pending is not decremented again when expiring old UIA sessions.
await self.store.mark_ui_auth_stage_complete(
session_id,
LoginType.REGISTRATION_TOKEN,
True,
)
await self.registration_handler.post_registration_actions(
user_id=registered_user_id,
auth_result=auth_result,
@ -868,6 +934,11 @@ def _calculate_registration_flows(
for flow in flows:
flow.insert(0, LoginType.RECAPTCHA)
# Prepend registration token to all flows if we're requiring a token
if config.registration_requires_token:
for flow in flows:
flow.insert(0, LoginType.REGISTRATION_TOKEN)
return flows
@ -876,4 +947,5 @@ def register_servlets(hs, http_server):
MsisdnRegisterRequestTokenRestServlet(hs).register(http_server)
UsernameAvailabilityRestServlet(hs).register(http_server)
RegistrationSubmitTokenServlet(hs).register(http_server)
RegistrationTokenValidityRestServlet(hs).register(http_server)
RegisterRestServlet(hs).register(http_server)