@@ -213,7 +213,11 @@ def clean(self):
213213
214214 if redirect_uris :
215215 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_URI_WILDCARDS ,
217221 )
218222 for uri in redirect_uris :
219223 validator (uri )
@@ -227,7 +231,11 @@ def clean(self):
227231 allowed_origins = self .allowed_origins .strip ().split ()
228232 if allowed_origins :
229233 # 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_URI_WILDCARDS ,
238+ )
231239 for uri in allowed_origins :
232240 validator (uri )
233241
@@ -777,39 +785,55 @@ def redirect_to_uri_allowed(uri, allowed_uris):
777785 :param allowed_uris: A list of URIs that are allowed
778786 """
779787
788+ if not isinstance (allowed_uris , list ):
789+ raise ValueError ("allowed_uris must be a list" )
790+
780791 parsed_uri = urlparse (uri )
781792 uqs_set = set (parse_qsl (parsed_uri .query ))
782793 for allowed_uri in allowed_uris :
783794 parsed_allowed_uri = urlparse (allowed_uri )
784795
796+ if parsed_allowed_uri .scheme != parsed_uri .scheme :
797+ # match failed, continue
798+ continue
799+
800+ """ check hostname """
801+ if oauth2_settings .ALLOW_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+
785808 # From RFC 8252 (Section 7.3)
809+ # https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
786810 #
787811 # Loopback redirect URIs use the "http" scheme
788812 # [...]
789813 # The authorization server MUST allow any port to be specified at the
790814 # time of the request for loopback IP redirect URIs, to accommodate
791815 # clients that obtain an available ephemeral port from the operating
792816 # 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
793833
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
812835
836+ # if uris matched then it's not allowed
813837 return False
814838
815839
@@ -833,4 +857,5 @@ def is_origin_allowed(origin, allowed_origins):
833857 and parsed_allowed_origin .netloc == parsed_origin .netloc
834858 ):
835859 return True
860+
836861 return False
0 commit comments