Reject tokens with multiple device scopes

This commit is contained in:
Quentin Gliech 2023-05-23 16:59:53 +02:00 committed by Patrick Cloke
parent 98afc57d59
commit f739bde962
2 changed files with 52 additions and 7 deletions

View File

@ -303,13 +303,31 @@ class MSC3861DelegatedAuth(BaseAuth):
else: else:
user_id = UserID.from_string(user_id_str) user_id = UserID.from_string(user_id_str)
# Find device_id in scope # Find device_ids in scope
device_id = None # We only allow a single device_id in the scope, so we find them all in the
for tok in scope: # scope list, and raise if there are more than one. The OIDC server should be
if tok.startswith(SCOPE_MATRIX_DEVICE_PREFIX): # the one enforcing valid scopes, so we raise a 500 if we find an invalid scope.
device_id = tok[len(SCOPE_MATRIX_DEVICE_PREFIX) :] device_ids = [
tok[len(SCOPE_MATRIX_DEVICE_PREFIX) :]
for tok in scope
if tok.startswith(SCOPE_MATRIX_DEVICE_PREFIX)
]
if len(device_ids) > 1:
raise AuthError(
500,
"Multiple device IDs in scope",
)
device_id = device_ids[0] if device_ids else None
if device_id is not None:
# Sanity check the device_id
if len(device_id) > 255 or len(device_id) < 1:
raise AuthError(
500,
"Invalid device ID in scope",
)
if device_id:
# Create the device on the fly if it does not exist # Create the device on the fly if it does not exist
try: try:
await self.store.get_device( await self.store.get_device(

View File

@ -27,6 +27,7 @@ from signedjson.sign import sign_json
from twisted.test.proto_helpers import MemoryReactor from twisted.test.proto_helpers import MemoryReactor
from synapse.api.errors import ( from synapse.api.errors import (
AuthError,
Codes, Codes,
InvalidClientTokenError, InvalidClientTokenError,
OAuthInsufficientScopeError, OAuthInsufficientScopeError,
@ -68,8 +69,9 @@ INTROSPECTION_ENDPOINT = ISSUER + "introspect"
SYNAPSE_ADMIN_SCOPE = "urn:synapse:admin:*" SYNAPSE_ADMIN_SCOPE = "urn:synapse:admin:*"
MATRIX_USER_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:*" MATRIX_USER_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:*"
MATRIX_GUEST_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:guest" MATRIX_GUEST_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:guest"
MATRIX_DEVICE_SCOPE_PREFIX = "urn:matrix:org.matrix.msc2967.client:device:"
DEVICE = "AABBCCDD" DEVICE = "AABBCCDD"
MATRIX_DEVICE_SCOPE = "urn:matrix:org.matrix.msc2967.client:device:" + DEVICE MATRIX_DEVICE_SCOPE = MATRIX_DEVICE_SCOPE_PREFIX + DEVICE
SUBJECT = "abc-def-ghi" SUBJECT = "abc-def-ghi"
USERNAME = "test-user" USERNAME = "test-user"
USER_ID = "@" + USERNAME + ":" + SERVER_NAME USER_ID = "@" + USERNAME + ":" + SERVER_NAME
@ -344,6 +346,31 @@ class MSC3861OAuthDelegation(HomeserverTestCase):
) )
self.assertEqual(requester.device_id, DEVICE) self.assertEqual(requester.device_id, DEVICE)
def test_multiple_devices(self) -> None:
"""The handler should raise an error if multiple devices are found in the scope."""
self.http_client.request = simple_async_mock(
return_value=FakeResponse.json(
code=200,
payload={
"active": True,
"sub": SUBJECT,
"scope": " ".join(
[
MATRIX_USER_SCOPE,
f"{MATRIX_DEVICE_SCOPE_PREFIX}AABBCC",
f"{MATRIX_DEVICE_SCOPE_PREFIX}DDEEFF",
]
),
"username": USERNAME,
},
)
)
request = Mock(args={})
request.args[b"access_token"] = [b"mockAccessToken"]
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
self.get_failure(self.auth.get_user_by_req(request), AuthError)
def test_active_guest_not_allowed(self) -> None: def test_active_guest_not_allowed(self) -> None:
"""The handler should return an insufficient scope error.""" """The handler should return an insufficient scope error."""