fastapi-ipware 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fastapi-ipware
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FastAPI/Starlette-native wrapper for python-ipware to get client IP addresses
|
|
5
|
+
Keywords: fastapi,starlette,ipware,ip-address,client-ip,proxy,x-forwarded-for
|
|
6
|
+
Author: Michael Bianco
|
|
7
|
+
Author-email: Michael Bianco <mike@mikebian.co>
|
|
8
|
+
Requires-Dist: python-ipware>=3.0.0
|
|
9
|
+
Requires-Dist: starlette>=0.49.1
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Project-URL: Repository, https://github.com/iloveitaly/fastapi-ipware
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# fastapi-ipware
|
|
15
|
+
|
|
16
|
+
A FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Zero conversion overhead** - Headers converted once at initialization, not on every request
|
|
21
|
+
- **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects
|
|
22
|
+
- **Customizable precedence** - Easy to configure header priority for your infrastructure
|
|
23
|
+
- **Proxy validation** - Supports trusted proxy lists and proxy count validation
|
|
24
|
+
- **Thin wrapper** - Leverages all the battle-tested logic from python-ipware
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install fastapi-ipware
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or with uv:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv add fastapi-ipware
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from fastapi import FastAPI, Request
|
|
42
|
+
from fastapi_ipware import FastAPIIpWare
|
|
43
|
+
|
|
44
|
+
app = FastAPI()
|
|
45
|
+
ipware = FastAPIIpWare()
|
|
46
|
+
|
|
47
|
+
@app.get("/")
|
|
48
|
+
async def get_ip(request: Request):
|
|
49
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
50
|
+
|
|
51
|
+
if ip:
|
|
52
|
+
return {
|
|
53
|
+
"ip": str(ip),
|
|
54
|
+
"trusted": trusted,
|
|
55
|
+
"is_public": ip.is_global,
|
|
56
|
+
"is_private": ip.is_private,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {"error": "Could not determine IP"}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Basic Usage
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from fastapi_ipware import FastAPIIpWare
|
|
68
|
+
|
|
69
|
+
# Use default configuration (optimized for FastAPI/cloud deployments)
|
|
70
|
+
ipware = FastAPIIpWare()
|
|
71
|
+
|
|
72
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Custom Header Precedence
|
|
76
|
+
|
|
77
|
+
Customize which headers are checked and in what order:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# Prioritize Cloudflare headers
|
|
81
|
+
ipware = FastAPIIpWare(
|
|
82
|
+
precedence=(
|
|
83
|
+
"CF-Connecting-IP",
|
|
84
|
+
"X-Forwarded-For",
|
|
85
|
+
"X-Real-IP",
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# NGINX configuration
|
|
90
|
+
ipware = FastAPIIpWare(
|
|
91
|
+
precedence=(
|
|
92
|
+
"X-Real-IP",
|
|
93
|
+
"X-Forwarded-For",
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Proxy Count Validation
|
|
99
|
+
|
|
100
|
+
Validate that requests pass through the expected number of proxies:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Expect exactly 1 proxy (e.g., AWS ALB)
|
|
104
|
+
ipware = FastAPIIpWare(proxy_count=1)
|
|
105
|
+
|
|
106
|
+
# In strict mode, must be exactly 1 proxy
|
|
107
|
+
ip, trusted = ipware.get_client_ip_from_request(request, strict=True)
|
|
108
|
+
|
|
109
|
+
# In non-strict mode, allow 1 or more proxies
|
|
110
|
+
ip, trusted = ipware.get_client_ip_from_request(request, strict=False)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Trusted Proxy List
|
|
114
|
+
|
|
115
|
+
Validate that requests pass through specific trusted proxies:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Trust specific proxy IP prefixes
|
|
119
|
+
ipware = FastAPIIpWare(
|
|
120
|
+
proxy_list=["10.0.", "10.1."] # AWS internal IPs
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
124
|
+
|
|
125
|
+
# trusted=True only if request came through specified proxies
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Combined Validation
|
|
129
|
+
|
|
130
|
+
Use both proxy count and trusted proxy list:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
# Expect 1 proxy from a specific IP range
|
|
134
|
+
ipware = FastAPIIpWare(
|
|
135
|
+
proxy_count=1,
|
|
136
|
+
proxy_list=["10.0."]
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Real-World Examples
|
|
141
|
+
|
|
142
|
+
### AWS Application Load Balancer
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
ipware = FastAPIIpWare(
|
|
146
|
+
proxy_count=1,
|
|
147
|
+
proxy_list=["10.0."] # Your VPC CIDR
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Cloudflare
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
ipware = FastAPIIpWare(
|
|
155
|
+
precedence=("CF-Connecting-IP",)
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Multiple Proxies (CDN + Load Balancer)
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
ipware = FastAPIIpWare(
|
|
163
|
+
proxy_count=2,
|
|
164
|
+
proxy_list=["10.1.", "10.2."] # CDN and LB IPs
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### NGINX Reverse Proxy
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
ipware = FastAPIIpWare(
|
|
172
|
+
precedence=("X-Real-IP", "X-Forwarded-For"),
|
|
173
|
+
proxy_count=1
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## IP Address Types
|
|
178
|
+
|
|
179
|
+
The returned IP address object has useful properties:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
ip, _ = ipware.get_client_ip_from_request(request)
|
|
183
|
+
|
|
184
|
+
if ip:
|
|
185
|
+
print(f"Is public: {ip.is_global}")
|
|
186
|
+
print(f"Is private: {ip.is_private}")
|
|
187
|
+
print(f"Is loopback: {ip.is_loopback}")
|
|
188
|
+
print(f"Is multicast: {ip.is_multicast}")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
python-ipware automatically prefers:
|
|
192
|
+
1. Public (global) IPs first
|
|
193
|
+
2. Private IPs second
|
|
194
|
+
3. Loopback IPs last
|
|
195
|
+
|
|
196
|
+
## Default Header Precedence
|
|
197
|
+
|
|
198
|
+
The default precedence order is optimized for modern cloud deployments. See the [default precedence configuration](https://github.com/iloveitaly/fastapi-ipware/blob/main/fastapi_ipware/__init__.py#L48-L58) in the source code.
|
|
199
|
+
|
|
200
|
+
## Why fastapi-ipware?
|
|
201
|
+
|
|
202
|
+
`python-ipware` expects WSGI-style headers (`HTTP_X_FORWARDED_FOR`), but FastAPI uses natural header names (`X-Forwarded-For`). This wrapper handles the conversion automatically so you don't have to.
|
|
203
|
+
|
|
204
|
+
## Contributing
|
|
205
|
+
|
|
206
|
+
Contributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[MIT License](LICENSE.md)
|
|
211
|
+
|
|
212
|
+
## Credits
|
|
213
|
+
|
|
214
|
+
Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# fastapi-ipware
|
|
2
|
+
|
|
3
|
+
A FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Zero conversion overhead** - Headers converted once at initialization, not on every request
|
|
8
|
+
- **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects
|
|
9
|
+
- **Customizable precedence** - Easy to configure header priority for your infrastructure
|
|
10
|
+
- **Proxy validation** - Supports trusted proxy lists and proxy count validation
|
|
11
|
+
- **Thin wrapper** - Leverages all the battle-tested logic from python-ipware
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install fastapi-ipware
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or with uv:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv add fastapi-ipware
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from fastapi import FastAPI, Request
|
|
29
|
+
from fastapi_ipware import FastAPIIpWare
|
|
30
|
+
|
|
31
|
+
app = FastAPI()
|
|
32
|
+
ipware = FastAPIIpWare()
|
|
33
|
+
|
|
34
|
+
@app.get("/")
|
|
35
|
+
async def get_ip(request: Request):
|
|
36
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
37
|
+
|
|
38
|
+
if ip:
|
|
39
|
+
return {
|
|
40
|
+
"ip": str(ip),
|
|
41
|
+
"trusted": trusted,
|
|
42
|
+
"is_public": ip.is_global,
|
|
43
|
+
"is_private": ip.is_private,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {"error": "Could not determine IP"}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Basic Usage
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fastapi_ipware import FastAPIIpWare
|
|
55
|
+
|
|
56
|
+
# Use default configuration (optimized for FastAPI/cloud deployments)
|
|
57
|
+
ipware = FastAPIIpWare()
|
|
58
|
+
|
|
59
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Custom Header Precedence
|
|
63
|
+
|
|
64
|
+
Customize which headers are checked and in what order:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# Prioritize Cloudflare headers
|
|
68
|
+
ipware = FastAPIIpWare(
|
|
69
|
+
precedence=(
|
|
70
|
+
"CF-Connecting-IP",
|
|
71
|
+
"X-Forwarded-For",
|
|
72
|
+
"X-Real-IP",
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# NGINX configuration
|
|
77
|
+
ipware = FastAPIIpWare(
|
|
78
|
+
precedence=(
|
|
79
|
+
"X-Real-IP",
|
|
80
|
+
"X-Forwarded-For",
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Proxy Count Validation
|
|
86
|
+
|
|
87
|
+
Validate that requests pass through the expected number of proxies:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# Expect exactly 1 proxy (e.g., AWS ALB)
|
|
91
|
+
ipware = FastAPIIpWare(proxy_count=1)
|
|
92
|
+
|
|
93
|
+
# In strict mode, must be exactly 1 proxy
|
|
94
|
+
ip, trusted = ipware.get_client_ip_from_request(request, strict=True)
|
|
95
|
+
|
|
96
|
+
# In non-strict mode, allow 1 or more proxies
|
|
97
|
+
ip, trusted = ipware.get_client_ip_from_request(request, strict=False)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Trusted Proxy List
|
|
101
|
+
|
|
102
|
+
Validate that requests pass through specific trusted proxies:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Trust specific proxy IP prefixes
|
|
106
|
+
ipware = FastAPIIpWare(
|
|
107
|
+
proxy_list=["10.0.", "10.1."] # AWS internal IPs
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
ip, trusted = ipware.get_client_ip_from_request(request)
|
|
111
|
+
|
|
112
|
+
# trusted=True only if request came through specified proxies
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Combined Validation
|
|
116
|
+
|
|
117
|
+
Use both proxy count and trusted proxy list:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Expect 1 proxy from a specific IP range
|
|
121
|
+
ipware = FastAPIIpWare(
|
|
122
|
+
proxy_count=1,
|
|
123
|
+
proxy_list=["10.0."]
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Real-World Examples
|
|
128
|
+
|
|
129
|
+
### AWS Application Load Balancer
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
ipware = FastAPIIpWare(
|
|
133
|
+
proxy_count=1,
|
|
134
|
+
proxy_list=["10.0."] # Your VPC CIDR
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Cloudflare
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
ipware = FastAPIIpWare(
|
|
142
|
+
precedence=("CF-Connecting-IP",)
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Multiple Proxies (CDN + Load Balancer)
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
ipware = FastAPIIpWare(
|
|
150
|
+
proxy_count=2,
|
|
151
|
+
proxy_list=["10.1.", "10.2."] # CDN and LB IPs
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### NGINX Reverse Proxy
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
ipware = FastAPIIpWare(
|
|
159
|
+
precedence=("X-Real-IP", "X-Forwarded-For"),
|
|
160
|
+
proxy_count=1
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## IP Address Types
|
|
165
|
+
|
|
166
|
+
The returned IP address object has useful properties:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
ip, _ = ipware.get_client_ip_from_request(request)
|
|
170
|
+
|
|
171
|
+
if ip:
|
|
172
|
+
print(f"Is public: {ip.is_global}")
|
|
173
|
+
print(f"Is private: {ip.is_private}")
|
|
174
|
+
print(f"Is loopback: {ip.is_loopback}")
|
|
175
|
+
print(f"Is multicast: {ip.is_multicast}")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
python-ipware automatically prefers:
|
|
179
|
+
1. Public (global) IPs first
|
|
180
|
+
2. Private IPs second
|
|
181
|
+
3. Loopback IPs last
|
|
182
|
+
|
|
183
|
+
## Default Header Precedence
|
|
184
|
+
|
|
185
|
+
The default precedence order is optimized for modern cloud deployments. See the [default precedence configuration](https://github.com/iloveitaly/fastapi-ipware/blob/main/fastapi_ipware/__init__.py#L48-L58) in the source code.
|
|
186
|
+
|
|
187
|
+
## Why fastapi-ipware?
|
|
188
|
+
|
|
189
|
+
`python-ipware` expects WSGI-style headers (`HTTP_X_FORWARDED_FOR`), but FastAPI uses natural header names (`X-Forwarded-For`). This wrapper handles the conversion automatically so you don't have to.
|
|
190
|
+
|
|
191
|
+
## Contributing
|
|
192
|
+
|
|
193
|
+
Contributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
[MIT License](LICENSE.md)
|
|
198
|
+
|
|
199
|
+
## Credits
|
|
200
|
+
|
|
201
|
+
Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import ipaddress
|
|
2
|
+
|
|
3
|
+
from python_ipware.python_ipware import IpWare # type: ignore[import-not-found]
|
|
4
|
+
from starlette.requests import Request
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FastAPIIpWare(IpWare):
|
|
8
|
+
"""
|
|
9
|
+
A FastAPI/Starlette-native wrapper around python-ipware that eliminates
|
|
10
|
+
the need for WSGI-style header conversion at request time.
|
|
11
|
+
|
|
12
|
+
This class accepts natural header names (e.g., "X-Forwarded-For") and
|
|
13
|
+
handles the conversion to ipware's expected format internally.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> from fastapi_ipware import FastAPIIpWare
|
|
17
|
+
>>> ipware = FastAPIIpWare()
|
|
18
|
+
>>> ip, trusted = ipware.get_client_ip_from_request(request)
|
|
19
|
+
|
|
20
|
+
>>> # With custom precedence
|
|
21
|
+
>>> ipware = FastAPIIpWare(
|
|
22
|
+
... precedence=("CF-Connecting-IP", "X-Forwarded-For"),
|
|
23
|
+
... proxy_count=1
|
|
24
|
+
... )
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
precedence: tuple[str, ...] | None = None,
|
|
30
|
+
leftmost: bool = True,
|
|
31
|
+
proxy_count: int | None = None,
|
|
32
|
+
proxy_list: list[str] | None = None,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize FastAPIIpWare with optional configuration.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
precedence: Tuple of header names to check in order. Uses natural header
|
|
39
|
+
names with dashes (e.g., "X-Forwarded-For", "X-Real-IP").
|
|
40
|
+
If None, uses FastAPI-optimized defaults.
|
|
41
|
+
leftmost: If True, use leftmost IP in comma-separated list (standard).
|
|
42
|
+
If False, use rightmost IP (rare legacy configurations).
|
|
43
|
+
proxy_count: Expected number of proxies between client and server.
|
|
44
|
+
Used to validate and extract the correct client IP.
|
|
45
|
+
proxy_list: List of trusted proxy IP prefixes (e.g., ["10.1.", "10.2.3"]).
|
|
46
|
+
"""
|
|
47
|
+
# FastAPI-native precedence using actual header names with dashes
|
|
48
|
+
if precedence is None:
|
|
49
|
+
precedence = (
|
|
50
|
+
"X-Forwarded-For", # Most common, used by AWS ELB, nginx, etc.
|
|
51
|
+
"X-Real-IP", # NGINX
|
|
52
|
+
"CF-Connecting-IP", # Cloudflare
|
|
53
|
+
"True-Client-IP", # Cloudflare Enterprise
|
|
54
|
+
"Fastly-Client-IP", # Fastly, Firebase
|
|
55
|
+
"X-Client-IP", # Microsoft Azure
|
|
56
|
+
"X-Cluster-Client-IP", # Rackspace Cloud Load Balancers
|
|
57
|
+
"Forwarded-For", # RFC 7239
|
|
58
|
+
"Forwarded", # RFC 7239
|
|
59
|
+
"Client-IP", # Akamai, Cloudflare
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Store FastAPI-style precedence for reference
|
|
63
|
+
self._fastapi_precedence = precedence
|
|
64
|
+
|
|
65
|
+
# Convert user-friendly header names (with dashes) to WSGI format once
|
|
66
|
+
# This happens only at initialization, not on every request
|
|
67
|
+
wsgi_precedence = tuple(
|
|
68
|
+
f"HTTP_{header.upper().replace('-', '_')}" for header in precedence
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Initialize parent class with WSGI-style headers
|
|
72
|
+
super().__init__(wsgi_precedence, leftmost, proxy_count, proxy_list)
|
|
73
|
+
|
|
74
|
+
def get_client_ip_from_request(
|
|
75
|
+
self, request: Request, strict: bool = False
|
|
76
|
+
) -> tuple[ipaddress.IPv4Address | ipaddress.IPv6Address | None, bool]:
|
|
77
|
+
"""
|
|
78
|
+
Get client IP address from a FastAPI/Starlette Request object.
|
|
79
|
+
|
|
80
|
+
This is the main method you should use with FastAPI/Starlette applications.
|
|
81
|
+
It handles the header conversion automatically.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
request: FastAPI/Starlette Request object
|
|
85
|
+
strict: If True, enforce exact proxy count/list match.
|
|
86
|
+
If False, allow more proxies than specified.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Tuple of (ip_address, trusted_route) where:
|
|
90
|
+
- ip_address: IPv4Address or IPv6Address object (or None if not found)
|
|
91
|
+
- trusted_route: True if request came through trusted proxies, False otherwise
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
>>> ip, trusted = ipware.get_client_ip_from_request(request)
|
|
95
|
+
>>> if ip:
|
|
96
|
+
... print(f"Client IP: {ip}")
|
|
97
|
+
... print(f"Is global: {ip.is_global}")
|
|
98
|
+
... print(f"Is private: {ip.is_private}")
|
|
99
|
+
"""
|
|
100
|
+
# Convert Starlette headers to WSGI-style dict that parent class expects
|
|
101
|
+
# This happens once per request
|
|
102
|
+
meta = {
|
|
103
|
+
f"HTTP_{name.upper().replace('-', '_')}": value
|
|
104
|
+
for name, value in request.headers.items()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return self.get_client_ip(meta, strict=strict)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
__all__ = ["FastAPIIpWare"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fastapi-ipware"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "FastAPI/Starlette-native wrapper for python-ipware to get client IP addresses"
|
|
5
|
+
keywords = ["fastapi", "starlette", "ipware", "ip-address", "client-ip", "proxy", "x-forwarded-for"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"python-ipware>=3.0.0",
|
|
10
|
+
"starlette>=0.49.1",
|
|
11
|
+
]
|
|
12
|
+
authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
|
|
13
|
+
urls = { "Repository" = "https://github.com/iloveitaly/fastapi-ipware" }
|
|
14
|
+
|
|
15
|
+
# additional packaging information: https://packaging.python.org/en/latest/specifications/core-metadata/#license
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.8.11,<0.9.0"]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
|
|
21
|
+
[tool.uv.build-backend]
|
|
22
|
+
# avoids the src/ directory structure
|
|
23
|
+
module-root = ""
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = ["pytest>=8.3.3","pyright>=1.1.380","ruff>=0.8.0"]
|
|
27
|
+
|
|
28
|
+
[tool.pyright]
|
|
29
|
+
exclude = ["example.py", ".venv", "**/__pycache__"]
|
|
30
|
+
|