@@ -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,7 @@ 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 (oauth2_settings .ALLOWED_SCHEMES , "allowed origin" , allow_hostname_wildcard = oauth2_settings . ALLOW_REDIRECT_URI_WILDCARDS )
231235 for uri in allowed_origins :
232236 validator (uri )
233237
@@ -782,35 +786,43 @@ def redirect_to_uri_allowed(uri, allowed_uris):
782786 for allowed_uri in allowed_uris :
783787 parsed_allowed_uri = urlparse (allowed_uri )
784788
789+ if parsed_allowed_uri .scheme != parsed_uri .scheme :
790+ # match failed, continue
791+ continue
792+
793+ """ check hostname """
794+ if oauth2_settings .ALLOW_REDIRECT_URI_WILDCARDS and parsed_allowed_uri .hostname .startswith ("*" ):
795+ """ wildcard hostname """
796+ if not parsed_uri .hostname .endswith (parsed_allowed_uri .hostname [1 :]):
797+ continue
798+ elif parsed_allowed_uri .hostname != parsed_uri .hostname :
799+ continue
800+
785801 # From RFC 8252 (Section 7.3)
802+ # https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
786803 #
787804 # Loopback redirect URIs use the "http" scheme
788805 # [...]
789806 # The authorization server MUST allow any port to be specified at the
790807 # time of the request for loopback IP redirect URIs, to accommodate
791808 # clients that obtain an available ephemeral port from the operating
792809 # system at the time of the request.
810+ allowed_uri_is_loopback = parsed_allowed_uri .scheme == "http" and parsed_allowed_uri .hostname in [
811+ "127.0.0.1" ,
812+ "::1" ,
813+ ]
814+ """ check port """
815+ if not allowed_uri_is_loopback and parsed_allowed_uri .port != parsed_uri .port :
816+ continue
793817
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
818+ """ check path """
819+ if parsed_allowed_uri .path != parsed_uri .path :
820+ continue
812821
813- return False
822+ """ check querystring """
823+ aqs_set = set (parse_qsl (parsed_allowed_uri .query ))
824+ if not aqs_set .issubset (uqs_set ):
825+ continue # circuit break
814826
815827
816828def is_origin_allowed (origin , allowed_origins ):
@@ -833,4 +845,5 @@ def is_origin_allowed(origin, allowed_origins):
833845 and parsed_allowed_origin .netloc == parsed_origin .netloc
834846 ):
835847 return True
848+
836849 return False
0 commit comments