all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Azharul Haque <haque@azharul.com>
To: Shan Shaji <s.shaji@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [PATCH 0/2] android: register OpenID Connect callback activity for #4281
Date: Wed, 9 Sep 2026 18:37:16 -0400	[thread overview]
Message-ID: <CAFCWXbg=Miu+1FdCNxSqUTq3Dj+MyGQeAjqs_9cu-0SfWpu6Eg@mail.gmail.com> (raw)
In-Reply-To: <DL3VHUZCB1DI.39LV5RFBUBD9N@proxmox.com>

Hi Shan,

Dug into the server side before replying. PVE's /access/openid/auth-url
and /access/openid/login (src/PVE/API2/OpenId.pm in pve-access-control)
only take {realm, redirect-url} and {state, code, redirect-url}
respectively, no code_challenge/code_verifier field in the API for a
client to plug into.

The actual PKCE handling lives in proxmox-openid (proxmox-rs), pve-rs's
openid module is just a thin binding on top. PrivateAuthState::new()
generates a fresh PkceCodeChallenge/PkceCodeVerifier pair per login
attempt, authorize_url() writes that state to disk via store_auth_state()
and only sends the challenge to the IdP, and
verify_authorization_code_userinfo() reads the same on-disk state back
by the state value and supplies the verifier itself when redeeming the
code. So PVE is already doing PKCE against the actual IdP, entirely
server-side, the verifier never leaves the server.

I don't think that fully closes the question though. Walk the flow: the
IdP redirects state+code straight to the app's custom scheme, the app
relays state+code to /access/openid/login over HTTPS, and PVE does the
real token exchange using the verifier it kept for itself. That last hop
means anyone holding a valid state+code pair can call /login and walk
away with a PVE ticket, the endpoint never asks the caller to prove it's
the same client the redirect was meant for.

That splits into two separate threats:

1. A different app intercepts the redirect itself (duplicate
   custom-scheme registration). Auth Tabs and ASWebAuthenticationSession
   close this, agreed, the redirect only ever reaches the requesting
   app now.

2. state+code leaks after our app has them but before they reach PVE:
   a crash reporter snapshotting the callback URL, a logging proxy in
   between, or something on a compromised device reading it out from
   under the app, an Android accessibility-service/clipboard scraper,
   a rogue iOS keyboard extension or MDM profile, either way. Nothing
   defends this today, not the platform APIs (they only guard the
   redirect hop), not PVE's server-held verifier (it never checks
   who's calling /login), and it's identical exposure on both
   platforms since it's the same /login endpoint either way.

Real client-side PKCE would close that second gap: app generates a
verifier, sends the challenge at auth-url time, PVE forwards that
challenge to the IdP instead of its own, and /login requires the
verifier back before it'll redeem the code. But we can't add that from
the app alone, /login has no code_verifier field to send it to. It'd
need a pve-access-control change to accept a client-supplied
code_challenge/code_verifier and use that instead of (or in addition to)
the one it generates itself now.

PVE deployments aren't always kept behind a VPN or LAN, plenty get
exposed straight to the public internet by whoever's running them, so I'd
rather not leave that second gap open on the assumption it's unlikely.
Is a server-side change along those lines something you'd consider, or
is there a reason PVE holding the verifier itself instead of the client
is a deliberate design choice I'm missing?

Azharul

On Tue, Sep 1, 2026 at 5:30 AM Shan Shaji <s.shaji@proxmox.com> wrote:

> Hi Azharul,
>
> After reading through this [RFC 8252][0], it is a must to always use PKCE
> with native apps.
>
> > Public native app clients MUST implement the Proof Key for Code
> > Exchange (PKCE RFC7636 [1]) extension to OAuth, and authorization
> > servers MUST support PKCE for such clients, for the reasons detailed
> > in Section 8.1 [2].
>
> Section 8.1
>
> > The redirect URI options documented in Section 7 share the benefit
> > that only a native app on the same device or the app's own website
> > can receive the authorization code, which limits the attack surface.
> > However, code interception by a different native app running on the
> > same device may be possible.
>
> > A limitation of using private-use URI schemes for redirect URIs is
> > that multiple apps can typically register the same scheme, which
> > makes it indeterminate as to which app will receive the authorization
> > code.
>
> In Android, if there are multiple apps with the same URI schemes, it will
> show a disambiguation dialog [3], which will allow the user to select
> the app. AFAIU, this behaviour happens if the app is using custom tabs
> instead of the custom auth tabs which was released with chrome 132 [4][5].
>
> As for the custom auth tabs [7]:
>
> > Authentication strategies built on Custom Tabs offer a vast improvement
> > from prior solutions, but challenges still remain:
> >
> >    - Communication between the browser tab and the app relies on
> >      Activity intents, which can expose your app to potential
> >      interference to your intent
> >    - Using Activity intents to manage information transfer from the
> >      tab is less idiomatic than using Android APIs
> >
> > Auth Tab solves these problems. A dedicated callback adds a layer of
> > security and eliminates the need for Activity intents.
>
> On iOS, as the plugin is using (iOS 12+) ASWebAuthenticationSession [6]
> only the calling app's session will receive the authentication callback.
>
> > `ASWebAuthenticationSession` ensures that only the calling app's session
> > receives the authentication callback, even when more than one app
> > registers the same callback URL scheme.
>
> Unless I have missed something, the new APIs does protect from code
> interception attack. Although, IMHO to be on the safe side we should
> also support PKCE at the client side. WDYT?
>
> - [0] https://www.rfc-editor.org/info/rfc8252/#section-6
> - [1] https://www.rfc-editor.org/info/rfc7636/#section-1.1
> - [2] https://www.rfc-editor.org/info/rfc8252/#section-8.1
> - [3]
> https://developer.android.com/training/app-links/create-deeplinks#how-deep
> - [4] https://developer.chrome.com/docs/android/custom-tabs/guide-auth-tab
> - [5] https://developer.chrome.com/blog/android-auth-tab
> - [6]
> https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession
> - [7]
> https://developer.chrome.com/blog/android-auth-tab#auth_tab_versus_custom_tabs
>
> On Mon Aug 10, 2026 at 7:40 AM CEST, Azharul Haque wrote:
> > The native Flutter "Proxmox VE Companion" app never implemented OpenID
> > Connect / OAuth realm login (bug #4281[0]): selecting an OAuth realm
> > just showed username/password fields that could never work.
> >
> > Android requires an app to explicitly declare, in AndroidManifest.xml,
> > which activity should handle a given custom URL scheme. This series
> > registers flutter_web_auth_2's CallbackActivity for the
> > com.proxmox.app.openid:// redirect scheme used by the OAuth flow, and
> > fixes it up to match a later rename of that scheme.
> >
> > No iOS-side changes are needed: ASWebAuthenticationSession resolves
> > the custom-scheme redirect at runtime without a static declaration
> > equivalent to this manifest entry.
> >
> > Depends on the companion proxmox_dart_api_client and
> > proxmox_login_manager series, which implement the OIDC API calls and
> > login-form/OAuth-flow UI respectively.
> >
> > Verified end-to-end against a real PVE server with an Authentik OIDC
> > realm, on both Android and iOS.
> >
> > [0] https://bugzilla.proxmox.com/show_bug.cgi?id=4281
> >
> > Azharul Haque (2):
> >   fix #4281: android: register OpenID Connect callback activity
> >   fix #4281: android: match renamed OpenID callback scheme
> >
> >  android/app/src/main/AndroidManifest.xml     |  16 ++
> >  linux/flutter/generated_plugin_registrant.cc |   8 +
> >  linux/flutter/generated_plugins.cmake        |   2 +
> >  pubspec.lock                                 | 184 +++++++++++--------
> >  4 files changed, 138 insertions(+), 72 deletions(-)
>
>
>

  reply	other threads:[~2026-09-10  6:55 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-10  5:40 [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Azharul Haque
2026-08-10  5:40 ` [PATCH 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-10  5:40 ` [PATCH 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-10 13:59 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-10 14:47     ` [PATCH dart-api-client v2 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-20  9:17       ` Shan Shaji
2026-08-20 13:37         ` Azharul Haque
2026-08-20 14:39           ` Shan Shaji
2026-08-21  2:56             ` Azharul Haque
2026-08-21  8:00               ` Shan Shaji
2026-08-10 14:47     ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-20  9:48       ` Shan Shaji
2026-08-20 13:41         ` Azharul Haque
2026-08-10 14:47     ` [PATCH login-manager v2 1/3] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-20 12:31       ` Shan Shaji
2026-08-10 14:47     ` [PATCH login-manager v2 2/3] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-10 14:47     ` [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme Azharul Haque
2026-08-20 14:28       ` Shan Shaji
2026-08-10 14:47     ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-20 14:01       ` Shan Shaji
2026-08-10 14:47     ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-21  3:41     ` [PATCH v3 00/10] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-21  3:41       ` [PATCH dart-api-client v3 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-21  3:41       ` [PATCH dart-api-client v3 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-26 12:49         ` Shan Shaji
2026-08-21  3:41       ` [PATCH login-manager v3 1/5] fix #4281: deps: add flutter_web_auth_2 dependency Azharul Haque
2026-08-21  3:41       ` [PATCH login-manager v3 2/5] refactor: ui: factor out shared login tail into _finishLogin Azharul Haque
2026-08-26 13:41         ` Shan Shaji
2026-08-21  3:41       ` [PATCH login-manager v3 3/5] refactor: ui: split password form into its own widget Azharul Haque
2026-08-26  9:32         ` Shan Shaji
2026-08-21  3:41       ` [PATCH login-manager v3 4/5] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-21  3:41       ` [PATCH login-manager v3 5/5] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-26 10:24         ` Shan Shaji
2026-08-21  3:41       ` [PATCH flutter-frontend v3 1/3] chore: regenerate plugin registrant for flutter_web_auth_2 Azharul Haque
2026-08-21  3:41       ` [PATCH flutter-frontend v3 2/3] fix #4281: android: set taskAffinity="" on MainActivity Azharul Haque
2026-08-27 10:40         ` Shan Shaji
2026-08-21  3:41       ` [PATCH flutter-frontend v3 3/3] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-26 11:41       ` [PATCH v3 00/10] app: implement OpenID Connect (OAuth) realm login (#4281) Shan Shaji
2026-09-01  9:30 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
2026-09-09 22:37   ` Azharul Haque [this message]
2026-09-11 15:44     ` Shan Shaji

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAFCWXbg=Miu+1FdCNxSqUTq3Dj+MyGQeAjqs_9cu-0SfWpu6Eg@mail.gmail.com' \
    --to=haque@azharul.com \
    --cc=pve-devel@lists.proxmox.com \
    --cc=s.shaji@proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal