77from __future__ import annotations
88
99import inspect
10+ import ipaddress
1011import time
1112
1213from fastapi import APIRouter , Depends , Request
3940# 浏览器打不开的基址主机:回环与通配监听。PUBLIC_BASE_URL 默认值就是
4041# 127.0.0.1:8000,远程/容器部署忘了改时,回调会跳到**用户自己这台机器**,
4142# 浏览器既回不到服务也不报错(issue #21)。
42- _UNREACHABLE_BASE_HOSTS = frozenset ({"127.0.0.1" , " localhost" , "::1" , "0.0.0.0 " })
43+ _LOCALHOST_NAMES = frozenset ({"localhost" })
4344
4445
4546def _host_of (authority : str ) -> str :
@@ -50,6 +51,17 @@ def _host_of(authority: str) -> str:
5051 return text .split (":" , 1 )[0 ].lower ()
5152
5253
54+ def _browser_cannot_reach (host : str ) -> bool :
55+ """回环 / 通配监听:浏览器打开它到不了本服务(或到不了发请求的那个人)。"""
56+ if host in _LOCALHOST_NAMES :
57+ return True
58+ try :
59+ address = ipaddress .ip_address (host )
60+ except ValueError : # 普通域名 / 无效字面量:按可达处理
61+ return False
62+ return address .is_loopback or address .is_unspecified
63+
64+
5365def _request_origin (request : Request ) -> str :
5466 """当前请求的浏览器 origin(采信反代/隧道的 X-Forwarded-Proto)。"""
5567 proto = request .headers .get ("x-forwarded-proto" , "" ).split ("," , 1 )[0 ].strip ().lower ()
@@ -73,9 +85,9 @@ def resolve_public_callback_url(settings: Settings,
7385 `host_allowed` 白名单,故这里采信 Host 头不会放大攻击面。
7486 """
7587 base = settings .public_base_url .rstrip ("/" )
76- if request is not None and _host_of (base ) in _UNREACHABLE_BASE_HOSTS :
88+ if request is not None and _browser_cannot_reach ( _host_of (base )) :
7789 request_host = _host_of (request .headers .get ("host" , "" ) or request .url .netloc )
78- if request_host and request_host not in _UNREACHABLE_BASE_HOSTS :
90+ if request_host and not _browser_cannot_reach ( request_host ) :
7991 base = _request_origin (request ).rstrip ("/" )
8092 return base + "/authorize"
8193
0 commit comments