diff --git a/synapse/handlers/cas_handler.py b/synapse/handlers/cas_handler.py
index 295974c521..f3430c6713 100644
--- a/synapse/handlers/cas_handler.py
+++ b/synapse/handlers/cas_handler.py
@@ -77,6 +77,9 @@ class CasHandler:
# identifier for the external_ids table
self.idp_id = "cas"
+ # user-facing name of this auth provider
+ self.idp_name = "CAS"
+
self._sso_handler = hs.get_sso_handler()
self._sso_handler.register_identity_provider(self)
diff --git a/synapse/handlers/oidc_handler.py b/synapse/handlers/oidc_handler.py
index 3e2b60eb7b..6835c6c462 100644
--- a/synapse/handlers/oidc_handler.py
+++ b/synapse/handlers/oidc_handler.py
@@ -121,6 +121,9 @@ class OidcHandler(BaseHandler):
# identifier for the external_ids table
self.idp_id = "oidc"
+ # user-facing name of this auth provider
+ self.idp_name = "OIDC"
+
self._sso_handler = hs.get_sso_handler()
self._sso_handler.register_identity_provider(self)
diff --git a/synapse/handlers/saml_handler.py b/synapse/handlers/saml_handler.py
index 6106237f1f..a8376543c9 100644
--- a/synapse/handlers/saml_handler.py
+++ b/synapse/handlers/saml_handler.py
@@ -75,6 +75,9 @@ class SamlHandler(BaseHandler):
# identifier for the external_ids table
self.idp_id = "saml"
+ # user-facing name of this auth provider
+ self.idp_name = "SAML"
+
# a map from saml session id to Saml2SessionData object
self._outstanding_requests_dict = {} # type: Dict[str, Saml2SessionData]
diff --git a/synapse/handlers/sso.py b/synapse/handlers/sso.py
index d8fb8cdd05..2da1ea2223 100644
--- a/synapse/handlers/sso.py
+++ b/synapse/handlers/sso.py
@@ -14,7 +14,8 @@
# limitations under the License.
import abc
import logging
-from typing import TYPE_CHECKING, Awaitable, Callable, Dict, List, Optional
+from typing import TYPE_CHECKING, Awaitable, Callable, Dict, List, Mapping, Optional
+from urllib.parse import urlencode
import attr
from typing_extensions import NoReturn, Protocol
@@ -66,6 +67,11 @@ class SsoIdentityProvider(Protocol):
Eg, "saml", "cas", "github"
"""
+ @property
+ @abc.abstractmethod
+ def idp_name(self) -> str:
+ """User-facing name for this provider"""
+
@abc.abstractmethod
async def handle_redirect_request(
self,
@@ -156,6 +162,10 @@ class SsoHandler:
assert p_id not in self._identity_providers
self._identity_providers[p_id] = p
+ def get_identity_providers(self) -> Mapping[str, SsoIdentityProvider]:
+ """Get the configured identity providers"""
+ return self._identity_providers
+
def render_error(
self,
request: Request,
@@ -203,8 +213,10 @@ class SsoHandler:
ap = next(iter(self._identity_providers.values()))
return await ap.handle_redirect_request(request, client_redirect_url)
- # otherwise, we have a configuration error
- raise Exception("Multiple SSO identity providers have been configured!")
+ # otherwise, redirect to the IDP picker
+ return "/_synapse/client/pick_idp?" + urlencode(
+ (("redirectUrl", client_redirect_url),)
+ )
async def get_sso_user_by_remote_user_id(
self, auth_provider_id: str, remote_user_id: str
|