public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Lukas Wagner" <l.wagner@proxmox.com>
To: "Arthur Bied-Charreton" <a.bied-charreton@proxmox.com>,
	"Lukas Wagner" <l.wagner@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [PATCH proxmox 1/5] notify: Introduce xoauth2 module
Date: Tue, 10 Feb 2026 09:24:34 +0100	[thread overview]
Message-ID: <DGB4Z1E88A3W.SLOMK7UGLGST@proxmox.com> (raw)
In-Reply-To: <l6ne36qokbxj6djq2kjtcisewjntwytfxo5yolmp3o7gvnukss@jlzuwqko3ljz>

On Mon Feb 9, 2026 at 9:34 AM CET, Arthur Bied-Charreton wrote:
> On Fri, Feb 06, 2026 at 04:00:42PM +0100, Lukas Wagner wrote:
>> Hi Arthur, thanks for the patch!
>> 
>> some notes inline.
>> 
>> On Wed Feb 4, 2026 at 5:13 PM CET, Arthur Bied-Charreton wrote:
>> > [...]
>> > diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
>> > index bc63e19d..52493ef7 100644
>> > --- a/proxmox-notify/Cargo.toml
>> > +++ b/proxmox-notify/Cargo.toml
>> > @@ -19,6 +19,10 @@ http = { workspace = true, optional = true }
>> >  lettre = { workspace = true, optional = true }
>> >  tracing.workspace = true
>> >  mail-parser = { workspace = true, optional = true }
>> > +oauth2 = { version = "5.0.0" }
>> 
>> This misses a 'default-features = false' here, otherwise it will still
>> pull in reqwest.
>> 
>> Also, the oauth2 and ureq deps should be optional, since they are only
>> required by the smtp endpoint, which is gated behind a (default)
>> feature.
>> 
>> As an example, see how the `lettre` crate is handled, that one is also
>> only needed for the smtp backend.
>> 
>> In general, make sure that something like
>>   `cargo build --no-default-features --features smtp`
>> works (also cross-check with other features).
>> 
>> > [...]
>> >  pub mod endpoints;
>> >  pub mod renderer;
>> >  pub mod schema;
>> > +pub mod xoauth2;
>> 
>> This module is only needed for the 'smtp' feature, so this should be
>> gated with a 
>> 
>> #[cfg(feature = 'smtp')]
>> 
>> you might need to adapt some of the optional deps in Cargo.toml, e.g.
>> due to your new module, 'smtp' now also requires 'proxmox-sys', etc.
>> 
> Will fix the feature/dependencies issues in v2, thanks for the examples!
>
>> >  
>> >  #[derive(Debug)]
>> >  pub enum Error {
>> > diff --git a/proxmox-notify/src/xoauth2.rs b/proxmox-notify/src/xoauth2.rs
>> > new file mode 100644
>> > index 00000000..66faabfa
>> > --- /dev/null
>> > +++ b/proxmox-notify/src/xoauth2.rs
>> > @@ -0,0 +1,146 @@
>> > +use oauth2::{
>> > +    basic::BasicClient, AccessToken, AuthUrl, ClientId, ClientSecret, RefreshToken, TokenResponse,
>> > +    TokenUrl,
>> > +};
>> > +
>> > +use crate::Error;
>> > +
>> > +/// `oauth2` dropped support for the `ureq` backend, this newtype circumvents this
>> > +/// by implementing the `SyncHttpClient` trait. This allows us to avoid pulling in
>> > +/// a different backend like `reqwest`.
>> 
>> Maybe clarify here that Debian patched out the ureq backend due to a
>> version mismatch of the packaged ureq crate and the one pulled in by the
>> oauth crate. Once this is resolved we might drop this custom client
>> implementation again. oauth2 uses an old version of ureq; we might be
>> able to contribute a PR to update it to a newer version.
>> 
>> You mentioned it in the commit message, but this would be useful here as
>> well.
>>
> I already opened a PR in oauth2 [0], no answer yet. Will add more
> details & a link to it to the doc comment.
>
> [0] https://github.com/ramosbugs/oauth2-rs/pull/338
>

quoting from your PR: 

  'when trying to use the ureq backend for oauth2, I unfortunately had to
  find out that while it is documented as a feature in the crate docs, it
  is no longer available. This seems to be due to the API changes
  introduced by ureq 3.x.'

This might be a bit confusing for the maintainer, you should probably
explain that you are using the Debian-packaged version of oauth2 which
patched out the support for the ureq backend due to the version
conflict.

>> 
>> In general, please add doc-strings for 'pub' functions. Also - I have
>> not checked the other patches, yet, but also consider using 'pub(crate)'
>> if these types are only needed inside this crate (which I think might be
>> the case for these).
>> 
> Will do, thanks :)
>> > +
>> > +/// Microsoft Identity Platform refresh tokens replace themselves
>> > +/// upon every use, however the old one does *not* get revoked.
>> > +///
>> > +/// This means that every access token request yields both an access
>> > +/// token AND a new refresh token, which should replace the old one.
>> > +/// The old one should then be discarded.
>> 
>> This actually raises a very interesting question. Consider the case of a
>> read-only cluster filesystem due to a missing quorum:
>> 
>>  - state file is read
>>  - one or more smtp endpoints refresh their tokens while sending or due
>>    to the periodic refresh
>>  - state file is written, but fails due to pmxcfs being R/O
>> 
>> I guess we would need to find some way here to not 'lose' the token.
>> 
>> Some ideas:
>>   - check *before* refreshing if we can actually write -> but this is
>>     still racy
>>   - when the write fails, save the token in some other location that is
>>     not in the pmxcfs and then later, when pmxcfs is writable again,
>>     update the statefile there - this could also be weird, in case
>>     *multiple* nodes tried to send notifications at roughly the same
>>     time - which one is the "correct" token then? Hopefully only one of
>>     these nodes should be able to refresh the token in the first place,
>>     but still something to keep in mind I think.
>> 
>> Maybe there is a better way though, these were just some initial ideas...
>> 
> I may have formulated this doc comment too conservatively: according to the
> Microsoft docs on token lifetime [0], while the refresh token does get 
> "replaced" at every use, the replaced token is not revoked, and each
> token has a static lifetime of 90 days. 
>
> This means that in this case, I don't think we really *care* which token is
> the correct one, since they are all still valid. We can afford to lose
> some and persist the first new token we get after the file system
> becomes writable again. 
>
> (All of this assuming that the Microsoft docs are correct, and the 
> implementation does not bear any surprises, could not test it myself yet.)
>
> If the FS stays read-only for 90+ days, we need user interaction again to 
> get a new refresh token anyway, since even the freshest of tokens would be
> expired by then. Now that I think about it, we probably need a way to
> request a new authorization from the user in the frontend. 

Ah, that's good to know - so that is hopefully not be an issue in the
real world (but maybe still warrant a note in the documentation). Also
let's hope that this does not become an issue with any other email
provider we might support in the future.

>
> [0] https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens#token-lifetime
>
>> > +///
>> > +/// https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens#token-lifetime
>> > +pub fn get_microsoft_token(
>> > +    client_id: ClientId,
>> > +    client_secret: ClientSecret,
>> > +    tenant_id: &str,
>> > +    refresh_token: RefreshToken,
>> > +) -> Result<TokenExchangeResult, Error> {
>> > +    let client = BasicClient::new(client_id)
>> > +        .set_client_secret(client_secret)
>> > +        .set_auth_uri(
>> > +            AuthUrl::new("https://login.microsoftonline.com/common/oauth2/v2.0/authorize".into())
>> > +                .map_err(|e| Error::Generic(format!("invalid auth URL: {e}")))?,
>> > +        )
>> > +        .set_token_uri(
>> > +            TokenUrl::new(format!(
>> > +                "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
>> > +            ))
>> > +            .map_err(|e| Error::Generic(format!("invalid token URL: {e}")))?,
>> > +        );
>> > +
>> > +    let token_result = client
>> > +        .exchange_refresh_token(&refresh_token)
>> > +        .request(&UreqSyncHttpClient::default())
>> > +        .map_err(|e| Error::Generic(format!("could not get access token: {e}")))?;
>> 
>> apologies for the annoying error handling in proxmox-notify, I've been
>> meaning to make it more convenient and rethink the error variants a bit,
>> but I simply have not gotten to it yet :)
>> 
>
> I may not have helped by using Error::Generic everywhere ^^'
> Maybe we could add an AuthenticationError variant, and pull in thiserror
> to streamline the error handdling a little? Might be something for a
> follow-up series though.
>

Definitely something for a future patch series. While this will
*technically* be a breaking change for this crate, none of the existing
callers do anything with the returned errors except logging it anyway.

I have some other ideas and changes planned for proxmox-notify anyway
(like pulling out the static context, as explained my integration
testing proposal on pdm-devel [0]), so the error refactoring might
be something that I would include there.

[0] https://lore.proxmox.com/all/20260129134418.307552-1-l.wagner@proxmox.com/T/#u

>> > +
>> > +    Ok(TokenExchangeResult {
>> > +        access_token: token_result.access_token().clone(),
>> > +        refresh_token: token_result.refresh_token().cloned(),
>> > +    })
>> > +}





  reply	other threads:[~2026-02-10  8:23 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-04 16:13 [RFC cluster/docs/manager/proxmox{,-perl-rs,-widget-toolkit} 00/15] fix #7238: Add XOAUTH2 authentication support for SMTP notification targets Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox 1/5] notify: Introduce xoauth2 module Arthur Bied-Charreton
2026-02-06 15:00   ` Lukas Wagner
2026-02-09  8:34     ` Arthur Bied-Charreton
2026-02-10  8:24       ` Lukas Wagner [this message]
2026-02-10 10:23         ` Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox 2/5] notify: Add state file handling Arthur Bied-Charreton
2026-02-10 15:51   ` Lukas Wagner
2026-02-04 16:13 ` [PATCH proxmox 3/5] notify: Update Endpoint trait and Bus to use State Arthur Bied-Charreton
2026-02-10 15:52   ` Lukas Wagner
2026-02-12  8:26     ` Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox 4/5] notify: smtp: add OAuth2/XOAUTH2 authentication support Arthur Bied-Charreton
2026-02-10 15:52   ` Lukas Wagner
2026-02-11 13:00     ` Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox 5/5] notify: Add test for State Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox-perl-rs 1/1] notify: update bindings with new OAuth2 parameters Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox-widget-toolkit 1/2] utils: Add OAuth2 flow handlers Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH proxmox-widget-toolkit 2/2] notifications: Add opt-in OAuth2 support for SMTP targets Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-manager 1/5] notifications: Add OAuth2 parameters to schema and add/update endpoints Arthur Bied-Charreton
2026-02-11  8:55   ` Lukas Wagner
2026-02-11 12:47     ` Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-manager 2/5] notifications: Add refresh-targets endpoint Arthur Bied-Charreton
2026-02-11  9:49   ` Lukas Wagner
2026-02-11 12:44     ` Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-manager 3/5] notifications: Trigger notification target refresh in pveupdate Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-manager 4/5] notifications: Handle OAuth2 callback in login handler Arthur Bied-Charreton
2026-02-11  9:00   ` Lukas Wagner
2026-02-04 16:13 ` [PATCH pve-manager 5/5] notifications: Opt into OAuth2 authentication Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-cluster 1/1] notifications: Add refresh_targets subroutine to PVE::Notify Arthur Bied-Charreton
2026-02-04 16:13 ` [PATCH pve-docs 1/1] notifications: Add section about OAuth2 to SMTP targets docs Arthur Bied-Charreton
2026-02-11 10:06   ` Lukas Wagner
2026-02-11 13:15     ` Arthur Bied-Charreton
2026-02-13 16:06 ` superseded: [RFC cluster/docs/manager/proxmox{,-perl-rs,-widget-toolkit} 00/15] fix #7238: Add XOAUTH2 authentication support for SMTP notification targets Arthur Bied-Charreton

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=DGB4Z1E88A3W.SLOMK7UGLGST@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=a.bied-charreton@proxmox.com \
    --cc=pve-devel@lists.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal