Use custom stage UIA error for MAS cross-signing reset (#17509)

Rather than 501 M_UNRECOGNISED

Client side implementation at
https://github.com/matrix-org/matrix-react-sdk/pull/12892/
This commit is contained in:
Michael Telatynski 2024-08-30 13:52:57 +01:00 committed by Erik Johnston
parent b913aaa788
commit 53a3783750
5 changed files with 40 additions and 18 deletions

View File

@ -0,0 +1 @@
Improve cross-signing upload when using [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) to use a custom UIA flow stage, with web fallback support.

View File

@ -27,7 +27,7 @@ from twisted.web.server import Request
from synapse.api.constants import LoginType from synapse.api.constants import LoginType
from synapse.api.errors import LoginError, SynapseError from synapse.api.errors import LoginError, SynapseError
from synapse.api.urls import CLIENT_API_PREFIX from synapse.api.urls import CLIENT_API_PREFIX
from synapse.http.server import HttpServer, respond_with_html from synapse.http.server import HttpServer, respond_with_html, respond_with_redirect
from synapse.http.servlet import RestServlet, parse_string from synapse.http.servlet import RestServlet, parse_string
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
@ -66,6 +66,17 @@ class AuthRestServlet(RestServlet):
if not session: if not session:
raise SynapseError(400, "No session supplied") raise SynapseError(400, "No session supplied")
if (
self.hs.config.experimental.msc3861.enabled
and stagetype == "org.matrix.cross_signing_reset"
):
config = self.hs.config.experimental.msc3861
if config.account_management_url is not None:
url = f"{config.account_management_url}?action=org.matrix.cross_signing_reset"
else:
url = config.issuer
respond_with_redirect(request, str.encode(url))
if stagetype == LoginType.RECAPTCHA: if stagetype == LoginType.RECAPTCHA:
html = self.recaptcha_template.render( html = self.recaptcha_template.render(
session=session, session=session,

View File

@ -23,10 +23,13 @@
import logging import logging
import re import re
from collections import Counter from collections import Counter
from http import HTTPStatus
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
from synapse.api.errors import Codes, InvalidAPICallError, SynapseError from synapse.api.errors import (
InteractiveAuthIncompleteError,
InvalidAPICallError,
SynapseError,
)
from synapse.http.server import HttpServer from synapse.http.server import HttpServer
from synapse.http.servlet import ( from synapse.http.servlet import (
RestServlet, RestServlet,
@ -409,11 +412,24 @@ class SigningKeyUploadServlet(RestServlet):
else: else:
url = config.issuer url = config.issuer
raise SynapseError( # We use a dummy session ID as this isn't really a UIA flow, but we
HTTPStatus.NOT_IMPLEMENTED, # reuse the same API shape for better client compatibility.
"To reset your end-to-end encryption cross-signing identity, " raise InteractiveAuthIncompleteError(
f"you first need to approve it at {url} and then try again.", "dummy",
Codes.UNRECOGNIZED, {
"session": "dummy",
"flows": [
{"stages": ["org.matrix.cross_signing_reset"]},
],
"params": {
"org.matrix.cross_signing_reset": {
"url": url,
},
},
"msg": "To reset your end-to-end encryption cross-signing "
f"identity, you first need to approve it at {url} and "
"then try again.",
},
) )
else: else:
# Without MSC3861, we require UIA. # Without MSC3861, we require UIA.

View File

@ -550,7 +550,7 @@ class MSC3861OAuthDelegation(HomeserverTestCase):
access_token="mockAccessToken", access_token="mockAccessToken",
) )
self.assertEqual(channel.code, HTTPStatus.NOT_IMPLEMENTED, channel.json_body) self.assertEqual(channel.code, HTTPStatus.UNAUTHORIZED, channel.json_body)
def expect_unauthorized( def expect_unauthorized(
self, method: str, path: str, content: Union[bytes, str, JsonDict] = "" self, method: str, path: str, content: Union[bytes, str, JsonDict] = ""

View File

@ -315,9 +315,7 @@ class SigningKeyUploadServletTestCase(unittest.HomeserverTestCase):
"master_key": master_key2, "master_key": master_key2,
}, },
) )
self.assertEqual( self.assertEqual(channel.code, HTTPStatus.UNAUTHORIZED, channel.json_body)
channel.code, HTTPStatus.NOT_IMPLEMENTED, channel.json_body
)
# Pretend that MAS did UIA and allowed us to replace the master key. # Pretend that MAS did UIA and allowed us to replace the master key.
channel = self.make_request( channel = self.make_request(
@ -349,9 +347,7 @@ class SigningKeyUploadServletTestCase(unittest.HomeserverTestCase):
"master_key": master_key3, "master_key": master_key3,
}, },
) )
self.assertEqual( self.assertEqual(channel.code, HTTPStatus.UNAUTHORIZED, channel.json_body)
channel.code, HTTPStatus.NOT_IMPLEMENTED, channel.json_body
)
# Pretend that MAS did UIA and allowed us to replace the master key. # Pretend that MAS did UIA and allowed us to replace the master key.
channel = self.make_request( channel = self.make_request(
@ -376,6 +372,4 @@ class SigningKeyUploadServletTestCase(unittest.HomeserverTestCase):
"master_key": master_key3, "master_key": master_key3,
}, },
) )
self.assertEqual( self.assertEqual(channel.code, HTTPStatus.UNAUTHORIZED, channel.json_body)
channel.code, HTTPStatus.NOT_IMPLEMENTED, channel.json_body
)