UNPKG

authkit-js

Version:

Express auth toolkit (JWT, Sessions with Redis, Google/GitHub OAuth) in JavaScript

316 lines (238 loc) โ€ข 9.34 kB
# AuthKit-JS (Express) โ€” JWT, Sessions (Redis), Google/GitHub OAuth A simple, pluggable **authentication library** for **Express.js** applications. Designed to support multiple strategies: * ๐Ÿ”‘ **JWT Authentication** * ๐Ÿ— **Session Authentication** (Memory / Redis store) * ๐ŸŒ **Google OAuth2** * ๐Ÿ™ **GitHub OAuth2** * ๐Ÿ“˜ **Facebook OAuth2** * ๏ฃฟ **Apple Sign In (OAuth/OIDC)** * ๐Ÿ”’ Middleware helpers (`makeAuthenticate`, `requireAuth`) * ๐Ÿ”ข Built-in **TOTP 2FA utilities** (secret + QR generation, token verify) ## โœจ Features - Drop-in router: `makeAuthRouter` with `/auth/me`, JWT refresh/logout, session logout, and OAuth callbacks - `attachAuth` middleware: auto-populates `req.auth` without failing your routes - JWT and Session strategies with env-aware, secure-by-default cookies - Optional JWT refresh rotation with reuse detection via pluggable refresh stores (Memory/Redis) - Lightweight CORS (`makeCors`) and rate limit (`makeRateLimit`) middlewares - Guards: `requireAuthGuard`, `requireRole`, `requirePermission` - Standard error helpers with codes: `Errors` - TypeScript declarations included --- ## ๐Ÿ“ฆ Installation ```bash npm install authkit-js # Required peer dependencies npm install express cookie-parser jsonwebtoken # Optional (when you use them) npm install ioredis google-auth-library node-fetch ``` 2FA helpers (`TwoFA`) are included out-of-the-box; no extra install is required. --- ## โšก Quick Usage ```js const express = require('express'); const cookieParser = require('cookie-parser'); const { AuthKit, makeAuthRouter, attachAuth, makeCors, makeRateLimit, } = require('authkit-js'); const { MemorySessionStore } = require('authkit-js/lib/stores/memory'); const app = express(); app.use(express.json()); app.use(cookieParser()); // CORS (optional) app.use(makeCors({ origin: ['http://localhost:5173'] })); // Init authkit const kit = new AuthKit({ jwt: { secret: 'super-secret' }, session: { store: new MemorySessionStore() }, }); // Attach auth context on every request (non-fatal) app.use(attachAuth(kit)); // Auth router with simple rate limit (optional) app.use('/auth', makeRateLimit({ capacity: 100, intervalMs: 60_000 }), makeAuthRouter(kit)); // Your login endpoint (example) app.post('/login', async (req, res) => { const user = { id: 1, email: req.body.email }; await kit.jwt.login(res, user); res.json({ ok: true }); }); // Protected route app.get('/me', async (req, res) => { const ctx = await kit.authenticate(req); if (!ctx || !ctx.user) return res.status(401).json({ error: 'Unauthorized' }); res.json({ user: ctx.user }); }); app.listen(3000, () => console.log('Server running on http://localhost:3000')); ``` --- ## ๐Ÿ”— Social Login Setup ### Google OAuth2 ```js const { GoogleOAuthStrategy } = require('authkit-js/lib/strategies/oauth-google'); const kit = new AuthKit({ google: new GoogleOAuthStrategy({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, redirectUri: process.env.GOOGLE_REDIRECT_URI, verify: async (profile) => ({ id: profile.sub, email: profile.email }), }) }); ``` ### GitHub OAuth2 ### Facebook OAuth2 ```js const { FacebookOAuthStrategy } = require('authkit-js/lib/strategies/oauth-facebook'); const kit = new AuthKit({ facebook: new FacebookOAuthStrategy({ clientId: process.env.FB_CLIENT_ID, clientSecret: process.env.FB_CLIENT_SECRET, redirectUri: process.env.FB_REDIRECT_URI, verify: async (profile) => ({ id: profile.id, name: profile.name, email: profile.email }), }) }); ``` ### Apple Sign In (OAuth/OIDC) ```js const { AppleOAuthStrategy } = require('authkit-js/lib/strategies/oauth-apple'); const kit = new AuthKit({ apple: new AppleOAuthStrategy({ clientId: process.env.APPLE_CLIENT_ID, // For production, generate a JWT client secret signed with your Apple key clientSecret: process.env.APPLE_CLIENT_SECRET, redirectUri: process.env.APPLE_REDIRECT_URI, verify: async (payload) => ({ id: payload.sub, email: payload.email }), }) }); ``` --- ## ๐Ÿšฆ Built-in Router Endpoints - `GET /auth/me` โ€” returns `{ user }` when authenticated - `POST /auth/jwt/refresh` โ€” refreshes JWT using refresh cookie - `POST /auth/jwt/logout` โ€” clears JWT cookies - `POST /auth/session/logout` โ€” clears session and CSRF cookies and deletes session - `GET /auth/oauth/google` and `/auth/oauth/google/callback` - `GET /auth/oauth/github` and `/auth/oauth/github/callback` - `GET /auth/oauth/facebook` and `/auth/oauth/facebook/callback` - `GET /auth/oauth/apple` and `/auth/oauth/apple/callback` Note: You still implement your own login that calls `kit.jwt.login(res, user)` or `kit.session.login(res, user)`. --- ## ๐Ÿ”ข Two-Factor Authentication (TOTP) โ€” Built-in Helpers Use the `TwoFA` helper to generate a TOTP secret, show a QR code, and verify tokens during login. ```js const { TwoFA } = require('authkit-js'); // 1) Start 2FA setup โ€” generate a secret and a QR code Data URL app.get('/2fa/setup', async (req, res) => { const username = String(req.query.username); const secret = TwoFA.generateSecret(`MyApp (${username})`); // { base32, otpauth_url, ... } const qr = await TwoFA.generateQRCodeDataURL(secret.otpauth_url); // Persist `secret.base32` temporarily until user verifies a token res.json({ secret: secret.base32, qr }); }); // 2) Enable 2FA โ€” verify a token from user's authenticator app app.post('/2fa/enable', (req, res) => { const { secretBase32, token } = req.body; const ok = TwoFA.verifyToken(secretBase32, token, 1); // window=1 for slight clock drift if (!ok) return res.status(400).json({ error: 'Invalid token' }); // Mark 2FA enabled for the user, persist secretBase32 res.json({ ok: true }); }); // 3) During login โ€” require a valid TOTP if the user has 2FA enabled app.post('/login', async (req, res) => { const { username, password, twoFactorToken } = req.body; const user = await findUser(username); if (!user || !(await checkPassword(user, password))) return res.status(401).json({ error: 'Invalid credentials' }); if (user.twoFAEnabled) { const ok = TwoFA.verifyToken(user.twoFASecretBase32, twoFactorToken || '', 1); if (!ok) return res.status(401).json({ error: 'Invalid 2FA token' }); } await kit.jwt.login(res, { id: user.id, name: user.name }); res.json({ ok: true }); }); ``` These helpers are strategy-agnostic and work with both JWT and Session flows. --- ## ๐Ÿ”„ Refresh Token Rotation (JWT) Enable rotation + reuse detection by providing a refresh store: ```js const { MemoryRefreshStore } = require('authkit-js'); const kit = new AuthKit({ jwt: { secret: process.env.JWT_SECRET, refreshStore: new MemoryRefreshStore(), cookie: { env: process.env.NODE_ENV, crossSite: true } } }); ``` Use `RedisRefreshStore` for multi-instance deployments. --- ## ๐Ÿ›ก Guards & Errors ```js const { attachAuth, requireAuthGuard, requireRole, requirePermission, Errors } = require('authkit-js'); app.use(attachAuth(kit)); app.get('/admin', requireAuthGuard(), requireRole('admin'), (req, res) => res.send('ok')); app.use((err, _req, res, _next) => res.status(err.status || 500).json({ code: err.code, error: err.message })); ``` --- ## ๐ŸŒ CORS & Rate Limit ```js const { makeCors, makeRateLimit } = require('authkit-js'); // Global CORS app.use(makeCors({ origin: ['https://app.example.com'] })); // Basic rate limit (per IP) app.use('/auth', makeRateLimit({ capacity: 100, intervalMs: 60_000 })); ``` --- ## ๐Ÿช Environment-Aware Cookies Cookies default to `httpOnly` and: - `secure=true` in production (or when `crossSite=true`) - `sameSite='lax'` by default, or `'none'` when `crossSite=true` Configure per strategy: ```js new AuthKit({ jwt: { secret: process.env.JWT_SECRET, cookie: { env: process.env.NODE_ENV, crossSite: true } }, session: { store: new MemorySessionStore(), cookie: { env: process.env.NODE_ENV, crossSite: false } } }); ``` ```js const { GitHubOAuthStrategy } = require('authkit-js/lib/strategies/oauth-github'); const kit = new AuthKit({ github: new GitHubOAuthStrategy({ clientId: process.env.GH_CLIENT_ID, clientSecret: process.env.GH_CLIENT_SECRET, redirectUri: process.env.GH_REDIRECT_URI, verify: async (profile) => ({ id: profile.id, login: profile.login }), }) }); ``` --- ## ๐Ÿ—„ Redis Session Store ```js const Redis = require('ioredis'); const { RedisSessionStore } = require('authkit-js/lib/stores/redis'); const redis = new Redis(process.env.REDIS_URL); const kit = new AuthKit({ session: { store: new RedisSessionStore(redis) } }); ``` --- ## ๐Ÿงช Example App A full working demo is in `examples/express-server/`: ```bash cd examples/express-server cp .env.example .env node index.js ``` Routes: * `GET /auth/google` โ†’ Google login * `GET /auth/github` โ†’ GitHub login * `GET /me` โ†’ Protected profile route --- ## ๐Ÿ“œ License MIT ยฉ 2025 Shashi