Passwordless WordPress, no SaaS

One link. Six characters. You're in.

Drop-in passwordless sign-in for WordPress. Click on your laptop, type on your phone. Same token, atomically consumed, gone in ten minutes.

ettic.nl / wp-login.php
Ettic

Sign in to Ettic

you@example.com
Send Link

Sign in with password

Tested on the WordPress you already run, no SaaS or SDK in the loop

WordPress 6.4+
PHP 8.0+
MySQL / MariaDB
Multisite
REST + Block Editor
Apache & nginx
WP-Cron
GPL-2.0

One link, one code, both ways in.

Every sign-in email contains both a clickable magic link and a typeable 6-character code. Same 256-bit token, atomically consumed.

Click on your laptop. Type on your phone. The first one through wins, every other path returns the same uniform error envelope.

Default TTL is 10 minutes. Default code attempts is 5. Adjustable in WP admin or by filter.

your-site.com / wp-login.php

Hardened where it counts.

Plaintext only ever lives in the URL or email. Only the HMAC-SHA-256 hash hits the database. Comparisons run with hash_equals().

A single SQL UPDATE serialises concurrent callers. Race-safe. The deferred mailer closes a measured 61ms timing oracle.

Unknown email, throttled, wrong code, expired link: all four take the same time to fail. 50 to 150ms jitter masks the rest.

audit log / sign-in
14:02:31 issue selector=a8k2q7 ttl=600s
14:02:31 store verifier=hmac_sha256("...256bit...")
14:02:32 mail.queue to="nol@ettic.nl" deferred=true
14:02:48 consume.try selector=a8k2q7
14:02:48 db.update rows=1 atomic=true
14:02:48 consume.ok jitter=87ms user=12
14:03:01 consume.try selector=a8k2q7
14:03:01 consume.miss reason=consumed jitter=112ms
14:03:14 throttle.check ip="203.0.113.4" ok=false
14:03:14 throttle.deny window=60s jitter=94ms

Scope, not feature creep

What MagicAuth doesn't try to do

SMS sign-in

Email is the lowest-overhead authenticator your users already have. SMS adds a paid third party, a phone-number PII surface, and a SIM-swap attack vector. We pass.

MagicAuth
scope is the feature
Phone OTP

Same problem as SMS, plus a per-message cost. If your auth needs span more than email, you're shopping for an identity layer, not a plugin.

QR codes

Pretty UX, but it's a magic link with extra steps. Use the link.

Third-party SSO

Google, Apple, GitHub: every one of those is its own consent screen, key rotation, and trust problem. WordPress already has a plugin for that.

User registration

MagicAuth signs people in. If they don't have an account yet, your existing onboarding handles that.

reCAPTCHA

We rate-limit by IP, by email, and by a global per-site bucket. That's the bot answer that doesn't ship Google to your users.

Pretty dashboards on someone else's cloud

Tokens, hashes, and audit logs live in your wp_ tables. No data leaves the box.

Charging per active user

GPL-2.0. No usage tier. No "starter" with a banner ad. Install it on your client's site, mark it up however you bill. We're not in the loop.

Free as in freedom
github.com/EtticDevelopment/magicauth

For developers. Hooks, not workarounds.

Thirty public actions and filters cover every important moment: token issued, token consumed, email subject, redirect target, client IP source, login heading, cookie set.

Override email templates by dropping a copy into your-theme/magicauth/. Send via your own transport by short-circuiting magicauth_email_send.

Programmatic API: TokenManager::issue(), ::consume(), ::revoke(). Same code paths your wp-login screen uses.

themes / your-theme / functions.php
// Issue a one-shot magic link from PHP
use MagicAuth\Auth\TokenManager;

$result = TokenManager::issue(
    $user_id,
    'user@example.com'
);

// $result['link_url']
// $result['code_plaintext']
// $result['selector']
// $result['expires_at']

// Route every email through your own transport
add_filter('magicauth_email_send', function ($null, $args) {
    return $mailer->send($args);
}, 10, 2);

Self-hosted, by default

Your servers, your data.

MagicAuth ships as a single WordPress plugin. Tokens, hashes, audit logs, brand assets: all of it lives in your wp_ tables, on your servers, under your backup policy.

  • WP-Cron native Token cleanup runs on the schedule WordPress already runs.
  • Multisite-safe Tables are per-site by default; network-wide via constant.
  • No outbound calls Email goes through wp_mail or your filter. That's it.
  • GDPR-clean Native erasure and export hooks; no third-party processor.
Read the install guide
Tested on WP Engine Kinsta SiteGround Your shared host
wp-config.php php-fpm
// In wp-config.php

define('MAGICAUTH_PRESET', 'production');
define('MAGICAUTH_TTL_LINK', 600);
define('MAGICAUTH_TTL_CODE', 600);
define('MAGICAUTH_RATE_PER_IP', 5);
define('MAGICAUTH_RATE_PER_EMAIL', 3);

// Optional: override the default brand
define('MAGICAUTH_BRAND_COLOR', '#0F5CFA');

// Optional: trust a specific reverse-proxy header
define('MAGICAUTH_TRUSTED_IP_HEADER', 'CF-Connecting-IP');

/* Token, audit-log and rate-limit tables
 * are created on activation in:
 *   wp_magicauth_tokens
 *   wp_magicauth_log
 *   wp_magicauth_throttle
 */

Three ways to install. All free.

GPL-2.0. No accounts, no usage tier, no paid pro version.

All paths give you the same plugin. Pick the one that fits your stack.

WordPress.org

For sites managed through wp-admin. Auto-updates handled by WordPress core.

Free/forever
Install from wp-admin
  • Plugins > Add new > "MagicAuth"
  • Auto-updates from core
  • Signed by WordPress.org
  • Plugin Check verified
GitHub

For contributors, security researchers, and unreleased branches.

Free/git clone
Clone the repo
  • EtticDevelopment/magicauth
  • GPL-2.0-or-later
  • CI runs PHPCS + Plugin Check
  • Security policy at /SECURITY.md

Install in two minutes.

Activate the plugin, set a brand colour, and your wp-login.php becomes a passwordless sign-in screen. No SaaS, no SDK, no SMS provider.

The whole thing is one PHP plugin, around 3000 lines, sitting in your wp-content/plugins/. It reads like a piece of WordPress, because it is one.

If you can install a plugin, you've already finished the hard part.

From plugin upload to first sign-in
Two minutes

Upload, activate, set a brand colour. Your wp-login is now passwordless.

wp plugin install magicauth --activate
10x lower auth-related support load.
No more "I forgot my password"

If your users have email, they have access. The password tickets stop arriving.