@@ -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_REDIRECT_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_REDIRECT_URI_WILDCARDS ,
238+ )
231239 for uri in allowed_origins :
232240 validator (uri )
233241
@@ -782,35 +790,43 @@ def redirect_to_uri_allowed(uri, allowed_uris):
782790 for allowed_uri in allowed_uris :
783791 parsed_allowed_uri = urlparse (allowed_uri )
784792
793+ if parsed_allowed_uri .scheme != parsed_uri .scheme :
794+ # match failed, continue
795+ continue
796+
797+ """ check hostname """
798+ if oauth2_settings .ALLOW_REDIRECT_URI_WILDCARDS and parsed_allowed_uri .hostname .startswith ("*" ):
799+ """ wildcard hostname """
800+ if not parsed_uri .hostname .endswith (parsed_allowed_uri .hostname [1 :]):
801+ continue
802+ elif parsed_allowed_uri .hostname != parsed_uri .hostname :
803+ continue
804+
785805 # From RFC 8252 (Section 7.3)
806+ # https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
786807 #
787808 # Loopback redirect URIs use the "http" scheme
788809 # [...]
789810 # The authorization server MUST allow any port to be specified at the
790811 # time of the request for loopback IP redirect URIs, to accommodate
791812 # clients that obtain an available ephemeral port from the operating
792813 # system at the time of the request.
814+ allowed_uri_is_loopback = parsed_allowed_uri .scheme == "http" and parsed_allowed_uri .hostname in [
815+ "127.0.0.1" ,
816+ "::1" ,
817+ ]
818+ """ check port """
819+ if not allowed_uri_is_loopback and parsed_allowed_uri .port != parsed_uri .port :
820+ continue
793821
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
822+ """ check path """
823+ if parsed_allowed_uri .path != parsed_uri .path :
824+ continue
812825
813- return False
826+ """ check querystring """
827+ aqs_set = set (parse_qsl (parsed_allowed_uri .query ))
828+ if not aqs_set .issubset (uqs_set ):
829+ continue # circuit break
814830
815831
816832def is_origin_allowed (origin , allowed_origins ):
@@ -833,4 +849,5 @@ def is_origin_allowed(origin, allowed_origins):
833849 and parsed_allowed_origin .netloc == parsed_origin .netloc
834850 ):
835851 return True
852+
836853 return False
0 commit comments