authkit-js
Version:
Express auth toolkit (JWT, Sessions with Redis, Google/GitHub OAuth) in JavaScript
150 lines (128 loc) • 5.34 kB
JavaScript
const express = require('express');
const { makeCors } = require('../middleware/cors');
// Create an opinionated auth router that wires up what is available on the kit
// Routes provided (when strategy present):
// - GET /me → returns { user }
// - POST /jwt/refresh → refresh JWT using refresh cookie
// - POST /jwt/logout → clears JWT cookies
// - POST /session/logout → destroys session and clears cookies
// - GET /oauth/google → redirect to Google OAuth
// - GET /oauth/google/callback → handle Google callback
// - GET /oauth/github → redirect to GitHub OAuth
// - GET /oauth/github/callback → handle GitHub callback
//
// You are expected to implement your own login that calls kit.jwt.login or kit.session.login.
function makeAuthRouter(kit, options = {}) {
const router = express.Router();
// Optional CORS for all auth routes
if (options.cors) {
const corsMw = makeCors(typeof options.cors === 'object' ? options.cors : {});
router.use(corsMw);
}
// Attach auth context on each request (non-failing)
router.use(async (req, _res, next) => {
try { req.auth = await kit.authenticate(req) || undefined; }
catch { /* ignore and continue */ }
next();
});
// Me endpoint
router.get('/me', (req, res) => {
if (!req.auth || !req.auth.user) return res.status(401).json({ error: 'Unauthorized' });
res.json({ user: req.auth.user });
});
// JWT routes when configured
if (kit.jwt) {
router.post('/jwt/refresh', async (req, res, next) => {
try { const tokens = await kit.jwt.refresh(req, res); res.json({ ok: true, tokens }); }
catch (e) { next(e); }
});
router.post('/jwt/logout', async (_req, res) => {
await kit.jwt.logout(res);
res.json({ ok: true });
});
}
// Session routes when configured
if (kit.session) {
router.post('/session/logout', async (req, res) => {
await kit.session.logout(req, res);
res.json({ ok: true });
});
}
// Google OAuth (optional)
if (kit.google) {
router.get('/oauth/google', (_req, res) => {
const url = kit.google.getAuthUrl();
res.redirect(url);
});
router.get('/oauth/google/callback', async (req, res, next) => {
try {
const code = req.query && req.query.code;
if (!code) return res.status(400).json({ error: 'Missing code' });
const { user, tokens, profile } = await kit.google.handleCallback(code);
// Delegate to whichever strategy is default for persistence
if (kit.jwt) await kit.jwt.login(res, user);
else if (kit.session) await kit.session.login(res, user);
res.json({ ok: true, user, profile });
} catch (e) { next(e); }
});
}
// GitHub OAuth (optional)
if (kit.github) {
router.get('/oauth/github', (_req, res) => {
const url = kit.github.getAuthUrl();
res.redirect(url);
});
router.get('/oauth/github/callback', async (req, res, next) => {
try {
const code = req.query && req.query.code;
if (!code) return res.status(400).json({ error: 'Missing code' });
const { user, accessToken, profile } = await kit.github.handleCallback(code);
if (kit.jwt) await kit.jwt.login(res, user);
else if (kit.session) await kit.session.login(res, user);
res.json({ ok: true, user, profile });
} catch (e) { next(e); }
});
}
// Facebook OAuth (optional)
if (kit.facebook) {
router.get('/oauth/facebook', (_req, res) => {
const url = kit.facebook.getAuthUrl();
res.redirect(url);
});
router.get('/oauth/facebook/callback', async (req, res, next) => {
try {
const code = req.query && req.query.code;
if (!code) return res.status(400).json({ error: 'Missing code' });
const { user, accessToken, profile } = await kit.facebook.handleCallback(code);
if (kit.jwt) await kit.jwt.login(res, user);
else if (kit.session) await kit.session.login(res, user);
res.json({ ok: true, user, profile });
} catch (e) { next(e); }
});
}
// Apple OAuth (optional)
if (kit.apple) {
router.get('/oauth/apple', (_req, res) => {
const url = kit.apple.getAuthUrl();
res.redirect(url);
});
// Apple commonly posts back; this handler uses GET for simplicity here
router.get('/oauth/apple/callback', async (req, res, next) => {
try {
const code = req.query && req.query.code;
if (!code) return res.status(400).json({ error: 'Missing code' });
const { user, tokens, profile } = await kit.apple.handleCallback(code);
if (kit.jwt) await kit.jwt.login(res, user);
else if (kit.session) await kit.session.login(res, user);
res.json({ ok: true, user, profile });
} catch (e) { next(e); }
});
}
// Generic error handler for this router
router.use((err, _req, res, _next) => {
const status = err && (err.status || err.statusCode) || 500;
res.status(status).json({ error: err.message || 'Internal Server Error' });
});
return router;
}
module.exports = { makeAuthRouter };