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 all commits
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
1 change: 1 addition & 0 deletions changelog.d/15582.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Experimental [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) support: delegate auth to an OIDC provider.
175 changes: 175 additions & 0 deletions synapse/api/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Copyright 2023 The Matrix.org Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Optional, Tuple

from typing_extensions import Protocol

from twisted.web.server import Request

from synapse.appservice import ApplicationService
from synapse.http.site import SynapseRequest
from synapse.types import Requester

# guests always get this device id.
GUEST_DEVICE_ID = "guest_device"


class Auth(Protocol):
"""The interface that an auth provider must implement."""

async def check_user_in_room(
self,
room_id: str,
requester: Requester,
allow_departed_users: bool = False,
) -> Tuple[str, Optional[str]]:
"""Check if the user is in the room, or was at some point.
Args:
room_id: The room to check.

user_id: The user to check.

current_state: Optional map of the current state of the room.
If provided then that map is used to check whether they are a
member of the room. Otherwise the current membership is
loaded from the database.

allow_departed_users: if True, accept users that were previously
members but have now departed.

Raises:
AuthError if the user is/was not in the room.
Returns:
The current membership of the user in the room and the
membership event ID of the user.
"""

async def get_user_by_req(
self,
request: SynapseRequest,
allow_guest: bool = False,
allow_expired: bool = False,
) -> Requester:
"""Get a registered user's ID.

Args:
request: An HTTP request with an access_token query parameter.
allow_guest: If False, will raise an AuthError if the user making the
request is a guest.
allow_expired: If True, allow the request through even if the account
is expired, or session token lifetime has ended. Note that
/login will deliver access tokens regardless of expiration.

Returns:
Resolves to the requester
Raises:
InvalidClientCredentialsError if no user by that token exists or the token
is invalid.
AuthError if access is denied for the user in the access token
"""

async def validate_appservice_can_control_user_id(
self, app_service: ApplicationService, user_id: str
) -> None:
"""Validates that the app service is allowed to control
the given user.

Args:
app_service: The app service that controls the user
user_id: The author MXID that the app service is controlling

Raises:
AuthError: If the application service is not allowed to control the user
(user namespace regex does not match, wrong homeserver, etc)
or if the user has not been registered yet.
"""

async def get_user_by_access_token(
self,
token: str,
allow_expired: bool = False,
) -> Requester:
"""Validate access token and get user_id from it

Args:
token: The access token to get the user by
allow_expired: If False, raises an InvalidClientTokenError
if the token is expired

Raises:
InvalidClientTokenError if a user by that token exists, but the token is
expired
InvalidClientCredentialsError if no user by that token exists or the token
is invalid
"""

async def is_server_admin(self, requester: Requester) -> bool:
"""Check if the given user is a local server admin.

Args:
requester: user to check

Returns:
True if the user is an admin
"""

async def check_can_change_room_list(
self, room_id: str, requester: Requester
) -> bool:
"""Determine whether the user is allowed to edit the room's entry in the
published room list.

Args:
room_id
user
"""

@staticmethod
def has_access_token(request: Request) -> bool:
"""Checks if the request has an access_token.

Returns:
False if no access_token was given, True otherwise.
"""

@staticmethod
def get_access_token_from_request(request: Request) -> str:
"""Extracts the access_token from the request.

Args:
request: The http request.
Returns:
The access_token
Raises:
MissingClientTokenError: If there isn't a single access_token in the
request
"""

async def check_user_in_room_or_world_readable(
self, room_id: str, requester: Requester, allow_departed_users: bool = False
) -> Tuple[str, Optional[str]]:
"""Checks that the user is or was in the room or the room is world
readable. If it isn't then an exception is raised.

Args:
room_id: room to check
user_id: user to check
allow_departed_users: if True, accept users that were previously
members but have now departed

Returns:
Resolves to the current membership of the user in the room and the
membership event ID of the user. If the user is not in the room and
never has been, then `(Membership.JOIN, None)` is returned.
"""
Loading
Loading