@qapps/qauth-sdk
v2.0.0
Published
Framework-agnostic authentication SDK for qAuth service
Downloads
2
Maintainers
Readme
qAuth SDK
A framework-agnostic TypeScript/JavaScript SDK for authentication and authorization using the qAuth service. This SDK provides robust token validation, permission checking, and seamless integration with popular frameworks like NestJS, Express.js, Next.js, and Fastify.
🚀 Features
- 🔐 Token Validation: Validate JWT tokens and API keys via REST API
- 🛡️ Permission Checking: Support for Role-Based Access Control (RBAC)
- 🌍 Multi-Environment: Built-in support for dev, staging, and production environments
- ⚙️ Framework Agnostic: Works with any JavaScript/TypeScript framework
- 📝 TypeScript First: Full TypeScript support with comprehensive type definitions
- 🔄 Retry Logic: Automatic retry with exponential backoff for transient failures
- ⏱️ Timeout Handling: Configurable request timeouts
- 📊 Error Handling: Comprehensive error types with detailed information
📦 Installation
npm install @qapps/qauth-sdkyarn add @qapps/qauth-sdkpnpm add @qapps/qauth-sdk🏁 Quick Start
import { QAuthClient } from '@qapps/qauth-sdk';
// Initialize the client
const qauth = new QAuthClient({
environment: 'dev', // 'dev', 'staging', 'prd', or 'production'
timeout: 5000, // Request timeout in milliseconds
retryAttempts: 3, // Number of retry attempts
});
// Validate a token
const result = await qauth.validateToken('your-jwt-token-or-api-key');
if (result.isValid) {
console.log('Token is valid!', result.data);
} else {
console.log('Token is invalid:', result.message);
}🔧 Configuration
Environment Configuration
The SDK supports multiple environments:
const qauth = new QAuthClient({
environment: 'dev', // Development environment
// environment: 'staging', // Staging environment
// environment: 'prd', // Production environment
// environment: 'production', // Alias for 'prd'
});Custom Base URL
Override the default environment URLs:
const qauth = new QAuthClient({
baseURL: 'https://your-custom-domain.com/qauth',
});Advanced Configuration
const qauth = new QAuthClient({
environment: 'dev',
timeout: 10000, // 10 second timeout
retryAttempts: 5, // Retry up to 5 times
enableLogging: true, // Enable debug logging
});🛠️ API Reference
QAuthClient
Constructor
new QAuthClient(config?: QAuthConfig)Methods
validateToken(token, permissions?)
Validates a token with optional permission checking.
async validateToken(
token: string,
permissions?: string[]
): Promise<ValidationResult>Parameters:
token- JWT token (with or without Bearer prefix) or API keypermissions- Optional array of required permissions
Returns: ValidationResult object with validation details
validateTokenStrict(token, permissions?)
Same as validateToken but throws an error if validation fails.
async validateTokenStrict(
token: string,
permissions?: string[]
): Promise<ValidationResult>isTokenValid(token)
Simple boolean check for token validity.
async isTokenValid(token: string): Promise<boolean>getTokenData(token)
Get token data without permission validation.
async getTokenData(token: string): Promise<TokenData | ApiKeyData | null>🔐 Permission Validation
The SDK supports Role-Based Access Control (RBAC) by checking if the user has required permissions:
// Validate token with required permissions
const result = await qauth.validateToken('token', [
'users:read',
'users:create',
'posts:read'
]);
if (result.isValid) {
// User has all required permissions
console.log('Access granted');
} else {
// Missing permissions or invalid token
console.log('Access denied:', result.message);
}🏗️ Framework Integration
NestJS
Create a guard for route protection:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { QAuthClient } from '@qapps/qauth-sdk';
@Injectable()
export class QAuthGuard implements CanActivate {
private qauth = new QAuthClient({ environment: 'dev' });
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) return false;
try {
const result = await this.qauth.validateToken(token);
request.user = result.data;
return result.isValid;
} catch {
return false;
}
}
private extractTokenFromHeader(request: any): string | undefined {
const authHeader = request.headers.authorization;
return authHeader?.startsWith('Bearer ') ? authHeader : undefined;
}
}
// Use the guard
@Controller('users')
@UseGuards(QAuthGuard)
export class UsersController {
@Get()
getUsers() {
return 'Protected route';
}
}Express.js
Create middleware for route protection:
import express from 'express';
import { QAuthClient } from '@qapps/qauth-sdk';
const qauth = new QAuthClient({ environment: 'dev' });
export const authMiddleware = (requiredPermissions?: string[]) => {
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
try {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const result = await qauth.validateToken(token, requiredPermissions);
if (result.isValid) {
req.user = result.data;
next();
} else {
res.status(401).json({ error: result.message });
}
} catch (error) {
res.status(500).json({ error: 'Authentication error' });
}
};
};
// Use the middleware
app.get('/protected', authMiddleware(['users:read']), (req, res) => {
res.json({ message: 'Access granted', user: req.user });
});Next.js
Create an API route handler:
// pages/api/protected.ts or app/api/protected/route.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { QAuthClient } from '@qapps/qauth-sdk';
const qauth = new QAuthClient({ environment: 'dev' });
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const result = await qauth.validateToken(token);
if (result.isValid) {
res.json({ message: 'Access granted', user: result.data });
} else {
res.status(401).json({ error: result.message });
}
} catch (error) {
res.status(500).json({ error: 'Authentication error' });
}
}Fastify
Create a plugin for authentication:
import fastify from 'fastify';
import { QAuthClient } from '@qapps/qauth-sdk';
const qauth = new QAuthClient({ environment: 'dev' });
// Register auth plugin
fastify.register(async function (fastify) {
fastify.decorateRequest('user', null);
fastify.addHook('preHandler', async (request, reply) => {
if (request.url === '/protected') {
const token = request.headers.authorization;
if (!token) {
return reply.code(401).send({ error: 'No token provided' });
}
try {
const result = await qauth.validateToken(token);
if (result.isValid) {
request.user = result.data;
} else {
return reply.code(401).send({ error: result.message });
}
} catch (error) {
return reply.code(500).send({ error: 'Authentication error' });
}
}
});
});
// Protected route
fastify.get('/protected', async (request, reply) => {
return { message: 'Access granted', user: request.user };
});🔍 Error Handling
The SDK provides comprehensive error types for different scenarios:
import {
QAuthError,
NetworkError,
ValidationError,
ConfigurationError,
PermissionError,
TimeoutError,
ResponseFormatError
} from '@qapps/qauth-sdk';
try {
const result = await qauth.validateToken('invalid-token');
} catch (error) {
if (error instanceof NetworkError) {
console.log('Network issue:', error.message);
} else if (error instanceof ValidationError) {
console.log('Validation failed:', error.message);
} else if (error instanceof PermissionError) {
console.log('Missing permissions:', error.missingPermissions);
} else if (error instanceof TimeoutError) {
console.log('Request timed out after:', error.timeoutMs, 'ms');
} else if (error instanceof ConfigurationError) {
console.log('Configuration error:', error.message);
}
}📊 Response Types
ValidationResult
interface ValidationResult {
success: boolean; // API request success
isValid: boolean; // Token/permission validity
message: string; // Human-readable message
isExpired?: boolean; // Token expiration status (JWT only)
data?: TokenData | ApiKeyData; // Token/API key data
extra?: Record<string, any>; // Additional metadata
}TokenData (JWT)
interface TokenData {
tenantId: string;
organizationId: string;
userId: string;
permissions: string[];
extra: ExtraData;
}ApiKeyData
interface ApiKeyData {
tenantId: string;
organizationId: string;
apiKeyId: string;
permissions: string[];
extra: ExtraData;
}🧪 Testing
The SDK includes comprehensive test coverage for all functionality:
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch🔧 Development
# Install dependencies
npm install
# Build the SDK
npm run build
# Run linting
npm run lint
# Format code
npm run format
# Type checking
npm run type-check📝 TypeScript Support
The SDK is built with TypeScript and provides full type definitions:
import { QAuthClient, QAuthConfig, ValidationResult, TokenData } from '@qapps/qauth-sdk';
const config: QAuthConfig = {
environment: 'dev',
timeout: 5000,
};
const client = new QAuthClient(config);
const result: ValidationResult = await client.validateToken('token');🤝 Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests for any improvements.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🏢 About ExaCall
This SDK is developed and maintained by ExaCall. For more information about our authentication services, please visit our website.
Need help? Check out our documentation or open an issue on GitHub.
