UserNotification event handler - email body always empty in 3.13

Hello,

thank for this great application !

The UserNotification event handler sends emails correctly (subject is received), but the email body is always empty, even with plain static text like “Hello” and no variables.

Environment:

  • privacyIDEA version: 3.13

  • OS: Ubuntu 24.04 LTS

  • Web server: Apache2

  • Database: MySQL

Steps to reproduce:

  1. Create a new Event Handler with:

    • Event: token_init

    • Handlermodule: UserNotification

    • Action: sendmail

    • Position: post

  2. Set body to any text, for example: “Hello” (no variables)

  3. Set subject to: New token enrolled for {username}

  4. Enroll a TOTP token for a user

Observed behavior:

  • Email is received

  • Subject is correct and variables are resolved

  • Body is completely empty

Verified:

  • The body value is correctly saved in the database (eventhandleroption table, Key='body', Value='Hello')

  • The subject works fine with and without variables

  • The same behavior occurs with mimetype=plain and mimetype=html

  • Log shows Mail sent: {} with no errors

Is this a known bug in 3.13? Any workaround?

thank advance

regards

Vincent

Can you please paste the relevant debug log output.

How does your mailserver config in privacyIDEA look like?
What mailserver are you using?

Do you have access to the logs of the mailserver?
THanks a lot
Cornelius

Further information.

Mail send: {} means that the mail server accepted the email.

Did you try a longer Body and Plain-Text?

Hello, thank for your reply.

I’m using smtp on port 25 without authentication. The mail flow work well as I recevie an email.
I use Exchange SE onprem as mailserver. Find another informations

  • privacyIDEA version: 3.13

  • Event: token_init

  • Handler: UserNotification

  • Action: sendmail

  • Position: post

  • Body configured: “Salut” (static text, no variables)

  • Subject configured: “Test {username}”

  • Result: Email received, subject correct with variable resolved, body completely empty

  • Body verified in database: SELECT Key, Value FROM eventhandleroption WHERE Key = ‘body’; → returns “Salut”

  • init_details in logs: {} (empty)

  • Logs show: Mail sent successfully with no errors

Is it possible that the body is sent but Exchange strips it? The init_details are empty — could this affect the body rendering?

And the log of /var/log/privacyidea/privacyidea.log :
[2026-04-20 15:49:47,562][47243][136216374662848][DEBUG][privacyidea.lib.audit:68] Entering getAudit with arguments HIDDEN and keywords HIDDEN (called from event.py:112)
[2026-04-20 15:49:47,612][47243][136216374662848][DEBUG][privacyidea.lib.audit:68] Exiting getAudit with result <privacyidea.lib.auditmodules.sqlaudit.Audit object at 0x7be34067a300> (called from event.py:112)
[2026-04-20 15:49:47,614][47243][136216374662848][DEBUG][privacyidea.lib.auditmodules.base:169] Exiting log with result None (called from event.py:119)
[2026-04-20 15:49:47,616][47243][136216374662848][DEBUG][privacyidea.lib.eventhandler.usernotification:327] Executing event for action sendmail, user myuser.admin@local, logged_in_user {‘username’: ‘admin’, ‘realm’: ‘’, ‘role’: ‘admin’}
[2026-04-20 15:49:47,617][47243][136216374662848][DEBUG][privacyidea.lib.token:529] Entering get_tokens with arguments () and keywords {‘serial’: ‘TOTP000242CD’} (called from usernotification.py:430)
[2026-04-20 15:49:47,629][47243][136216374662848][DEBUG][privacyidea.lib.token:529] Exiting get_tokens with result [<<class ‘privacyidea.lib.tokens.totptoken.TotpTokenClass’> {“‘token’”: ‘<<class ‘privacyidea.models.token.Token’> {“‘_sa_instance_state’”: ‘<sqlalchemy.orm.state.InstanceState object at 0x7be340484dd0>’, “‘user_pin_iv’”: “‘’”, “‘key_iv’”: “‘d945e87ea51a97033cdfa4c0a4e02e74’”, “‘sync_window’”: ‘1000’, “‘so_pin’”: “‘’”, “‘maxfail’”: ‘10’, “‘rollout_state’”: “‘’”, “‘so_pin_iv’”: “‘’”, “‘active’”: ‘True’, “‘id’”: ‘12’, “‘pin_seed’”: “‘’”, “‘revoked’”: ‘False’, “‘description’”: “‘’”, “‘otplen’”: ‘6’, “‘locked’”: ‘False’, “‘serial’”: “‘TOTP000242CD’”, “‘pin_hash’”: “‘’”, “‘failcount’”: ‘0’, “‘tokentype’”: “‘totp’”, “‘key_enc’”: “‘351113580689c1ae5d1b82eeaae68f6057bc21e07e839e99a8c44abd0567287b83a66c69741c0be791bd157148cc9907’”, “‘count’”: ‘0’, “‘user_pin’”: “‘’”, “‘count_window’”: ‘10’, “‘realm_list’”: ‘[<TokenRealm 12>]’}>’, “‘type’”: “‘totp’”, “‘init_details’”: ‘{}’, “‘auth_details’”: ‘{}’, “‘hKeyRequired’”: ‘True’}>] (called from usernotification.py:430)
[2026-04-20 15:49:47,643][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:247] Entering get_smtpserver with arguments (‘SRVEXCHSE’,) and keywords {} (called from smtpserver.py:214)
[2026-04-20 15:49:47,646][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:247] Exiting get_smtpserver with result <privacyidea.lib.smtpserver.SMTPServer object at 0x7be34067b620> (called from smtpserver.py:214)
[2026-04-20 15:49:47,661][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:123] submitting message to
[2026-04-20 15:49:47,661][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:124] Saying EHLO to mailserver smtp://192.168.10.10
[2026-04-20 15:49:47,663][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:126] mailserver responded with (250, b’SRVEXCHSE.mondomain.com Hello [192.168.10.11]\nSIZE 37748736\nPIPELINING\nDSN\nENHANCEDSTATUSCODES\nSTARTTLS\n8BITMIME\nBINARYMIME\nCHUNKING\nSMTPUTF8’)
[2026-04-20 15:49:47,777][47243][136216374662848][INFO][privacyidea.lib.smtpserver:168] Mail sent: {}
[2026-04-20 15:49:47,777][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:180] I am done sending your email.
[2026-04-20 15:49:47,778][47243][136216374662848][DEBUG][privacyidea.lib.smtpserver:199] Exiting send_email_identifier with result True (called from usernotification.py:522)
[2026-04-20 15:49:47,778][47243][136216374662848][INFO][privacyidea.lib.eventhandler.usernotification:532] Sent a notification email to user {‘email’: [‘vincent.emery@mondomaine.com’]}
[2026-04-20 15:49:47,778][47243][136216374662848][DEBUG][privacyidea.lib.auditmodules.base:169] Entering log with arguments (<privacyidea.lib.auditmodules.sqlaudit.Audit object at 0x7be34067a300>, {‘success’: True}) and keywords {} (called from event.py:128)
[2026-04-20 15:49:47,778][47243][136216374662848][DEBUG][privacyidea.lib.auditmodules.base:169] Exiting log with result None (called from event.py:128)

Many thank

Best regards

Vincent Emery

Please find Exchange receive connector log. I found nothing special
2026-04-22T06:04:48.805Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,194,192.168.10.10:2525,192.168.10.10:23339,<,RSET,
2026-04-22T06:04:48.805Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,195,192.168.10.10:2525,192.168.10.10:23339,>,250 2.0.0 Resetting,
2026-04-22T06:04:48.805Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,196,192.168.10.10:2525,192.168.10.10:23339,<,XPROXYFROM SID=08DE8A5F579C1E2A IP=192.168.10.15 PORT=40438 DOMAIN=srvpidea01.mondomain.com SEQNUM=1 PERMS=1789 AUTHSRC=Organization,
2026-04-22T06:04:48.805Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,197,192.168.10.10:2525,192.168.10.10:23339,,SMTPSubmit SMTPSubmitForMLS SMTPAcceptAnyRecipient SMTPAcceptAuthenticationFlag SMTPAcceptAnySender SMTPAcceptAuthoritativeDomainSender BypassAntiSpam BypassMessageSizeLimit SMTPSendEXCH50 SMTPAcceptEXCH50 AcceptRoutingHeaders AcceptForestHeaders AcceptOrganizationHeaders SendRoutingHeaders SendForestHeaders SendOrganizationHeaders SendAs SMTPSendXShadow SMTPAcceptXShadow SMTPAcceptXProxyFrom SMTPAcceptXSessionParams SMTPAcceptXMessageContextADRecipientCache SMTPAcceptXMessageContextExtendedProperties SMTPAcceptXMessageContextFastIndex SMTPAcceptXAttr SMTPAcceptXSysProbe,Set Session Permissions
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,198,192.168.10.10:2525,192.168.10.10:23339,>,250 XProxyFrom accepted,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,199,192.168.10.10:2525,192.168.10.10:23339,<,MAIL FROM:privacyidea@mondomain.com SIZE=0 XMESSAGEVALUE=MediumHigh,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,200,192.168.10.10:2525,192.168.10.10:23339,
,08DE8A5F70D8FF87;2026-04-22T06:01:59.455Z;12,receiving message
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,201,192.168.10.10:2525,192.168.10.10:23339,<,RCPT TO:,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,202,192.168.10.10:2525,192.168.10.10:23339,>,250 2.1.0 Sender OK,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,203,192.168.10.10:2525,192.168.10.10:23339,>,250 2.1.5 Recipient OK,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,204,192.168.10.10:2525,192.168.10.10:23339,<,DATA,
2026-04-22T06:04:48.806Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,205,192.168.10.10:2525,192.168.10.10:23339,>,354 Start mail input; end with .,
2026-04-22T06:04:48.807Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,206,192.168.10.10:2525,192.168.10.10:23339,*,receiving message with InternetMessageId
2026-04-22T06:04:48.910Z,SRVEXCHSE\Default SRVEXCHSE,08DE8A5F70D8FF87,207,192.168.10.10:2525,192.168.10.10:23339,>,“250 2.6.0 [InternalId=220920232804399] 1872 bytes in 0.103, 17,744 KB/sec Queued mail for delivery”,

This log is similar as any other email going through my Exchange server
Thank

I found something with TCPDUMP directly on the server.
The body seems to be correctly sent to Exchange, encoded with Base64. The issu is coming from my server I think

We have an issue here:

So it could come from the Exchange, since it does not like \n only.
But Exchange may be difficult to educate.
We should send \n\r again.

Hey,
Changing msg = msg.as_bytes() by msg = msg.as_string() at line 138 of smtpserver.py fixe the issue.

Thank you very much, Cornelius. I really appreciate the time you took to help me.
Your app works great, and I was able to install it very easily. I mainly use it for the Windows provider to enable two-factor authentication when logging into Windows VMs.
We’ve decided to use PrivacyIdea to replace Thales Safenet Gemalto solution.
Have a greate day
Vincent

1 Like

I usually would not recommand to change the code, but great, that you found a way that works for you. THanks a lot for the feedback.

I very much think, that we will have this fixed in 3.13.1.
When you run the update, your modified file will be overwritten, but you will get a new file that should also work.