@natiwo/auth
v0.1.0
Published
NATIWO Auth - JWT, RBAC e autenticação
Readme
@natiwo/auth
JWT authentication and Role-Based Access Control (RBAC)
Installation
pnpm add @natiwo/auth jsonwebtokenFeatures
- 🔐 JWT Management - Sign, verify, and refresh tokens
- 👥 RBAC - Role-based access control with inheritance
- 🔑 Password Hashing - Secure password handling with bcrypt
- ⏱️ Token Refresh - Automatic token rotation
- 🛡️ Type-Safe - Full TypeScript support
Quick Start
JWT Authentication
import { JWTManager } from '@natiwo/auth';
const jwt = new JWTManager(process.env.JWT_SECRET);
// Sign a token
const token = jwt.sign({
userId: '123',
email: '[email protected]',
role: 'user',
});
// Verify token
try {
const payload = jwt.verify(token);
console.log(payload.userId); // '123'
} catch (error) {
console.error('Invalid token');
}
// Decode without verification
const decoded = jwt.decode(token);RBAC
import { RBACManager } from '@natiwo/auth';
const rbac = new RBACManager();
// Define roles and permissions
rbac.defineRole('admin', ['*']); // All permissions
rbac.defineRole('editor', ['read:*', 'write:posts', 'write:pages']);
rbac.defineRole('user', ['read:own', 'write:own']);
// Role inheritance
rbac.defineRole('super-admin', ['*'], ['admin']);
// Check permissions
const canEdit = rbac.hasPermission('editor', 'write:posts'); // true
const canDelete = rbac.hasPermission('user', 'delete:*'); // false
// Get all permissions for a role
const permissions = rbac.getRolePermissions('editor');Password Hashing
import { PasswordHasher } from '@natiwo/auth';
const hasher = new PasswordHasher();
// Hash password
const hashed = await hasher.hash('myPassword123');
// Verify password
const isValid = await hasher.verify('myPassword123', hashed); // trueAPI Reference
JWTManager
const jwt = new JWTManager(secret: string, options?: JWTManagerOptions);Methods:
sign(payload, options?)- Create JWT tokenverify(token)- Verify and decode tokendecode(token)- Decode without verificationrefresh(token, options?)- Generate new token
Options:
interface JWTOptions {
expiresIn?: string; // '7d', '1h', '30m'
issuer?: string;
audience?: string;
}RBACManager
const rbac = new RBACManager();Methods:
defineRole(name, permissions, inherits?)- Define rolehasPermission(role, permission)- Check permissiongetRolePermissions(role)- Get all permissionsgetRole(name)- Get role definition
Advanced Usage
Custom Payload
interface CustomPayload extends JWTPayload {
userId: string;
email: string;
role: string;
tenantId: string;
metadata: {
department: string;
level: number;
};
}
const token = jwt.sign<CustomPayload>({
userId: '123',
email: '[email protected]',
role: 'admin',
tenantId: 'acme-corp',
metadata: {
department: 'engineering',
level: 5,
},
});Token Refresh
// Original token expires in 1 hour
const accessToken = jwt.sign(payload, { expiresIn: '1h' });
// Refresh token expires in 7 days
const refreshToken = jwt.sign(
{ userId: payload.userId },
{ expiresIn: '7d' }
);
// Later, get new access token
const newAccessToken = jwt.refresh(refreshToken, {
expiresIn: '1h',
});Integration with Express
import { JWTManager } from '@natiwo/auth';
import { Request, Response, NextFunction } from 'express';
const jwt = new JWTManager(process.env.JWT_SECRET);
function authMiddleware(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const payload = jwt.verify(token);
req.user = payload;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
app.get('/protected', authMiddleware, (req, res) => {
res.json({ user: req.user });
});Integration with tRPC
import { JWTManager } from '@natiwo/auth';
import { TRPCError } from '@trpc/server';
const jwt = new JWTManager(process.env.JWT_SECRET);
export async function createContext({ req }) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return { user: null };
}
try {
const user = jwt.verify(token);
return { user };
} catch {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'Invalid token',
});
}
}Complex RBAC with Wildcards
const rbac = new RBACManager();
// Admin can do everything
rbac.defineRole('admin', ['*']);
// Editor can read and write content
rbac.defineRole('editor', [
'read:*',
'write:posts',
'write:pages',
'write:media',
'delete:own',
]);
// Moderator inherits from editor
rbac.defineRole('moderator', [
'delete:posts',
'delete:comments',
], ['editor']);
// Check specific permissions
rbac.hasPermission('editor', 'write:posts'); // true
rbac.hasPermission('editor', 'delete:posts'); // false
rbac.hasPermission('moderator', 'delete:posts'); // true (own)
rbac.hasPermission('moderator', 'write:posts'); // true (inherited)Best Practices
Use strong secrets
// ✅ Good - use environment variable const jwt = new JWTManager(process.env.JWT_SECRET); // ❌ Bad - hardcoded secret const jwt = new JWTManager('my-secret');Set appropriate expiration times
// Access token - short lived const accessToken = jwt.sign(payload, { expiresIn: '15m' }); // Refresh token - longer lived const refreshToken = jwt.sign({ userId }, { expiresIn: '7d' });Handle token expiration gracefully
try { const payload = jwt.verify(token); } catch (error) { if (error.name === 'TokenExpiredError') { // Redirect to refresh token flow } else { // Invalid token - redirect to login } }Use RBAC for granular permissions
// ✅ Good - specific permissions rbac.defineRole('editor', [ 'read:posts', 'write:posts', 'delete:own-posts', ]); // ❌ Bad - too broad rbac.defineRole('editor', ['*']);
License
MIT © NATIWO Sistemas
