v2.4.8
v2.4.8 — Bug fix: SMTP port 465 + OIDC userinfo claim merge
Fixes #29 reported by @e64462 on an Authelia + Gmail SMTP setup.
What broke
SMTP test email over port 465 silently failed, no email sent and no useful log. System.Net.Mail.SmtpClient.EnableSsl=true triggers STARTTLS rather than Implicit TLS — port 465 expects an SSL ClientHello before any SMTP commands, so the plaintext EHLO hits a server waiting for crypto and the connection drops.
OIDC sign-in via Authelia rejected users as "not part of allowed groups" even when groups was in the scope list. The plugin only read claims from the id_token JWT, but Authelia (default config), Keycloak (most realms), and Authentik emit groups/email/email_verified only at the /userinfo endpoint. claims.Groups was therefore empty regardless of requested scopes, and the AllowedGroups allowlist rejected every user.
Fix
SMTP: migrated EmailOtpService from System.Net.Mail.SmtpClient to MailKit. New PickSocketOptions helper maps port → SecureSocketOptions:
- 465 →
SslOnConnect(Implicit TLS / SMTPS) - 587 →
StartTls - other ports + UseSsl=true →
StartTlsWhenAvailable - UseSsl=false →
None(opt-in plaintext for local dev)
OIDC: OidcService.CompleteAsync now fetches /userinfo with the access_token after id_token verification and merges any groups, roles, email, email_verified, and username claims into the bundle before the AllowedGroups check and ResolveUserAsync. New internal helper ExtractClaimsFromUserInfo handles JSON-array AND comma-separated-string group formats, plus parses email_verified as bool or string. Userinfo fetch is best-effort — failure falls through to id_token-only behaviour with debug log.
Implementation
- Adds MailKit 4.16.0 + transitive MimeKit + BouncyCastle.Cryptography to the fat package (~7.2MB total).
build.sh,build.ps1,meta.jsonAssemblies[] all updated. - 11 new regression tests:
SmtpSocketOptionsTests(5 cases) pin port→SocketOptions mapping including the load-bearing port-465 → SslOnConnect caseOidcUserInfoExtractionTests(6 cases) cover groups-as-JSON-array, groups-as-CSV, groups+roles merge, missing claims, non-object root, empty/whitespace filtering
Validated end-to-end
- Gmail
smtp.gmail.com:465(the canonical Implicit-TLS SMTPS server) sends test email successfully. Pre-v2.4.8 this is the exact failure mode reported. - Authelia 4.39 OIDC sign-in with
AllowedGroups=jellyfin-userssucceeds. The old "Account is not in an allowed group" error is gone; the userinfo merge correctly retrieves the group. Same IdP family the reporter uses.
166/166 tests pass. Build clean with TreatWarningsAsErrors=true.
Upgrade
In-place upgrade. No config changes. Users on Authelia / Keycloak / Authentik / any IdP that emits groups at /userinfo will find their OIDC allowlists actually work. Users on Gmail / Outlook365 / Mailgun port-465 SMTP will find their test emails actually send.
Verify the release
cosign verify-blob \
--certificate Jellyfin.Plugin.TwoFactorAuthv2.4.8.0.zip.pem \
--signature Jellyfin.Plugin.TwoFactorAuthv2.4.8.0.zip.sig \
--certificate-identity-regexp 'https://github.com/ZL154/JellyfinSecurity/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
Jellyfin.Plugin.TwoFactorAuthv2.4.8.0.zip
SLSA build provenance: Jellyfin.Plugin.TwoFactorAuthv2.4.8.0.zip.intoto.jsonl — verify with gh attestation verify --owner ZL154 Jellyfin.Plugin.TwoFactorAuthv2.4.8.0.zip.