Google's "Transfer accounts -> Export accounts" produces an otpauth-migration:// link wrapping a base64 protobuf that holds many accounts at once, and splits large exports across several QR codes. v0.1 rejected these outright; now they import. The protobuf is decoded by hand rather than pulling prost: the schema is four messages that have not changed since 2020, and the wire format needed is two types (varint, length-delimited). Unknown fields are skipped properly, so a future Google addition degrades rather than corrupting the parse. Two details in this format silently produce plausible-but-wrong codes if taken backwards, so both are pinned by tests: the `secret` field is RAW BYTES, not base32, and the payload carries NO period field -- every Google export is 30-second TOTP. - core/migration.rs: protobuf decode, lenient base64 (all four alphabet and padding spellings), issuer reconciliation between the `name` prefix and the dedicated `issuer` field, and batch metadata surfaced as "Part 1 of 2" - vault: add_many() with duplicate detection, persisting once per batch rather than once per account; duplicates match on secret AND naming, so keeping one secret under two labels stays possible - http-api: preview returns a list flagged with what is already stored; import takes the link plus selected indices and re-parses server-side, so secrets never travel out to the browser and back. /api/qr/decode now returns every payload in an image, not just the first - webui: multi-account import checklist with select all/none, per-entry algorithm and digit chips, already-present entries pre-unticked, and skipped entries named with their reason (HOTP, MD5) instead of silently dropped Verified end to end against real QR images: upload -> decode -> preview -> import, with the resulting codes cross-checked against an independent RFC 6238 implementation, including an 8-digit account. 124 tests (was 101). Co-Authored-By: Claude Opus 4.8 <[email protected]>
77 lines
4.0 KiB
XML
77 lines
4.0 KiB
XML
<?xml version="1.0"?>
|
|
<!--
|
|
Unraid Docker template for Discord Authenticator.
|
|
|
|
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.
|
|
|
|
Unlike OpenPXE this needs no host networking — it only makes outbound HTTPS
|
|
calls to Discord and serves one HTTP port, so bridge mode is correct.
|
|
|
|
Web UI: http://<unraid-ip>:8710/
|
|
-->
|
|
<Container version="2">
|
|
<Name>Discord-Authenticator</Name>
|
|
<Repository>gitea.milesward.dev/mward4/discord-authenticator:latest</Repository>
|
|
<Registry>https://gitea.milesward.dev/mward4/-/packages/container/discord-authenticator</Registry>
|
|
<Network>bridge</Network>
|
|
<MyIP/>
|
|
<Shell>sh</Shell>
|
|
<Privileged>false</Privileged>
|
|
<Support>https://gitea.milesward.dev/mward4/Discord-Authenticator/issues</Support>
|
|
<Project>https://gitea.milesward.dev/mward4/Discord-Authenticator</Project>
|
|
<Overview>
|
|
Self-hosted TOTP authenticator with live Discord delivery.
|
|
|
|
Add accounts by uploading a QR code, scanning with the camera, or pasting
|
|
an otpauth:// link. Google Authenticator exports are supported too: one
|
|
export QR carries many accounts, and you get a checklist to pick which to
|
|
import, with duplicates detected automatically. 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 Discord
|
|
channel, delivered through an incoming webhook and rewritten in place
|
|
every time the code rolls. Three accounts means three live tiles that
|
|
keep themselves current. The code is posted both as a rendered WebP tile
|
|
and as copyable text, and the countdown uses Discord's own relative
|
|
timestamps so it ticks in the client without any extra traffic.
|
|
|
|
SECURITY NOTE: anyone who can read that Discord channel can read these
|
|
codes, which is most of the point of a second factor. Use a private
|
|
channel in a server only you are in, and treat the webhook URL as a
|
|
secret. TOTP secrets are encrypted at rest with XChaCha20-Poly1305.
|
|
</Overview>
|
|
<Category>Tools:Utilities Security:</Category>
|
|
<WebUI>http://[IP]:[PORT:8710]/</WebUI>
|
|
<TemplateURL/>
|
|
<Icon>https://gitea.milesward.dev/mward4/Discord-Authenticator/raw/branch/main/crates/webui/src/logo.svg</Icon>
|
|
<ExtraParams/>
|
|
<PostArgs/>
|
|
<CPUset/>
|
|
<DateInstalled/>
|
|
<DonateText/>
|
|
<DonateLink/>
|
|
<Requires>
|
|
A Discord incoming-webhook URL, from Server Settings → Integrations →
|
|
Webhooks → New Webhook → Copy Webhook URL. You can add it during first-run
|
|
setup or later in Settings.
|
|
</Requires>
|
|
<Config Name="WebUI Port" Target="8710" Default="8710" Mode="tcp"
|
|
Description="Port for the web UI and API."
|
|
Type="Port" Display="always" Required="true" Mask="false">8710</Config>
|
|
<Config Name="Data" Target="/var/lib/discord-authenticator"
|
|
Default="/mnt/user/appdata/discord-authenticator"
|
|
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/discord-authenticator</Config>
|
|
<Config Name="Master key (optional)" Target="DA_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="DA_LOG" Default="info,da=info"
|
|
Mode=""
|
|
Description="tracing-subscriber EnvFilter expression. Use info,da=debug for more detail."
|
|
Type="Variable" Display="advanced" Required="false" Mask="false">info,da=info</Config>
|
|
</Container>
|