https://github.com/theriturajps/itoken
iToken is a robust Node.js library for token-based authentication inspired by JWT
https://github.com/theriturajps/itoken
auth0 authentication authorization itoken jwt-authentication nodejs theriturajps token-based-authentication
Last synced: 4 months ago
JSON representation
iToken is a robust Node.js library for token-based authentication inspired by JWT
- Host: GitHub
- URL: https://github.com/theriturajps/itoken
- Owner: theriturajps
- License: mit
- Created: 2025-04-07T15:05:26.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-04-07T15:05:33.000Z (about 1 year ago)
- Last Synced: 2025-10-11T07:25:24.377Z (8 months ago)
- Topics: auth0, authentication, authorization, itoken, jwt-authentication, nodejs, theriturajps, token-based-authentication
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/itoken
- Size: 8.79 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# iToken
[](https://www.npmjs.com/package/itoken)
[](https://github.com/theriturajps/itoken/blob/main/LICENSE)
[](https://www.npmjs.com/package/itoken)
`iToken` is a robust Node.js library for creating, verifying, and managing authentication tokens with enhanced security features. Inspired by JWT but designed for stricter security practices, it simplifies token-based authentication while prioritizing protection against tampering, expiration, and misuse.
## Installation
Install the package using npm:
```bash
npm install itoken
```
Or using yarn:
```bash
yarn add itoken
```
## Quick Start
```javascript
const { iToken } = require('itoken');
// Create a token
const payload = { userId: '123', role: 'admin' };
const secret = 'your-secure-secret-key';
const token = iToken.encode(payload, secret, { expiresIn: '1h' });
// Verify and decode a token
try {
const decoded = iToken.decode(token, secret);
console.log(decoded.payload); // { userId: '123', role: 'admin', iat: 1649267348, exp: 1649270948 }
} catch (error) {
console.error('Token validation failed:', error.message);
}
// Simple verification (returns boolean)
const isValid = iToken.verify(token, secret);
```
## API Reference
### iToken.encode(payload, secret, options)
Creates a signed token.
**Parameters:**
- **payload** ``: The data to encode (must be a plain object)
- **secret** ``: The secret key used for signing (minimum 8 characters)
- **options** `` (optional):
- **expiresIn** ``: Token expiration time (e.g., '1h', '7d', or seconds)
- **notBefore** ``: Time before which the token is not valid
- **issuer** ``: Token issuer identifier (iss claim)
- **subject** ``: Token subject identifier (sub claim)
- **audience** ``: Token audience identifier (aud claim)
- **header** ``: Additional header values to include
**Returns:** `` - A signed iToken token string.
**Example:**
```javascript
// Creating a token that expires in 1 hour with custom claims
const token = iToken.encode(
{ userId: '123', role: 'admin' },
'your-secret-key',
{
expiresIn: '1h',
issuer: 'api.example.com',
audience: 'client-app'
}
);
```
### iToken.decode(token, secret, options)
Verifies and decodes a token.
**Parameters:**
- **token** ``: The token to decode
- **secret** ``: The secret key used for signing
- **options** `` (optional):
- **skipTimeValidation** ``: Skip expiration and notBefore validation
- **ignoreExpiration** `(Boolean)`: Bypass expiration check
- **ignoreNotBefore** `(Boolean)`: Bypass activation check
**Returns:** `` - An object with `header`, `payload`, and `signature` properties.
**Example:**
```javascript
try {
const { header, payload, signature } = iToken.decode(token, 'your-secret-key');
console.log(`User ID: ${payload.userId}, Role: ${payload.role}`);
console.log(`Token was issued at: ${new Date(payload.iat * 1000)}`);
console.log(`Token expires at: ${new Date(payload.exp * 1000)}`);
} catch (error) {
console.error(`Decoding error: ${error.message}`);
}
```
### iToken.verify(token, secret, options)
Quick validation without returning the decoded data.
**Parameters:**
- **token** ``: The token to verify
- **secret** ``: The secret key used for signing
- **options** `` (optional):
- **ignoreExpiration** ``: Ignore expiration time validation
- **ignoreNotBefore** ``: Ignore notBefore time validation
**Returns:** `` - `true` if the token is valid, otherwise `false`.
**Example:**
```javascript
// Standard verification - checks signature, expiration, and notBefore claims
if (iToken.verify(token, 'your-secret-key')) {
console.log('Token is valid');
} else {
console.log('Token is invalid');
}
// Verification with options - ignore expiration
if (iToken.verify(token, 'your-secret-key', { ignoreExpiration: true })) {
console.log('Token signature is valid (ignoring expiration)');
}
```
### iToken.refresh(token, secret, newOptions)
Refreshes an existing token with same payload but fresh timestamps.
**Parameters:**
- **token** ``: The token to refresh
- **secret** ``: The secret key used for signing
- **newOptions** `` (optional): New options for the refreshed token
**Returns:** `` - A new token string with updated options.
**Example:**
```javascript
// Refreshing an expired token with new expiration time
const refreshedToken = iToken.refresh(
oldToken,
'your-secret-key',
{
expiresIn: '2h',
audience: 'updated-client-app'
}
);
```
## Token Structure
Each iToken token consists of three parts separated by periods:
```
[header].[payload].[signature]
```
1. **Header**: Contains metadata about the token (base64url encoded JSON)
- `alg`: Algorithm used for signing (default: "HS256")
- `typ`: Token type identifier (default: "AEG1")
- Any custom header fields provided in options
2. **Payload**: Contains the claims (base64url encoded JSON)
- Custom claims (user data)
- Standard claims (iat, exp, nbf, iss, aud, sub)
3. **Signature**: HMAC-SHA256 hash of the header and payload using the secret key
## Standard Claims
iToken automatically handles these standard claims:
| Claim | Name | Description |
|-------|------|-------------|
| `iat` | Issued At | Timestamp when the token was created (automatically added) |
| `exp` | Expiration Time | Timestamp when the token expires (added when `expiresIn` option is used) |
| `nbf` | Not Before | Timestamp before which the token is not valid (added when `notBefore` option is used) |
| `iss` | Issuer | Identifies who issued the token (added when `issuer` option is used) |
| `aud` | Audience | Identifies the recipients of the token (added when `audience` option is used) |
| `sub` | Subject | Identifies the subject of the token (added when `subject` option is used) |
## Error Handling
iToken provides specific error types for different validation scenarios:
```javascript
const {
iToken,
iTokenError, // Base error type
iTokenExpiredError, // Token has expired
iTokenNotBeforeError, // Token is not yet valid
iTokenTamperError, // Token signature validation failed
iTokenValidationError, // Token payload or options validation failed
iTokenFormatError // Token format is invalid
} = require('itoken');
try {
const decoded = iToken.decode(token, secret);
// Token is valid
} catch (error) {
if (error instanceof iTokenExpiredError) {
console.log(`Token expired at: ${new Date(error.expiredAt * 1000)}`);
} else if (error instanceof iTokenNotBeforeError) {
console.log(`Token not valid until: ${new Date(error.notBefore * 1000)}`);
} else if (error instanceof iTokenTamperError) {
console.log('Token signature verification failed - possible tampering detected');
} else if (error instanceof iTokenValidationError) {
console.log('Token validation error:', error.message);
} else if (error instanceof iTokenFormatError) {
console.log('Token format error:', error.message);
} else {
console.log('Unknown error:', error.message);
}
}
```
## Working Examples
### API Access Token
```javascript
const { iToken } = require('itoken');
const token = iToken.encode(
{ userId: 456 },
SECRET,
{
issuer: 'api-server',
audience: 'mobile-app',
expiresIn: '30m',
notBefore: '5s' // Prevent immediate use
}
);
```
### Scheduled Maintenance Token
```javascript
const { iToken } = require('itoken');
// Valid during maintenance window (next 2 hours)
const maintenanceStart = Math.floor(Date.now() / 1000) + 3600; // 1h from now
const token = iToken.encode(
{ access: 'maintenance' },
SECRET,
{
notBefore: maintenanceStart,
expiresIn: 7200 // 2h
}
);
```
### Refresh Token Flow
```javascript
const { iToken } = require('itoken');
// Create long-lived refresh token
const refreshToken = iToken.encode(
{ userId: 789 },
SECRET,
{ expiresIn: '7d' }
);
// Refresh when access token expires
function refreshAccess(oldToken) {
return iToken.refresh(oldToken, SECRET, { expiresIn: '1h' });
}
```
### Basic Authentication
```javascript
const { iToken } = require('itoken');
// User login function
function login(username, password) {
// In a real app, verify credentials against a database
if (username === 'user@example.com' && password === 'correct-password') {
// Create a user object (excluding sensitive info)
const user = {
id: 'user-123',
email: username,
name: 'John Doe'
};
// Create a token that expires in 24 hours
const token = iToken.encode(
user,
'your-secure-key',
{ expiresIn: '24h' }
);
return { user, token };
}
throw new Error('Invalid credentials');
}
// Example usage
try {
const { user, token } = login('user@example.com', 'correct-password');
console.log('Login successful:', user);
console.log('Access token:', token);
} catch (error) {
console.error('Login failed:', error.message);
}
```
### Role-Based Access Control
```javascript
const { iToken, iTokenError } = require('itoken');
const SECRET_KEY = 'your-secure-key';
// Authorization function
function checkPermission(token, requiredRole) {
try {
const { payload } = iToken.decode(token, SECRET_KEY);
// Check if user has the required role
if (!payload.roles || !payload.roles.includes(requiredRole)) {
return { authorized: false, message: 'Insufficient permissions' };
}
return { authorized: true, user: payload };
} catch (error) {
if (error instanceof iTokenError) {
return { authorized: false, message: error.message };
}
return { authorized: false, message: 'Authorization failed' };
}
}
// Example token with role information
const adminToken = iToken.encode(
{
id: 'user-456',
name: 'Admin User',
roles: ['user', 'editor', 'admin']
},
SECRET_KEY,
{ expiresIn: '8h' }
);
const editorToken = iToken.encode(
{
id: 'user-789',
name: 'Editor User',
roles: ['user', 'editor']
},
SECRET_KEY,
{ expiresIn: '8h' }
);
// Check permissions
console.log(checkPermission(adminToken, 'admin')); // { authorized: true, user: {...} }
console.log(checkPermission(editorToken, 'admin')); // { authorized: false, message: 'Insufficient permissions' }
```
### Token Refresh Mechanism
```javascript
const { iToken, iTokenExpiredError } = require('itoken');
const SECRET_KEY = 'your-secure-key';
// Function to get a valid token, refreshing if expired
function getValidToken(currentToken) {
try {
// Try to decode the current token
const { payload } = iToken.decode(currentToken, SECRET_KEY);
return { valid: true, token: currentToken, payload };
} catch (error) {
// Check if token is expired
if (error instanceof iTokenExpiredError) {
try {
// Refresh the token with a new expiration time
const newToken = iToken.refresh(currentToken, SECRET_KEY, { expiresIn: '1h' });
const { payload } = iToken.decode(newToken, SECRET_KEY);
return { valid: true, token: newToken, payload, refreshed: true };
} catch (refreshError) {
return { valid: false, error: 'Could not refresh token: ' + refreshError.message };
}
}
return { valid: false, error: error.message };
}
}
// Example usage
const originalToken = iToken.encode({ userId: '123' }, SECRET_KEY, { expiresIn: '1s' });
// Wait 2 seconds for token to expire
setTimeout(() => {
const result = getValidToken(originalToken);
if (result.valid) {
console.log('Token is valid' + (result.refreshed ? ' (refreshed)' : ''));
console.log('Current token:', result.token);
console.log('Payload:', result.payload);
} else {
console.error('Invalid token:', result.error);
}
}, 2000);
```
### Express Middleware Integration
```javascript
const express = require('express');
const { iToken, iTokenError } = require('itoken');
const app = express();
app.use(express.json());
const SECRET_KEY = 'your-secure-key';
// Authentication middleware
function authenticate(req, res, next) {
// Get authorization header
const authHeader = req.headers.authorization;
// Check if header exists and has the right format
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Authentication required',
details: 'Missing or invalid authorization header'
});
}
// Extract token
const token = authHeader.split(' ')[1];
try {
// Verify and decode token
const { payload } = iToken.decode(token, SECRET_KEY);
// Attach user info to the request object
req.user = payload;
// Continue to the route handler
next();
} catch (error) {
// Handle specific errors
if (error instanceof iTokenError) {
let statusCode = 401;
let errorType = 'authentication_failed';
if (error.name === 'iTokenExpiredError') {
errorType = 'token_expired';
} else if (error.name === 'iTokenNotBeforeError') {
errorType = 'token_not_active';
} else if (error.name === 'iTokenTamperError') {
statusCode = 403;
errorType = 'token_invalid';
}
return res.status(statusCode).json({
error: errorType,
details: error.message
});
}
// Handle unexpected errors
return res.status(500).json({
error: 'internal_error',
details: 'Authentication process failed'
});
}
}
// Role-based authorization middleware factory
function authorize(requiredRole) {
return (req, res, next) => {
// Check if user exists (authenticate middleware should run first)
if (!req.user) {
return res.status(401).json({
error: 'authentication_required',
details: 'You must be logged in'
});
}
// Check if user has the required role
if (!req.user.roles || !req.user.roles.includes(requiredRole)) {
return res.status(403).json({
error: 'insufficient_permissions',
details: `Role '${requiredRole}' is required`
});
}
// User has the required role, proceed
next();
};
}
// Login endpoint
app.post('/login', (req, res) => {
const { username, password } = req.body;
// Simple user verification (replace with database lookup in real app)
let user;
if (username === 'admin@example.com' && password === 'admin123') {
user = { id: '1', username, name: 'Admin User', roles: ['user', 'admin'] };
} else if (username === 'user@example.com' && password === 'user123') {
user = { id: '2', username, name: 'Regular User', roles: ['user'] };
} else {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Create token
const token = iToken.encode(
user,
SECRET_KEY,
{
expiresIn: '1h',
issuer: 'api.example.com'
}
);
// Return user info and token
res.json({
message: 'Login successful',
user: {
id: user.id,
name: user.name,
roles: user.roles
},
token,
expiresIn: 3600 // seconds
});
});
// Protected route - requires authentication
app.get('/profile', authenticate, (req, res) => {
res.json({
message: 'Profile accessed successfully',
user: req.user
});
});
// Admin route - requires authentication and admin role
app.get('/admin/dashboard', authenticate, authorize('admin'), (req, res) => {
res.json({
message: 'Admin dashboard accessed successfully',
adminData: {
users: 1045,
newToday: 27,
activeNow: 165
}
});
});
// Public endpoint - no authentication required
app.get('/public', (req, res) => {
res.json({ message: 'This is public information' });
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
```
## Security Best Practices 🔐
Follow these security recommendations when working with iToken tokens:
1. **Use strong secrets**: Minimum 32 random characters
2. **Set reasonable expiration**: Typically 15-60 minutes
3. **Validate all claims**:
```javascript
const { payload } = iToken.decode(token, SECRET);
if (payload.aud !== 'api.example.com') throw new Error('Invalid audience');
```
4. **Always use HTTPS** for token transmission
5. **Rotate secrets** regularly
## Comparing iToken vs JWT
| Feature | iToken | Traditional JWT |
|---------|--------|-----------------|
| **Token Type** | Uses "AEG1" type identifier | Uses "JWT" type identifier |
| **Error Handling** | Provides specific error types | Generic errors in most libraries |
| **Time Validation** | Built-in expiration and notBefore handling | Varies by library |
| **Signature Verification** | Timing-safe comparison by default | Varies by library |
| **API** | Streamlined, focused API | Often more complex |
| **Refresh Mechanism** | Built-in token refresh | Usually requires custom implementation |
## TypeScript Support
iToken provides TypeScript definitions for better integration with TypeScript projects:
```typescript
import { iToken, iTokenError } from 'itoken';
interface UserPayload {
id: string;
username: string;
roles: string[];
}
// Create a token with typed payload
const payload: UserPayload = {
id: '123',
username: 'user@example.com',
roles: ['user', 'editor']
};
const token = iToken.encode(payload, 'secret', { expiresIn: '1h' });
try {
// Decoded payload will have UserPayload type plus standard claims
const { payload } = iToken.decode(token, 'secret');
// TypeScript knows about these properties
console.log(payload.id);
console.log(payload.roles);
console.log(payload.iat); // Standard claim
} catch (error) {
if (error instanceof iTokenError) {
console.error('iToken error:', error.message);
}
}
```
## FAQ ❓
**Q: How is this different from JWT?**
A: Focuses on identity security with stricter defaults and zero dependencies.
**Q: Can I use existing JWT tokens?**
A: No - iToken uses `AEG1` token format for enhanced security.
**Q: Is this production-ready?**
A: Yes! Follows Node.js crypto best practices.
**Q: How to handle clock skew?**
A: Add buffer to expiration times:
```javascript
iToken.encode(payload, SECRET, { expiresIn: 3600 + 300 }); // 1h + 5m buffer
```
---
**License**: MIT | **Author**: [Ritu Raj Pratap Singh](https://github.com/theriturajps)
**GitHub**: [github.com/theriturajps/itoken](https://github.com/theriturajps/itoken)
**Report Issues**: [Issues](https://github.com/theriturajps/itoken/issues)