26 lines
1.5 KiB
Markdown
26 lines
1.5 KiB
Markdown
# Backend contract
|
|
|
|
The admin UI is a single `backend/admin.html` file. It sends the configured admin
|
|
Bearer token in memory/session storage and calls only `/admin/api/*` endpoints.
|
|
|
|
## Device flow
|
|
|
|
1. Operator creates a user and device in the page.
|
|
2. Backend returns a one-time `enrollment_url` and a stable `subscription_url`.
|
|
3. The app makes the first request with `X-Device-Token: <random token>` and
|
|
`User-Agent: vpnapp/vMAJOR.MINOR/DEVICE_UUID`.
|
|
4. Backend checks URL UUID, credential, and User-Agent UUID atomically, records the
|
|
User-Agent, and returns the user's decrypted-for-transport config payload.
|
|
5. Polling every few hours uses the same device token and bound User-Agent. A different
|
|
installation cannot reuse the same device binding unless the token is copied; UUID
|
|
and User-Agent alone never authenticate.
|
|
|
|
The device endpoint must be deployed behind TLS. The admin token is not accepted on
|
|
`/d/{device_id}`. Server configs and user secrets are Fernet-encrypted at rest with
|
|
`VPNAPP_SECRET_KEY`; only sanitized fields are shown in the admin list.
|
|
|
|
The reusable client-side request code is in `app/services/subscription.py`. It accepts
|
|
an injected secret store, emits the required User-Agent, performs one-time enrollment,
|
|
refreshes the device subscription asynchronously, and maps the response into the
|
|
separate VLESS/Amnezia/FreeTurn profile models. Android should connect its injected
|
|
store to Keystore-backed native storage before shipping credentials.
|