🔐 DECEMBER DAY 2: Add Auth in 30 Minutes
Yesterday: Database ✅
Today: Authentication
Tomorrow: Your core feature
Pick Your Auth Method:
Option 1: Password Auth (Classic)
Option 2: Magic Links (No Passwords!)
Protect Your Routes:
🎯 Day 2 Success = 3 Things Work:
✅ Can register/login
✅ Get a token back
✅ Token protects routes
📊 Day 2 Live Stats:
Password auth: 67 devs
Magic links: 23 devs
Still building: 12 devs
Quick Test:
Day 2 Check-in:
✅ Auth working?
🔐 Which method?
⏱️ Time taken?
Tomorrow: Build your ONE core feature
Pro tip: Don't overthink. Basic auth shipped > Perfect auth planned.
Who has auth working? 🔐
#December #Day2 #Auth #Building
@postgres
Yesterday: Database ✅
Today: Authentication
Tomorrow: Your core feature
Pick Your Auth Method:
Option 1: Password Auth (Classic)
npm install bcrypt jsonwebtoken
// Register
app.post('/api/register', async (req, res) => {
const { email, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const result = await pool.query(
'INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING id, email',
[email, hashedPassword]
);
const token = jwt.sign(
{ userId: result.rows[0].id },
process.env.JWT_SECRET || 'december-2024',
{ expiresIn: '7d' }
);
res.json({ user: result.rows[0], token });
});
// Login
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
if (!user.rows[0]) return res.status(401).json({ error: 'Invalid credentials' });
const validPassword = await bcrypt.compare(password, user.rows[0].password_hash);
if (!validPassword) return res.status(401).json({ error: 'Invalid credentials' });
const token = jwt.sign(
{ userId: user.rows[0].id },
process.env.JWT_SECRET || 'december-2024',
{ expiresIn: '7d' }
);
res.json({ token });
});
Option 2: Magic Links (No Passwords!)
// Send magic link
app.post('/api/magic', async (req, res) => {
const token = crypto.randomBytes(32).toString('hex');
await pool.query(
`INSERT INTO magic_tokens (email, token, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '15 minutes')`,
[req.body.email, token]
);
console.log(`Magic link: https://localhost:3000/magic/${token}`);
res.json({ message: 'Check console for magic link' });
});
// Verify magic link
app.get('/magic/:token', async (req, res) => {
const result = await pool.query(
'SELECT email FROM magic_tokens WHERE token = $1 AND expires_at > NOW()',
[req.params.token]
);
if (!result.rows[0]) return res.status(401).json({ error: 'Invalid token' });
// Create/get user and return JWT
const token = jwt.sign({ email: result.rows[0].email }, 'december-2024');
res.json({ token });
});
Protect Your Routes:
const authenticate = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET || 'december-2024');
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
};
// Use it
app.get('/api/me', authenticate, (req, res) => {
res.json({ userId: req.user.userId });
});
🎯 Day 2 Success = 3 Things Work:
✅ Can register/login
✅ Get a token back
✅ Token protects routes
📊 Day 2 Live Stats:
Password auth: 67 devs
Magic links: 23 devs
Still building: 12 devs
Quick Test:
# Register
curl -X POST localhost:3000/api/register \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"test123"}'
Day 2 Check-in:
✅ Auth working?
🔐 Which method?
⏱️ Time taken?
Tomorrow: Build your ONE core feature
Pro tip: Don't overthink. Basic auth shipped > Perfect auth planned.
Who has auth working? 🔐
#December #Day2 #Auth #Building
@postgres
❤2🔥2