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

Experimental MSC3861 support: delegate auth to an OIDC provider #15582

Merged
merged 24 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a4e264e
Make the api.auth.Auth a Protocol
sandhose Jun 17, 2022
d7ebf6d
Initial MSC3964 support: delegation of auth to OIDC server
sandhose Sep 13, 2022
e4bdd76
Expose the public keys used for client authentication on an endpoint
sandhose May 16, 2023
6e96ae2
MSC2965: OIDC Provider discovery via well-known document
sandhose Nov 18, 2021
0a6566a
Save the scopes in the requester
sandhose Jun 17, 2022
0302ad6
Handle the Synapse admin scope
sandhose Jun 20, 2022
d39e2b7
Record the `sub` claims as an external_id
sandhose Sep 13, 2022
e47917e
Use `name` claim as display name when registering users on the fly.
hughns Sep 20, 2022
c56a7e2
MSC2967: Check access token scope for use as user and add guest support
hughns Nov 16, 2022
aa9b1ef
Initial tests for OAuth delegation
hughns Nov 16, 2022
7098589
Actually enforce guest + return www-authenticate header
hughns Nov 17, 2022
8ff7984
Disable account related endpoints when using OAuth delegation
sandhose May 10, 2023
3dbefc4
Test MSC2965 implementation: well-known discovery document
hughns Feb 6, 2023
f97937e
Refactor config to be an experimental feature
hughns May 9, 2023
77f3ecb
Tests for JWKS endpoint
hughns Feb 7, 2023
29af6e2
Add an admin token for MAS -> Synapse calls
sandhose Apr 4, 2023
0109f26
Make AS tokens work & allow ASes to /register
sandhose May 16, 2023
b3d7f2a
Disable incompatible Admin API endpoints
sandhose May 10, 2023
80326f2
Newsfile.
sandhose May 12, 2023
61be1f1
Handle errors when introspecting tokens
sandhose May 22, 2023
9f830ff
Make OIDC scope constants
sandhose May 22, 2023
95b2eee
Reject tokens with multiple device scopes
sandhose May 23, 2023
261968d
Make the config tests spawn the homeserver only when needed
sandhose May 26, 2023
78133b7
Enforce that an admin token also has the basic Matrix API scope
sandhose May 26, 2023
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
Prev Previous commit
Next Next commit
Reject tokens with multiple device scopes
  • Loading branch information
sandhose committed May 30, 2023
commit 95b2eeec9688288e4fbbb47778b6f8d784168594
30 changes: 24 additions & 6 deletions synapse/api/auth/msc3861_delegated.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,31 @@ async def get_user_by_access_token(
else:
user_id = UserID.from_string(user_id_str)

# Find device_id in scope
device_id = None
for tok in scope:
if tok.startswith(SCOPE_MATRIX_DEVICE_PREFIX):
device_id = tok[len(SCOPE_MATRIX_DEVICE_PREFIX) :]
# Find device_ids in scope
# We only allow a single device_id in the scope, so we find them all in the
# scope list, and raise if there are more than one. The OIDC server should be
# the one enforcing valid scopes, so we raise a 500 if we find an invalid scope.
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
try:
await self.store.get_device(
Expand Down
29 changes: 28 additions & 1 deletion tests/handlers/test_oauth_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from twisted.test.proto_helpers import MemoryReactor

from synapse.api.errors import (
AuthError,
Codes,
InvalidClientTokenError,
OAuthInsufficientScopeError,
Expand Down Expand Up @@ -68,8 +69,9 @@
SYNAPSE_ADMIN_SCOPE = "urn:synapse:admin:*"
MATRIX_USER_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:*"
MATRIX_GUEST_SCOPE = "urn:matrix:org.matrix.msc2967.client:api:guest"
MATRIX_DEVICE_SCOPE_PREFIX = "urn:matrix:org.matrix.msc2967.client:device:"
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"
USERNAME = "test-user"
USER_ID = "@" + USERNAME + ":" + SERVER_NAME
Expand Down Expand Up @@ -344,6 +346,31 @@ def test_active_user_with_device(self) -> None:
)
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:
"""The handler should return an insufficient scope error."""

Expand Down