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

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

> 
> 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. 

[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.

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




  reply	other threads:[~2026-02-09  8:34 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 [this message]
2026-02-10  8:24       ` Lukas Wagner
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=l6ne36qokbxj6djq2kjtcisewjntwytfxo5yolmp3o7gvnukss@jlzuwqko3ljz \
    --to=a.bied-charreton@proxmox.com \
    --cc=l.wagner@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