Skip to content

About

FastAPI service for proxy-user authentication and geographic proxy selection, with protocol-aware fallback and PostgreSQL storage.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Auth and proxy routing server

A FastAPI service implementing the auth and routing pipeline in SPEC.md. POST /auth validates credentials and extracts location preferences from a username. POST /route uses the returned context and a protocol to select a proxy.

Requirements

  • Python 3.13 or later and uv.
  • Docker with Docker Compose for the supplied PostgreSQL database.
  • PostgreSQL's libpq client library, required by the standard psycopg package.

Setup and run

Run these commands from the repository root. Choose an available HTTP port; 8000 below is an example:

uv sync --locked
# Create .env if it does not already exist.
test -f .env || cp .env.example .env

docker compose up -d
docker compose exec -T postgres pg_isready -U ping_dev -d ping_residential

APP_PORT=8000
uv run uvicorn src.main:app --host 127.0.0.1 --port "$APP_PORT"

If PostgreSQL is still starting, repeat the readiness check after a few seconds. The .env.example connection matches the Compose database:

DATABASE_URL=postgresql://ping_dev:localdev@localhost:5432/ping_residential?sslmode=disable

init.sql creates the tables and seed data when the database directory is first initialized. Restarting an existing database does not reapply it.

Open http://127.0.0.1:<port>/docs, replacing <port> with your chosen port, to try the endpoints. Add --reload to the Uvicorn command during development. Stop the server with Ctrl+C and the database with docker compose stop.

Example: authenticate, then route

In a second terminal, set APP_PORT to the same port used by the server. Then authenticate the seeded Alice user, requesting a US proxy in New York:

APP_PORT=8000  # Use the port you selected above.
APP_BASE_URL="http://127.0.0.1:${APP_PORT}"

curl -sS -X POST "${APP_BASE_URL}/auth" \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice_residential_c_us_city_new_york","password":"secretpass123"}'

The HTTP 200 response contains the user's ID, bandwidth, and parsed country/city inside context.auth_service. Pass that context to /route:

curl -sS -X POST "${APP_BASE_URL}/route" \
  -H 'Content-Type: application/json' \
  -d '{"context":{"auth_service":{"proxy_user_id":"alice_residential","available_bandwidth":10737418240,"residential_params":{"country":"us","city":"new_york"}}},"protocol":"http"}'

With the seed data, this returns 192.168.1.1:8080. The response preserves the auth context, adds context.route_service, and repeats the selected proxy_addr at the top level. Changing the protocol to socks5 falls back to another US proxy, 192.168.1.3:1080, because New York has no compatible SOCKS5 proxy.

The username format is <proxy_user_id>[_c_<country>[_city_<city>]]:

  • alice_residential: select from any location supporting the protocol.
  • alice_residential_c_gb: select within Great Britain.
  • alice_residential_c_us_city_new_york: try New York, then another US proxy.

City requires country. Routing never falls back to a different country. Every lookup filters by http or socks5, and randomly selects one eligible proxy. The seed addresses are sample records; this service does not forward traffic.

Errors

Error responses use this shape, with the HTTP status sent separately:

{"internal_error_code": 1001, "error_message": "invalid password"}
Source HTTP status Internal code Message
Auth 400 1000 invalid username format
Auth 401 1001 invalid password
Auth 403 1002 no available bandwidth
Auth 401 1003 user not found
Route 400 3001 unsupported protocol
Route 404 3400 no proxy found for country
Request validation 400 4000 invalid request body
Framework HTTP errors Original status, e.g. 404/405 4001 Framework message, e.g. Not Found
Unexpected failures 500 5000 internal server error

Codes 4000, 4001, and 5000 extend the specification. Unexpected failures are logged with their traceback; exception details are omitted from the response. Error 3400 also covers untargeted requests with no protocol-compatible proxies.

Tests

uv run pytest
uv run ruff check .

The suite contains 69 test cases covering username parsing, authentication failures, routing and fallback, context preservation, exact HTTP responses, validation, and exception formatting. Tests use mocked repositories and require neither Docker nor a running server. Ruff checks the Python source and tests for lint issues.

Assumptions

  • /route receives context from a trusted internal caller and does not authenticate again or recheck bandwidth in the database.
  • Bandwidth is returned in bytes. These endpoints do not consume or reserve it.
  • User IDs contain ASCII letters, digits, and underscores. _c_ and _city_ are reserved delimiters. City names contain lowercase alphabetic words separated by single underscores. These are parser assumptions beyond the spec's examples.
  • Country codes must contain exactly two lowercase letters and exist in pycountry's ISO country data. Invalid codes return HTTP 400 with code 1000. Valid countries without matching proxies authenticate normally; routing then returns HTTP 404 with code 3400.
  • Passwords are compared as plaintext to match the supplied seed data.
  • Context preservation covers the declared schema fields; unknown fields are ignored.

Trade-offs and future improvements

  • Code structure: HTTP handlers, business services, and database repositories have separate responsibilities.
  • Database sessions: SQLAlchemy provides a shared connection pool and a session per request. Pool limits, worker counts, and timeouts need tuning together for the intended deployment and database capacity.
  • Proxy selection: ORDER BY random() LIMIT 1 evaluates random values for every eligible row, which becomes expensive as the proxy pool grows. A maintained pool of eligible proxy IDs or a cache could reduce selection work.
  • Query indexes: The supplied schema has no dedicated indexes for protocol, country, or city filtering. Indexes should be chosen using measured query plans.
  • Database round trips: Each routing request performs one lookup, or two when city targeting falls back to country. Routing depends on database availability.
  • Performance: Functional checks do not establish production throughput or latency under concurrency. Load testing is needed before making capacity claims.
  • Optional region fallback: A future allow_region_fallback flag could permit selection within a configured geographic region when the requested country has no compatible proxy. It would default to false, preserving the current 3400 error on a country miss. Enabling it would require a country-to-region mapping, allow selection in another country, and retain the requested protocol.
  • Additional routing rules: State targeting (e.g. Montana within the US) could extend the service's fallback policy. Per-user rate limiting could be added as a separate stage.
  • Passwords: Password hashing would replace plaintext comparison in a production implementation.
  • Logging: SQL logging is enabled for development. Structured request logs would improve operational visibility.
  • Database storage: An attached persistent data volume would provide durable local storage across container removal.

About

FastAPI service for proxy-user authentication and geographic proxy selection, with protocol-aware fallback and PostgreSQL storage.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages