@@ -311,7 +311,7 @@ async def _exchange_code(self, code: str) -> Token:
311
311
``ClientAuth`` to authenticate with the client with its ID and secret.
312
312
313
313
Args:
314
- code: The autorization code we got from the callback.
314
+ code: The authorization code we got from the callback.
315
315
316
316
Returns:
317
317
A dict containing various tokens.
@@ -497,11 +497,14 @@ async def _parse_id_token(self, token: Token, nonce: str) -> UserInfo:
497
497
return UserInfo (claims )
498
498
499
499
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 :
502
505
"""Handle an incoming request to /login/sso/redirect
503
506
504
- It redirects the browser to the authorization endpoint with a few
507
+ It returns a redirect to the authorization endpoint with a few
505
508
parameters:
506
509
507
510
- ``client_id``: the client ID set in ``oidc_config.client_id``
@@ -511,24 +514,32 @@ async def handle_redirect_request(
511
514
- ``state``: a random string
512
515
- ``nonce``: a random string
513
516
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
515
518
a signed macaroon token containing the state, the nonce and the
516
519
client_redirect_url params. Those are then checked when the client
517
520
comes back from the provider.
518
521
519
-
520
522
Args:
521
523
request: the incoming request from the browser.
522
524
We'll respond to it with a redirect and a cookie.
523
525
client_redirect_url: the URL that we should redirect the client to
524
526
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
+
525
533
"""
526
534
527
535
state = generate_token ()
528
536
nonce = generate_token ()
529
537
530
538
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 ,
532
543
)
533
544
request .addCookie (
534
545
SESSION_COOKIE_NAME ,
@@ -541,7 +552,7 @@ async def handle_redirect_request(
541
552
542
553
metadata = await self .load_metadata ()
543
554
authorization_endpoint = metadata .get ("authorization_endpoint" )
544
- uri = prepare_grant_uri (
555
+ return prepare_grant_uri (
545
556
authorization_endpoint ,
546
557
client_id = self ._client_auth .client_id ,
547
558
response_type = "code" ,
@@ -550,8 +561,6 @@ async def handle_redirect_request(
550
561
state = state ,
551
562
nonce = nonce ,
552
563
)
553
- request .redirect (uri )
554
- finish_request (request )
555
564
556
565
async def handle_oidc_callback (self , request : SynapseRequest ) -> None :
557
566
"""Handle an incoming request to /_synapse/oidc/callback
@@ -625,7 +634,11 @@ async def handle_oidc_callback(self, request: SynapseRequest) -> None:
625
634
626
635
# Deserialize the session token and verify it.
627
636
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 )
629
642
except MacaroonDeserializationException as e :
630
643
logger .exception ("Invalid session" )
631
644
self ._render_error (request , "invalid_session" , str (e ))
@@ -678,15 +691,21 @@ async def handle_oidc_callback(self, request: SynapseRequest) -> None:
678
691
return
679
692
680
693
# 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
+ )
684
702
685
703
def _generate_oidc_session_token (
686
704
self ,
687
705
state : str ,
688
706
nonce : str ,
689
707
client_redirect_url : str ,
708
+ ui_auth_session_id : Optional [str ],
690
709
duration_in_ms : int = (60 * 60 * 1000 ),
691
710
) -> str :
692
711
"""Generates a signed token storing data about an OIDC session.
@@ -702,6 +721,8 @@ def _generate_oidc_session_token(
702
721
nonce: The ``nonce`` parameter passed to the OIDC provider.
703
722
client_redirect_url: The URL the client gave when it initiated the
704
723
flow.
724
+ ui_auth_session_id: The session ID of the ongoing UI Auth (or
725
+ None if this is a login).
705
726
duration_in_ms: An optional duration for the token in milliseconds.
706
727
Defaults to an hour.
707
728
@@ -718,12 +739,19 @@ def _generate_oidc_session_token(
718
739
macaroon .add_first_party_caveat (
719
740
"client_redirect_url = %s" % (client_redirect_url ,)
720
741
)
742
+ if ui_auth_session_id :
743
+ macaroon .add_first_party_caveat (
744
+ "ui_auth_session_id = %s" % (ui_auth_session_id ,)
745
+ )
721
746
now = self ._clock .time_msec ()
722
747
expiry = now + duration_in_ms
723
748
macaroon .add_first_party_caveat ("time < %d" % (expiry ,))
749
+
724
750
return macaroon .serialize ()
725
751
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 ]]:
727
755
"""Verifies and extract an OIDC session token.
728
756
729
757
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
734
762
state: The state the OIDC provider gave back
735
763
736
764
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
738
766
"""
739
767
macaroon = pymacaroons .Macaroon .deserialize (session )
740
768
@@ -744,17 +772,27 @@ def _verify_oidc_session_token(self, session: str, state: str) -> Tuple[str, str
744
772
v .satisfy_exact ("state = %s" % (state ,))
745
773
v .satisfy_general (lambda c : c .startswith ("nonce = " ))
746
774
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 = " ))
747
778
v .satisfy_general (self ._verify_expiry )
748
779
749
780
v .verify (macaroon , self ._macaroon_secret_key )
750
781
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.
752
784
nonce = self ._get_value_from_macaroon (macaroon , "nonce" )
753
785
client_redirect_url = self ._get_value_from_macaroon (
754
786
macaroon , "client_redirect_url"
755
787
)
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
756
794
757
- return nonce , client_redirect_url
795
+ return nonce , client_redirect_url , ui_auth_session_id
758
796
759
797
def _get_value_from_macaroon (self , macaroon : pymacaroons .Macaroon , key : str ) -> str :
760
798
"""Extracts a caveat value from a macaroon token.
@@ -773,7 +811,7 @@ def _get_value_from_macaroon(self, macaroon: pymacaroons.Macaroon, key: str) ->
773
811
for caveat in macaroon .caveats :
774
812
if caveat .caveat_id .startswith (prefix ):
775
813
return caveat .caveat_id [len (prefix ) :]
776
- raise Exception ("No %s caveat in macaroon" % (key ,))
814
+ raise ValueError ("No %s caveat in macaroon" % (key ,))
777
815
778
816
def _verify_expiry (self , caveat : str ) -> bool :
779
817
prefix = "time < "
0 commit comments