Platform-neutral successor to Discord Authenticator, forked from its v0.1.2
state with fresh history. Same vault, same UI, same live-message mechanism —
now behind one `Sink` that speaks three platforms.
Why each platform needs what it needs (this drove the whole settings model):
- Discord: an incoming-webhook URL. Webhooks can edit their own messages.
- Slack: a bot token (xoxb-, scope chat:write) + channel ID. Slack incoming
webhooks can post but never edit, and editing IS the live mechanism, so
chat.postMessage / chat.update via a bot is the only path.
- Telegram: a bot token + chat id. No webhook concept exists. The app detects
the chat id itself from the bot's pending updates ("send /start, then
Detect"), since users rarely know it.
- core: Provider enum; settings hold all three configs so switching back and
forth never re-asks for tokens; per-platform shape validation that names the
common mistakes (xoxp- user token, #name instead of channel ID); a
destination key that clears message ids on platform/channel change but not
on token rotation
- delivery: shared DeliveryError + pacing Governor; Discord client refactored
onto them; new Slack client (classifies Slack's ok:false-on-HTTP-200
errors) and Telegram client ("message is not modified" is success; chat
detection via getUpdates); provider-agnostic message builders — code in a
copyable span everywhere, rollover time as live as each platform allows
- http-api: setup accepts an optional provider; settings PUT/GET carry the
full model; /api/slack/whoami, /api/telegram/whoami, /api/telegram/chats
- webui: three-way optional picker on setup and in Settings → Delivery with
guided per-platform fields; "Check token" (Slack) and "Detect chat"
(Telegram); inline white mark via currentColor
- brand: white mark on the dark plate; blue stays with the Discord sibling
- docs/TEAMS.md: exploration for v0.2 — Teams connectors were disabled May
2026 and Workflows webhooks can't edit, so live tiles need a Bot Framework
bot; options for the inbound endpoint on a homelab and a recommended scope
Verified against mock Discord, Slack and Telegram servers speaking each
platform's real protocol shape: edits target the same message rather than
re-posting, deleted tiles are recovered, dead credentials are surfaced with a
next step, renames refresh without a rollover, and switching platform reposts
on the new one and leaves the old one alone. 138 tests.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
80 lines
4.0 KiB
XML
80 lines
4.0 KiB
XML
<?xml version="1.0"?>
|
|
<!--
|
|
Unraid Docker template for OpenMFA.
|
|
|
|
Drop this into /boot/config/plugins/dockerMan/templates-user/ on the Unraid
|
|
box, or add the repo as a template repository under Docker → Add Container.
|
|
|
|
Bridge networking is correct: OpenMFA makes outbound HTTPS calls to the chat
|
|
platform you choose and serves one HTTP port.
|
|
|
|
Web UI: http://<unraid-ip>:8720/
|
|
-->
|
|
<Container version="2">
|
|
<Name>OpenMFA</Name>
|
|
<Repository>gitea.milesward.dev/mward4/openmfa:latest</Repository>
|
|
<Registry>https://gitea.milesward.dev/mward4/-/packages/container/openmfa</Registry>
|
|
<Network>bridge</Network>
|
|
<MyIP/>
|
|
<Shell>sh</Shell>
|
|
<Privileged>false</Privileged>
|
|
<Support>https://gitea.milesward.dev/mward4/OpenMFA/issues</Support>
|
|
<Project>https://gitea.milesward.dev/mward4/OpenMFA</Project>
|
|
<Overview>
|
|
Self-hosted TOTP authenticator with live delivery to Discord, Slack or
|
|
Telegram.
|
|
|
|
Add accounts by uploading a QR code, scanning with the camera, pasting an
|
|
otpauth:// link, or importing a Google Authenticator export (one export QR
|
|
carries many accounts; you get a checklist, and duplicates are detected).
|
|
The web UI lists every account with its rotating code, the way Google or
|
|
Microsoft Authenticator does.
|
|
|
|
The distinctive part: each account keeps ONE message in your chosen
|
|
channel, rewritten in place every time the code rolls. Three accounts means
|
|
three live tiles that keep themselves current. Codes are posted as copyable
|
|
text; on Discord a rendered WebP tile is attached as well.
|
|
|
|
Pick the platform during first-run setup or later in Settings. Discord
|
|
needs only a webhook URL. Slack needs a bot token (its webhooks can't edit
|
|
messages). Telegram needs a bot token, and the app detects your chat ID for
|
|
you.
|
|
|
|
SECURITY NOTE: anyone who can read that channel can read these codes, which
|
|
is most of the point of a second factor. Use a private channel only you are
|
|
in, and treat the webhook URL or bot token as a secret. TOTP secrets are
|
|
encrypted at rest with XChaCha20-Poly1305.
|
|
</Overview>
|
|
<Category>Tools:Utilities Security:</Category>
|
|
<WebUI>http://[IP]:[PORT:8720]/</WebUI>
|
|
<TemplateURL/>
|
|
<Icon>https://gitea.milesward.dev/mward4/OpenMFA/raw/branch/main/docs/brand/icon-256.png</Icon>
|
|
<ExtraParams/>
|
|
<PostArgs/>
|
|
<CPUset/>
|
|
<DateInstalled/>
|
|
<DonateText/>
|
|
<DonateLink/>
|
|
<Requires>
|
|
One of: a Discord incoming-webhook URL; a Slack app with the chat:write
|
|
bot scope plus a channel ID; or a Telegram bot token from @BotFather. All
|
|
can be added after install from Settings → Delivery.
|
|
</Requires>
|
|
<Config Name="WebUI Port" Target="8720" Default="8720" Mode="tcp"
|
|
Description="Port for the web UI and API."
|
|
Type="Port" Display="always" Required="true" Mask="false">8720</Config>
|
|
<Config Name="Data" Target="/var/lib/openmfa"
|
|
Default="/mnt/user/appdata/openmfa"
|
|
Mode="rw"
|
|
Description="Vault, encryption key, admin account and settings. BACK THIS UP — losing it loses every stored TOTP secret."
|
|
Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/openmfa</Config>
|
|
<Config Name="Master key (optional)" Target="OPENMFA_MASTER_KEY" Default=""
|
|
Mode=""
|
|
Description="Base64 of 32 random bytes. Set this to keep the vault key OFF the appdata share, so a leaked backup of the share is useless on its own. Generate with: openssl rand -base64 32. Leave blank to auto-generate a key file inside the data folder instead. WARNING: if you set this later, existing secrets become unreadable — export them first."
|
|
Type="Variable" Display="advanced" Required="false" Mask="true"></Config>
|
|
<Config Name="Log filter" Target="OPENMFA_LOG" Default="info,openmfa=info"
|
|
Mode=""
|
|
Description="tracing-subscriber EnvFilter expression. Use info,openmfa=debug for more detail."
|
|
Type="Variable" Display="advanced" Required="false" Mask="false">info,openmfa=info</Config>
|
|
</Container>
|