From 9ca8fce14e2ba4b08923f7b5e8433199e10ccb54 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Mon, 26 Oct 2026 20:43:33 +0100 Subject: [PATCH] fix: a VPN user downloads the own OpenVPN client profile through /account, with the current password and code. --- README.md | 1 + debian-package/debian/changelog | 9 +++++++++ debian-package/src/usr/lib/vpn-router/web | 10 +++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2c62041..d5276a2 100644 --- a/README.md +++ b/README.md @@ -1255,6 +1255,7 @@ router manages them, their display name and description included. A failure is o | `POST /account/passkeys` | `{"current_password", "current_code"}` | `{"id", "options"}`: a pending registration, valid 2 minutes, and the options of `navigator.credentials.create` in their JSON form | | `PUT /account/passkeys` | `{"id", "credential", "label"?}`, `credential` the browser's answer as its `toJSON()` gives it; `label` at most 64 characters, `Passkey` without one | the user | | `DELETE /account/passkeys/` | | the user | +| `POST /account/profile` | `{"current_password", "current_code"}` | `{"name", "profile"}`, the user's OpenVPN client profile | | `POST /account/apple-profile` | `{"current_password", "current_code", "password"}` | `{"name", "profile"}`, the user's Apple configuration profile of IKEv2 road-warrior access (see Apple VPN profile) | `current_code` is needed only for a user enrolled for TOTP; removing a passkey needs neither. A wrong current password or code gets diff --git a/debian-package/debian/changelog b/debian-package/debian/changelog index 46656c0..557ae4f 100644 --- a/debian-package/debian/changelog +++ b/debian-package/debian/changelog @@ -1,3 +1,12 @@ +vpn-router (0.32.1-1) unstable; urgency=medium + + fix: a VPN user downloads the own OpenVPN client profile through /account, with the current password and code. + + * POST /account/profile answers the user's OpenVPN client profile; it + needs the current password and, when enrolled, a code. + + -- Slawomir Koszewski Tue, 27 Oct 2026 16:00:00 +0100 + vpn-router (0.32.0-1) unstable; urgency=medium feature: the web service gives Apple VPN profiles that set up IKEv2 road-warrior access on macOS, iOS and iPadOS. diff --git a/debian-package/src/usr/lib/vpn-router/web b/debian-package/src/usr/lib/vpn-router/web index e3b31d1..152428f 100644 --- a/debian-package/src/usr/lib/vpn-router/web +++ b/debian-package/src/usr/lib/vpn-router/web @@ -1048,12 +1048,14 @@ def account(name, method, path, body): PUT /account/passkeys store the passkey: {"id", "credential", "label"?}, credential the browser's answer DELETE /account/passkeys/ remove a passkey + POST /account/profile the user's OpenVPN client profile: {"current_password", "current_code"?}; {"name", + "profile"} POST /account/apple-profile the user's Apple configuration profile of IKEv2 road-warrior access: {"current_password", "current_code"?, "password"}, password encrypting the user's key in it; {"name", "profile"} - A change but the removal of a passkey, and the Apple profile, need the current password and, for an enrolled user, a + A change but the removal of a passkey, and the profiles, need the current password and, for an enrolled user, a current code; a wrong one is 403 and counts as a failed login. A password change revokes the user's refresh tokens. """ @@ -1064,13 +1066,13 @@ def account(name, method, path, body): removing = method == 'DELETE' and path.startswith('/account/passkeys/') and len(path.strip('/').split('/')) == 3 if not removing and route not in (('GET', '/account'), ('PUT', '/account/password'), ('POST', '/account/otp'), ('PUT', '/account/otp'), ('POST', '/account/passkeys'), ('PUT', '/account/passkeys'), - ('POST', '/account/apple-profile')): + ('POST', '/account/profile'), ('POST', '/account/apple-profile')): return 404, {'error': f'no route {method} {path}'} try: if route == ('PUT', '/account/password'): vpnrouter_users.check_password(body.get('password')) if route in (('PUT', '/account/password'), ('PUT', '/account/otp'), ('POST', '/account/passkeys'), - ('POST', '/account/apple-profile')): + ('POST', '/account/profile'), ('POST', '/account/apple-profile')): vpnrouter_users.login(name, body.get('current_password'), body.get('current_code')) with vpnrouter_users.session() as connection: if route == ('GET', '/account'): @@ -1080,6 +1082,8 @@ def account(name, method, path, body): document = vpnrouter_users.passwd(connection, name, body['password']) elif route == ('POST', '/account/otp'): document = vpnrouter_users.otp_new(connection, name) + elif route == ('POST', '/account/profile'): + document = vpnrouter_users.profile(connection, name) elif route == ('POST', '/account/apple-profile'): document = vpnrouter_users.apple_profile(connection, name, body.get('password')) elif route == ('POST', '/account/passkeys'):