@@ -213,7 +213,11 @@ def clean(self):
213
213
214
214
if redirect_uris :
215
215
validator = AllowedURIValidator (
216
- allowed_schemes , name = "redirect uri" , allow_path = True , allow_query = True
216
+ allowed_schemes ,
217
+ name = "redirect uri" ,
218
+ allow_path = True ,
219
+ allow_query = True ,
220
+ allow_hostname_wildcard = oauth2_settings .ALLOW_REDIRECT_URI_WILDCARDS ,
217
221
)
218
222
for uri in redirect_uris :
219
223
validator (uri )
@@ -227,7 +231,11 @@ def clean(self):
227
231
allowed_origins = self .allowed_origins .strip ().split ()
228
232
if allowed_origins :
229
233
# oauthlib allows only https scheme for CORS
230
- validator = AllowedURIValidator (oauth2_settings .ALLOWED_SCHEMES , "allowed origin" )
234
+ validator = AllowedURIValidator (
235
+ oauth2_settings .ALLOWED_SCHEMES ,
236
+ "allowed origin" ,
237
+ allow_hostname_wildcard = oauth2_settings .ALLOW_REDIRECT_URI_WILDCARDS ,
238
+ )
231
239
for uri in allowed_origins :
232
240
validator (uri )
233
241
@@ -777,39 +785,55 @@ def redirect_to_uri_allowed(uri, allowed_uris):
777
785
:param allowed_uris: A list of URIs that are allowed
778
786
"""
779
787
788
+ if not isinstance (allowed_uris , list ):
789
+ raise ValueError ("allowed_uris must be a list" )
790
+
780
791
parsed_uri = urlparse (uri )
781
792
uqs_set = set (parse_qsl (parsed_uri .query ))
782
793
for allowed_uri in allowed_uris :
783
794
parsed_allowed_uri = urlparse (allowed_uri )
784
795
796
+ if parsed_allowed_uri .scheme != parsed_uri .scheme :
797
+ # match failed, continue
798
+ continue
799
+
800
+ """ check hostname """
801
+ if oauth2_settings .ALLOW_REDIRECT_URI_WILDCARDS and parsed_allowed_uri .hostname .startswith ("*" ):
802
+ """ wildcard hostname """
803
+ if not parsed_uri .hostname .endswith (parsed_allowed_uri .hostname [1 :]):
804
+ continue
805
+ elif parsed_allowed_uri .hostname != parsed_uri .hostname :
806
+ continue
807
+
785
808
# From RFC 8252 (Section 7.3)
809
+ # https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
786
810
#
787
811
# Loopback redirect URIs use the "http" scheme
788
812
# [...]
789
813
# The authorization server MUST allow any port to be specified at the
790
814
# time of the request for loopback IP redirect URIs, to accommodate
791
815
# clients that obtain an available ephemeral port from the operating
792
816
# system at the time of the request.
817
+ allowed_uri_is_loopback = parsed_allowed_uri .scheme == "http" and parsed_allowed_uri .hostname in [
818
+ "127.0.0.1" ,
819
+ "::1" ,
820
+ ]
821
+ """ check port """
822
+ if not allowed_uri_is_loopback and parsed_allowed_uri .port != parsed_uri .port :
823
+ continue
824
+
825
+ """ check path """
826
+ if parsed_allowed_uri .path != parsed_uri .path :
827
+ continue
828
+
829
+ """ check querystring """
830
+ aqs_set = set (parse_qsl (parsed_allowed_uri .query ))
831
+ if not aqs_set .issubset (uqs_set ):
832
+ continue # circuit break
793
833
794
- allowed_uri_is_loopback = (
795
- parsed_allowed_uri .scheme == "http"
796
- and parsed_allowed_uri .hostname in ["127.0.0.1" , "::1" ]
797
- and parsed_allowed_uri .port is None
798
- )
799
- if (
800
- allowed_uri_is_loopback
801
- and parsed_allowed_uri .scheme == parsed_uri .scheme
802
- and parsed_allowed_uri .hostname == parsed_uri .hostname
803
- and parsed_allowed_uri .path == parsed_uri .path
804
- ) or (
805
- parsed_allowed_uri .scheme == parsed_uri .scheme
806
- and parsed_allowed_uri .netloc == parsed_uri .netloc
807
- and parsed_allowed_uri .path == parsed_uri .path
808
- ):
809
- aqs_set = set (parse_qsl (parsed_allowed_uri .query ))
810
- if aqs_set .issubset (uqs_set ):
811
- return True
834
+ return True
812
835
836
+ # if uris matched then it's not allowed
813
837
return False
814
838
815
839
@@ -833,4 +857,5 @@ def is_origin_allowed(origin, allowed_origins):
833
857
and parsed_allowed_origin .netloc == parsed_origin .netloc
834
858
):
835
859
return True
860
+
836
861
return False
0 commit comments