Originally published byDev.to
Magic link authentication is becoming one of the most popular ways to verify users without passwords. Instead of asking users to remember passwords, we simply send them a secure login link through email.
What is a Magic Link?
A magic link is a unique URL sent to a user’s email. When the user clicks the link:
- The server verifies the token
- The user becomes authenticated
- No password is required Example:
https://yourapp.com/verify?token=abc123
This approach is:
- More secure than passwords in many cases
- Easier for users
- Great for modern SaaS apps
Basic setup for magic link authentication system
Firstly, you need to install auth-verify:
npm install auth-verify
const AuthVerify = require('auth-verify')
const auth = new AuthVerify({
mlSecret: 'super_secret_key',
mlExpiry: '5m',
appUrl: 'http://localhost:3000',
storeTokens: 'memory' // in production 'redis'
})
You need also web server like built on Express.js
Configure Magic Link Sender
Before sending links, you must set up your email transport.
Gmail Example
auth.magic.sender({
service: 'gmail',
sender: '[email protected]',
pass: 'your_gmail_app_password'
});
Custom SMTP Example
auth.magic.sender({
host: 'smtp.mailgun.org',
port: 587,
secure: false,
sender: '[email protected]',
pass: 'your_smtp_password'
});
API services Example
auth.magic.sender({
service: 'api',
apiService: 'resend', // 'mailgun', 'sendgrid'
sender: '[email protected]',
apiKey: 'YOUR_API_KEY'
})
For "mailgun" you should also add also your domain
domain: "your-domain.com"
Send Magic Link
Send a secure, expiring link to the user’s email:
auth.magic.send('[email protected]', {
subject: 'Your Secure Login Link ✨',
html: `<p>Click below to sign in:</p>
<a href="{{link}}">Login Now</a>`
});
The
{{link}}placeholder will automatically be replaced with the generated magic link.
Verify Magic Link
Typically used in your backend /auth/verify route:
app.get('/auth/verify', async (req, res) => {
const { token } = req.query;
try {
const user = await auth.magic.verify(token);
res.json({ success: true, user });
} catch (err) {
res.status(400).json({ success: false, message: err.message });
}
});
Full working example
const express = require('express');
const bodyParser = require('body-parser');
const AuthVerify = require('auth-verify');
const app = express();
app.use(bodyParser.json());
const auth = new AuthVerify({
mlSecret: 'supersecretkey',
appUrl: 'http://localhost:3000',
storeTokens: 'memory'
});
auth.magic.sender({
service: 'gmail',
sender: '[email protected]',
pass: 'your_app_password'
});
// Send link
app.post('/auth/send', async (req, res) => {
const { email } = req.body;
await auth.magic.send(email);
res.json({ message: 'Magic link sent!' });
});
// Verify link
app.get('/auth/verify', async (req, res) => {
try {
const user = await auth.magic.verify(req.query.token);
res.json({ message: 'Login successful!', user });
} catch (err) {
res.status(400).json({ message: err.message });
}
});
app.listen(3000, () => console.log('🚀 Server running on port 3000'));
🇺🇸
More news from United StatesUnited States
NORTH AMERICA
Related News
How Braze’s CTO is rethinking engineering for the agentic area
10h ago
Amazon Employees Are 'Tokenmaxxing' Due To Pressure To Use AI Tools
21h ago

Implementing Multicloud Data Sharding with Hexagonal Storage Adapters
15h ago

DeepMind’s CEO Says AGI May Be ~4 Years Away. The Last Three Missing Pieces Are Not What Most People Think.
15h ago

CCSnapshot - A Claude Code Configs Transfer Tool
21h ago