Implement passwordless magic link auth
✓Works with OpenClaudeYou are a backend authentication specialist. The user wants to implement passwordless magic link authentication where users receive a unique token via email to log in without a password.
What to check first
- Verify you have an email service configured (SendGrid, Resend, Nodemailer, or similar)
- Check that your database has a
userstable with at leastid,email,created_atcolumns - Confirm you have a way to store temporary tokens (database table or Redis)
Steps
- Create a
magic_tokenstable with columns:id,token,user_email,expires_at,used_at,created_at - Generate a cryptographically secure token using
crypto.randomBytes()with 32 bytes, then hex-encode it - Hash the token before storing in database (use
crypto.createHash('sha256')) to avoid exposing plain tokens if DB is breached - Set token expiration to 15 minutes from creation time
- Send the magic link via email containing the unhashed token in the URL (e.g.
/auth/verify?token=abc123...) - Create a verification endpoint that retrieves the token, hashes it, compares against database, and validates expiration
- Upon successful verification, create a session or JWT and clear the used token
- Add a check to prevent token reuse by storing
used_attimestamp
Code
import crypto from 'crypto';
import nodemailer from 'nodemailer';
import jwt from 'jsonwebtoken';
const mailer = nodemailer.createTransport({
service: 'gmail',
auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS }
});
async function generateMagicLink(email) {
// Generate random token (32 bytes = 64 hex chars)
const token = crypto.randomBytes(32).toString('hex');
// Hash token for storage
const hashedToken = crypto.createHash('sha256').update(token).digest('hex');
// Set 15-min expiration
const expiresAt = new Date(Date.now() + 15 * 60 * 1000);
// Store in database
await db.query(
'INSERT INTO magic_tokens (token, user_email, expires_at) VALUES (?, ?, ?)',
[hashedToken, email, expiresAt]
);
// Send email
const magicLink = `${process.env.APP_URL}/auth/verify?token=${token}`;
await mailer.sendMail({
to: email,
subject: 'Your Magic Login Link',
html: `<a href="${magicLink}">Click here to sign in</a> (expires in 15 min)`
});
return { success: true, message: 'Magic link sent to email' };
}
Note: this example was truncated in the source. See the GitHub repo for the latest full version.
Common Pitfalls
- Treating this skill as a one-shot solution — most workflows need iteration and verification
- Skipping the verification steps — you don't know it worked until you measure
- Applying this skill without understanding the underlying problem — read the related docs first
When NOT to Use This Skill
- When a simpler manual approach would take less than 10 minutes
- On critical production systems without testing in staging first
- When you don't have permission or authorization to make these changes
How to Verify It Worked
- Run the verification steps documented above
- Compare the output against your expected baseline
- Check logs for any warnings or errors — silent failures are the worst kind
Production Considerations
- Test in staging before deploying to production
- Have a rollback plan — every change should be reversible
- Monitor the affected systems for at least 24 hours after the change
Related Authentication Skills
Other Claude Code skills in the same category — free to download.
JWT Auth
Implement JWT authentication from scratch
OAuth Setup
Set up OAuth 2.0 with multiple providers
NextAuth Setup
Configure NextAuth.js/Auth.js
Passport Setup
Set up Passport.js with strategies
Two Factor Auth
Add 2FA/MFA to authentication flow
Session Management
Implement secure session management
API Key Auth
Create API key authentication system
SSO Setup
Set up Single Sign-On (SAML/OIDC)
Want a Authentication skill personalized to YOUR project?
This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.