@@ -311,7 +311,7 @@ async def _exchange_code(self, code: str) -> Token:
311311 ``ClientAuth`` to authenticate with the client with its ID and secret.
312312
313313 Args:
314- code: The autorization code we got from the callback.
314+ code: The authorization code we got from the callback.
315315
316316 Returns:
317317 A dict containing various tokens.
@@ -497,11 +497,14 @@ async def _parse_id_token(self, token: Token, nonce: str) -> UserInfo:
497497 return UserInfo (claims )
498498
499499 async def handle_redirect_request (
500- self , request : SynapseRequest , client_redirect_url : bytes
501- ) -> None :
500+ self ,
501+ request : SynapseRequest ,
502+ client_redirect_url : bytes ,
503+ ui_auth_session_id : Optional [str ] = None ,
504+ ) -> str :
502505 """Handle an incoming request to /login/sso/redirect
503506
504- It redirects the browser to the authorization endpoint with a few
507+ It returns a redirect to the authorization endpoint with a few
505508 parameters:
506509
507510 - ``client_id``: the client ID set in ``oidc_config.client_id``
@@ -511,24 +514,32 @@ async def handle_redirect_request(
511514 - ``state``: a random string
512515 - ``nonce``: a random string
513516
514- In addition to redirecting the client , we are setting a cookie with
517+ In addition generating a redirect URL , we are setting a cookie with
515518 a signed macaroon token containing the state, the nonce and the
516519 client_redirect_url params. Those are then checked when the client
517520 comes back from the provider.
518521
519-
520522 Args:
521523 request: the incoming request from the browser.
522524 We'll respond to it with a redirect and a cookie.
523525 client_redirect_url: the URL that we should redirect the client to
524526 when everything is done
527+ ui_auth_session_id: The session ID of the ongoing UI Auth (or
528+ None if this is a login).
529+
530+ Returns:
531+ The redirect URL to the authorization endpoint.
532+
525533 """
526534
527535 state = generate_token ()
528536 nonce = generate_token ()
529537
530538 cookie = self ._generate_oidc_session_token (
531- state = state , nonce = nonce , client_redirect_url = client_redirect_url .decode (),
539+ state = state ,
540+ nonce = nonce ,
541+ client_redirect_url = client_redirect_url .decode (),
542+ ui_auth_session_id = ui_auth_session_id ,
532543 )
533544 request .addCookie (
534545 SESSION_COOKIE_NAME ,
@@ -541,7 +552,7 @@ async def handle_redirect_request(
541552
542553 metadata = await self .load_metadata ()
543554 authorization_endpoint = metadata .get ("authorization_endpoint" )
544- uri = prepare_grant_uri (
555+ return prepare_grant_uri (
545556 authorization_endpoint ,
546557 client_id = self ._client_auth .client_id ,
547558 response_type = "code" ,
@@ -550,8 +561,6 @@ async def handle_redirect_request(
550561 state = state ,
551562 nonce = nonce ,
552563 )
553- request .redirect (uri )
554- finish_request (request )
555564
556565 async def handle_oidc_callback (self , request : SynapseRequest ) -> None :
557566 """Handle an incoming request to /_synapse/oidc/callback
@@ -625,7 +634,11 @@ async def handle_oidc_callback(self, request: SynapseRequest) -> None:
625634
626635 # Deserialize the session token and verify it.
627636 try :
628- nonce , client_redirect_url = self ._verify_oidc_session_token (session , state )
637+ (
638+ nonce ,
639+ client_redirect_url ,
640+ ui_auth_session_id ,
641+ ) = self ._verify_oidc_session_token (session , state )
629642 except MacaroonDeserializationException as e :
630643 logger .exception ("Invalid session" )
631644 self ._render_error (request , "invalid_session" , str (e ))
@@ -678,15 +691,21 @@ async def handle_oidc_callback(self, request: SynapseRequest) -> None:
678691 return
679692
680693 # and finally complete the login
681- await self ._auth_handler .complete_sso_login (
682- user_id , request , client_redirect_url
683- )
694+ if ui_auth_session_id :
695+ await self ._auth_handler .complete_sso_ui_auth (
696+ user_id , ui_auth_session_id , request
697+ )
698+ else :
699+ await self ._auth_handler .complete_sso_login (
700+ user_id , request , client_redirect_url
701+ )
684702
685703 def _generate_oidc_session_token (
686704 self ,
687705 state : str ,
688706 nonce : str ,
689707 client_redirect_url : str ,
708+ ui_auth_session_id : Optional [str ],
690709 duration_in_ms : int = (60 * 60 * 1000 ),
691710 ) -> str :
692711 """Generates a signed token storing data about an OIDC session.
@@ -702,6 +721,8 @@ def _generate_oidc_session_token(
702721 nonce: The ``nonce`` parameter passed to the OIDC provider.
703722 client_redirect_url: The URL the client gave when it initiated the
704723 flow.
724+ ui_auth_session_id: The session ID of the ongoing UI Auth (or
725+ None if this is a login).
705726 duration_in_ms: An optional duration for the token in milliseconds.
706727 Defaults to an hour.
707728
@@ -718,12 +739,19 @@ def _generate_oidc_session_token(
718739 macaroon .add_first_party_caveat (
719740 "client_redirect_url = %s" % (client_redirect_url ,)
720741 )
742+ if ui_auth_session_id :
743+ macaroon .add_first_party_caveat (
744+ "ui_auth_session_id = %s" % (ui_auth_session_id ,)
745+ )
721746 now = self ._clock .time_msec ()
722747 expiry = now + duration_in_ms
723748 macaroon .add_first_party_caveat ("time < %d" % (expiry ,))
749+
724750 return macaroon .serialize ()
725751
726- def _verify_oidc_session_token (self , session : str , state : str ) -> Tuple [str , str ]:
752+ def _verify_oidc_session_token (
753+ self , session : str , state : str
754+ ) -> Tuple [str , str , Optional [str ]]:
727755 """Verifies and extract an OIDC session token.
728756
729757 This verifies that a given session token was issued by this homeserver
@@ -734,7 +762,7 @@ def _verify_oidc_session_token(self, session: str, state: str) -> Tuple[str, str
734762 state: The state the OIDC provider gave back
735763
736764 Returns:
737- The nonce and the client_redirect_url for this session
765+ The nonce, client_redirect_url, and ui_auth_session_id for this session
738766 """
739767 macaroon = pymacaroons .Macaroon .deserialize (session )
740768
@@ -744,17 +772,27 @@ def _verify_oidc_session_token(self, session: str, state: str) -> Tuple[str, str
744772 v .satisfy_exact ("state = %s" % (state ,))
745773 v .satisfy_general (lambda c : c .startswith ("nonce = " ))
746774 v .satisfy_general (lambda c : c .startswith ("client_redirect_url = " ))
775+ # Sometimes there's a UI auth session ID, it seems to be OK to attempt
776+ # to always satisfy this.
777+ v .satisfy_general (lambda c : c .startswith ("ui_auth_session_id = " ))
747778 v .satisfy_general (self ._verify_expiry )
748779
749780 v .verify (macaroon , self ._macaroon_secret_key )
750781
751- # Extract the `nonce` and `client_redirect_url` from the token
782+ # Extract the `nonce`, `client_redirect_url`, and maybe the
783+ # `ui_auth_session_id` from the token.
752784 nonce = self ._get_value_from_macaroon (macaroon , "nonce" )
753785 client_redirect_url = self ._get_value_from_macaroon (
754786 macaroon , "client_redirect_url"
755787 )
788+ try :
789+ ui_auth_session_id = self ._get_value_from_macaroon (
790+ macaroon , "ui_auth_session_id"
791+ ) # type: Optional[str]
792+ except ValueError :
793+ ui_auth_session_id = None
756794
757- return nonce , client_redirect_url
795+ return nonce , client_redirect_url , ui_auth_session_id
758796
759797 def _get_value_from_macaroon (self , macaroon : pymacaroons .Macaroon , key : str ) -> str :
760798 """Extracts a caveat value from a macaroon token.
@@ -773,7 +811,7 @@ def _get_value_from_macaroon(self, macaroon: pymacaroons.Macaroon, key: str) ->
773811 for caveat in macaroon .caveats :
774812 if caveat .caveat_id .startswith (prefix ):
775813 return caveat .caveat_id [len (prefix ) :]
776- raise Exception ("No %s caveat in macaroon" % (key ,))
814+ raise ValueError ("No %s caveat in macaroon" % (key ,))
777815
778816 def _verify_expiry (self , caveat : str ) -> bool :
779817 prefix = "time < "
0 commit comments