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

Commit 2f053f3

Browse files
authored
Stabilise support for MSC2918 refresh tokens as they have now been merged into the Matrix specification. (#11435)
1 parent a15a893 commit 2f053f3

File tree

6 files changed

+115
-44
lines changed

6 files changed

+115
-44
lines changed

changelog.d/11435.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stabilise support for [MSC2918](https://github.com/matrix-org/matrix-doc/blob/main/proposals/2918-refreshtokens.md#msc2918-refresh-tokens) refresh tokens as they have now been merged into the Matrix specification.

docs/sample_config.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -1209,6 +1209,44 @@ oembed:
12091209
#
12101210
#session_lifetime: 24h
12111211

1212+
# Time that an access token remains valid for, if the session is
1213+
# using refresh tokens.
1214+
# For more information about refresh tokens, please see the manual.
1215+
# Note that this only applies to clients which advertise support for
1216+
# refresh tokens.
1217+
#
1218+
# Note also that this is calculated at login time and refresh time:
1219+
# changes are not applied to existing sessions until they are refreshed.
1220+
#
1221+
# By default, this is 5 minutes.
1222+
#
1223+
#refreshable_access_token_lifetime: 5m
1224+
1225+
# Time that a refresh token remains valid for (provided that it is not
1226+
# exchanged for another one first).
1227+
# This option can be used to automatically log-out inactive sessions.
1228+
# Please see the manual for more information.
1229+
#
1230+
# Note also that this is calculated at login time and refresh time:
1231+
# changes are not applied to existing sessions until they are refreshed.
1232+
#
1233+
# By default, this is infinite.
1234+
#
1235+
#refresh_token_lifetime: 24h
1236+
1237+
# Time that an access token remains valid for, if the session is NOT
1238+
# using refresh tokens.
1239+
# Please note that not all clients support refresh tokens, so setting
1240+
# this to a short value may be inconvenient for some users who will
1241+
# then be logged out frequently.
1242+
#
1243+
# Note also that this is calculated at login time: changes are not applied
1244+
# retrospectively to existing sessions for users that have already logged in.
1245+
#
1246+
# By default, this is infinite.
1247+
#
1248+
#nonrefreshable_access_token_lifetime: 24h
1249+
12121250
# The user must provide all of the below types of 3PID when registering.
12131251
#
12141252
#registrations_require_3pid:

synapse/config/registration.py

+38
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,44 @@ def generate_config_section(self, generate_secrets=False, **kwargs):
220220
#
221221
#session_lifetime: 24h
222222
223+
# Time that an access token remains valid for, if the session is
224+
# using refresh tokens.
225+
# For more information about refresh tokens, please see the manual.
226+
# Note that this only applies to clients which advertise support for
227+
# refresh tokens.
228+
#
229+
# Note also that this is calculated at login time and refresh time:
230+
# changes are not applied to existing sessions until they are refreshed.
231+
#
232+
# By default, this is 5 minutes.
233+
#
234+
#refreshable_access_token_lifetime: 5m
235+
236+
# Time that a refresh token remains valid for (provided that it is not
237+
# exchanged for another one first).
238+
# This option can be used to automatically log-out inactive sessions.
239+
# Please see the manual for more information.
240+
#
241+
# Note also that this is calculated at login time and refresh time:
242+
# changes are not applied to existing sessions until they are refreshed.
243+
#
244+
# By default, this is infinite.
245+
#
246+
#refresh_token_lifetime: 24h
247+
248+
# Time that an access token remains valid for, if the session is NOT
249+
# using refresh tokens.
250+
# Please note that not all clients support refresh tokens, so setting
251+
# this to a short value may be inconvenient for some users who will
252+
# then be logged out frequently.
253+
#
254+
# Note also that this is calculated at login time: changes are not applied
255+
# retrospectively to existing sessions for users that have already logged in.
256+
#
257+
# By default, this is infinite.
258+
#
259+
#nonrefreshable_access_token_lifetime: 24h
260+
223261
# The user must provide all of the below types of 3PID when registering.
224262
#
225263
#registrations_require_3pid:

synapse/rest/client/login.py

+13-16
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class LoginRestServlet(RestServlet):
7272
JWT_TYPE_DEPRECATED = "m.login.jwt"
7373
APPSERVICE_TYPE = "m.login.application_service"
7474
APPSERVICE_TYPE_UNSTABLE = "uk.half-shot.msc2778.login.application_service"
75-
REFRESH_TOKEN_PARAM = "org.matrix.msc2918.refresh_token"
75+
REFRESH_TOKEN_PARAM = "refresh_token"
7676

7777
def __init__(self, hs: "HomeServer"):
7878
super().__init__()
@@ -90,7 +90,7 @@ def __init__(self, hs: "HomeServer"):
9090
self.saml2_enabled = hs.config.saml2.saml2_enabled
9191
self.cas_enabled = hs.config.cas.cas_enabled
9292
self.oidc_enabled = hs.config.oidc.oidc_enabled
93-
self._msc2918_enabled = (
93+
self._refresh_tokens_enabled = (
9494
hs.config.registration.refreshable_access_token_lifetime is not None
9595
)
9696

@@ -163,17 +163,16 @@ def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
163163
async def on_POST(self, request: SynapseRequest) -> Tuple[int, LoginResponse]:
164164
login_submission = parse_json_object_from_request(request)
165165

166-
if self._msc2918_enabled:
167-
# Check if this login should also issue a refresh token, as per MSC2918
168-
should_issue_refresh_token = login_submission.get(
169-
"org.matrix.msc2918.refresh_token", False
170-
)
171-
if not isinstance(should_issue_refresh_token, bool):
172-
raise SynapseError(
173-
400, "`org.matrix.msc2918.refresh_token` should be true or false."
174-
)
175-
else:
176-
should_issue_refresh_token = False
166+
# Check to see if the client requested a refresh token.
167+
client_requested_refresh_token = login_submission.get(
168+
LoginRestServlet.REFRESH_TOKEN_PARAM, False
169+
)
170+
if not isinstance(client_requested_refresh_token, bool):
171+
raise SynapseError(400, "`refresh_token` should be true or false.")
172+
173+
should_issue_refresh_token = (
174+
self._refresh_tokens_enabled and client_requested_refresh_token
175+
)
177176

178177
try:
179178
if login_submission["type"] in (
@@ -463,9 +462,7 @@ def _get_auth_flow_dict_for_idp(idp: SsoIdentityProvider) -> JsonDict:
463462

464463

465464
class RefreshTokenServlet(RestServlet):
466-
PATTERNS = client_patterns(
467-
"/org.matrix.msc2918.refresh_token/refresh$", releases=(), unstable=True
468-
)
465+
PATTERNS = (re.compile("^/_matrix/client/v1/refresh$"),)
469466

470467
def __init__(self, hs: "HomeServer"):
471468
self._auth_handler = hs.get_auth_handler()

synapse/rest/client/register.py

+10-13
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ def __init__(self, hs: "HomeServer"):
419419
self.password_policy_handler = hs.get_password_policy_handler()
420420
self.clock = hs.get_clock()
421421
self._registration_enabled = self.hs.config.registration.enable_registration
422-
self._msc2918_enabled = (
422+
self._refresh_tokens_enabled = (
423423
hs.config.registration.refreshable_access_token_lifetime is not None
424424
)
425425

@@ -445,18 +445,15 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
445445
f"Do not understand membership kind: {kind}",
446446
)
447447

448-
if self._msc2918_enabled:
449-
# Check if this registration should also issue a refresh token, as
450-
# per MSC2918
451-
should_issue_refresh_token = body.get(
452-
"org.matrix.msc2918.refresh_token", False
453-
)
454-
if not isinstance(should_issue_refresh_token, bool):
455-
raise SynapseError(
456-
400, "`org.matrix.msc2918.refresh_token` should be true or false."
457-
)
458-
else:
459-
should_issue_refresh_token = False
448+
# Check if the clients wishes for this registration to issue a refresh
449+
# token.
450+
client_requested_refresh_tokens = body.get("refresh_token", False)
451+
if not isinstance(client_requested_refresh_tokens, bool):
452+
raise SynapseError(400, "`refresh_token` should be true or false.")
453+
454+
should_issue_refresh_token = (
455+
self._refresh_tokens_enabled and client_requested_refresh_tokens
456+
)
460457

461458
# Pull out the provided username and do basic sanity checks early since
462459
# the auth layer will store these in sessions.

tests/rest/client/test_auth.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ def use_refresh_token(self, refresh_token: str) -> FakeChannel:
520520
"""
521521
return self.make_request(
522522
"POST",
523-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
523+
"/_matrix/client/v1/refresh",
524524
{"refresh_token": refresh_token},
525525
)
526526

@@ -557,7 +557,7 @@ def test_login_issue_refresh_token(self):
557557
login_with_refresh = self.make_request(
558558
"POST",
559559
"/_matrix/client/r0/login",
560-
{"org.matrix.msc2918.refresh_token": True, **body},
560+
{"refresh_token": True, **body},
561561
)
562562
self.assertEqual(login_with_refresh.code, 200, login_with_refresh.result)
563563
self.assertIn("refresh_token", login_with_refresh.json_body)
@@ -588,7 +588,7 @@ def test_register_issue_refresh_token(self):
588588
"username": "test3",
589589
"password": self.user_pass,
590590
"auth": {"type": LoginType.DUMMY},
591-
"org.matrix.msc2918.refresh_token": True,
591+
"refresh_token": True,
592592
},
593593
)
594594
self.assertEqual(register_with_refresh.code, 200, register_with_refresh.result)
@@ -603,7 +603,7 @@ def test_token_refresh(self):
603603
"type": "m.login.password",
604604
"user": "test",
605605
"password": self.user_pass,
606-
"org.matrix.msc2918.refresh_token": True,
606+
"refresh_token": True,
607607
}
608608
login_response = self.make_request(
609609
"POST",
@@ -614,7 +614,7 @@ def test_token_refresh(self):
614614

615615
refresh_response = self.make_request(
616616
"POST",
617-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
617+
"/_matrix/client/v1/refresh",
618618
{"refresh_token": login_response.json_body["refresh_token"]},
619619
)
620620
self.assertEqual(refresh_response.code, 200, refresh_response.result)
@@ -641,7 +641,7 @@ def test_refreshable_access_token_expiration(self):
641641
"type": "m.login.password",
642642
"user": "test",
643643
"password": self.user_pass,
644-
"org.matrix.msc2918.refresh_token": True,
644+
"refresh_token": True,
645645
}
646646
login_response = self.make_request(
647647
"POST",
@@ -655,7 +655,7 @@ def test_refreshable_access_token_expiration(self):
655655

656656
refresh_response = self.make_request(
657657
"POST",
658-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
658+
"/_matrix/client/v1/refresh",
659659
{"refresh_token": login_response.json_body["refresh_token"]},
660660
)
661661
self.assertEqual(refresh_response.code, 200, refresh_response.result)
@@ -761,7 +761,7 @@ def test_refresh_token_expiry(self):
761761
"type": "m.login.password",
762762
"user": "test",
763763
"password": self.user_pass,
764-
"org.matrix.msc2918.refresh_token": True,
764+
"refresh_token": True,
765765
}
766766
login_response = self.make_request(
767767
"POST",
@@ -811,7 +811,7 @@ def test_ultimate_session_expiry(self):
811811
"type": "m.login.password",
812812
"user": "test",
813813
"password": self.user_pass,
814-
"org.matrix.msc2918.refresh_token": True,
814+
"refresh_token": True,
815815
}
816816
login_response = self.make_request(
817817
"POST",
@@ -868,7 +868,7 @@ def test_refresh_token_invalidation(self):
868868
"type": "m.login.password",
869869
"user": "test",
870870
"password": self.user_pass,
871-
"org.matrix.msc2918.refresh_token": True,
871+
"refresh_token": True,
872872
}
873873
login_response = self.make_request(
874874
"POST",
@@ -880,7 +880,7 @@ def test_refresh_token_invalidation(self):
880880
# This first refresh should work properly
881881
first_refresh_response = self.make_request(
882882
"POST",
883-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
883+
"/_matrix/client/v1/refresh",
884884
{"refresh_token": login_response.json_body["refresh_token"]},
885885
)
886886
self.assertEqual(
@@ -890,7 +890,7 @@ def test_refresh_token_invalidation(self):
890890
# This one as well, since the token in the first one was never used
891891
second_refresh_response = self.make_request(
892892
"POST",
893-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
893+
"/_matrix/client/v1/refresh",
894894
{"refresh_token": login_response.json_body["refresh_token"]},
895895
)
896896
self.assertEqual(
@@ -900,7 +900,7 @@ def test_refresh_token_invalidation(self):
900900
# This one should not, since the token from the first refresh is not valid anymore
901901
third_refresh_response = self.make_request(
902902
"POST",
903-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
903+
"/_matrix/client/v1/refresh",
904904
{"refresh_token": first_refresh_response.json_body["refresh_token"]},
905905
)
906906
self.assertEqual(
@@ -928,7 +928,7 @@ def test_refresh_token_invalidation(self):
928928
# Now that the access token from the last valid refresh was used once, refreshing with the N-1 token should fail
929929
fourth_refresh_response = self.make_request(
930930
"POST",
931-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
931+
"/_matrix/client/v1/refresh",
932932
{"refresh_token": login_response.json_body["refresh_token"]},
933933
)
934934
self.assertEqual(
@@ -938,7 +938,7 @@ def test_refresh_token_invalidation(self):
938938
# But refreshing from the last valid refresh token still works
939939
fifth_refresh_response = self.make_request(
940940
"POST",
941-
"/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
941+
"/_matrix/client/v1/refresh",
942942
{"refresh_token": second_refresh_response.json_body["refresh_token"]},
943943
)
944944
self.assertEqual(

0 commit comments

Comments
 (0)