npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

win-portal-auth-sdk

v1.7.10

Published

Shared authentication SDK for Win Portal applications with JWT and OAuth support

Downloads

3,102

Readme

@win-portal/auth-sdk

Shared authentication SDK for Win Portal applications using API Key authentication.

Note: The main Win Portal API already supports API Key authentication through HybridAuthGuard. This SDK provides convenient client libraries for external applications.

Features

  • 🔐 API Key Authentication - Secure authentication using application API keys
  • 🎯 Frontend Client - HTTP client with automatic API key injection for Next.js/React
  • 🛡️ Backend Middleware - Express & NestJS middleware for easy authentication
  • 📦 Lightweight - Minimal dependencies
  • 🔧 TypeScript Support - Full type definitions included
  • Type Safety - Express Request augmentation with full IntelliSense

How It Works

The Win Portal main API (apps/api) has built-in API key support:

  • All endpoints with @Auth() decorator accept X-API-Key header
  • Uses HybridAuthGuard which tries API Key → JWT → OAuth
  • No additional validation endpoint needed

This SDK helps external apps (template-web, template-api) easily integrate with the main API.

Installation

# Using pnpm (recommended for monorepo)
pnpm add @win-portal/auth-sdk

# Using npm
npm install @win-portal/auth-sdk

# Using yarn
yarn add @win-portal/auth-sdk

Entry Points

You can import from the main package or use sub-paths to load only what you need:

  • win-portal-auth-sdk — full package (default)
  • win-portal-auth-sdk/client — frontend only (AuthClient, SessionSocket, APIs)
  • win-portal-auth-sdk/middleware — backend only (Express, NestJS guard)
  • win-portal-auth-sdk/types — type definitions only

Usage

Frontend (Next.js / React)

1. Setup Client

import { AuthClient } from '@win-portal/auth-sdk';

// Initialize the client
const authClient = new AuthClient({
  apiKey: process.env.NEXT_PUBLIC_API_KEY!,
  baseURL: process.env.NEXT_PUBLIC_API_URL,
});

export default authClient;

2. Make Authenticated Requests

The SDK uses namespaced methods for better organization:

import authClient from '@/lib/auth-client';

// Authentication
const loginResult = await authClient.auth.login('[email protected]', 'password');

// ✅ Set JWT token with explicit type (recommended for better performance)
authClient.setToken(loginResult.token, 'jwt');

// Or use hybrid mode (backward compatible)
authClient.setToken(loginResult.token); // defaults to 'jwt'
authClient.setAuthType('hybrid'); // will try JWT first, then OAuth

const profile = await authClient.auth.profile();
const refreshed = await authClient.auth.refresh(refreshToken);
await authClient.auth.logout();

// OAuth Token Usage
// ✅ Set OAuth token with explicit type (recommended for better performance)
const oauthToken = await authClient.oauth.exchangeCode(code, codeVerifier);
authClient.setToken(oauthToken.access_token, 'oauth');

// TOTP (Two-Factor Authentication)
// 1. Setup TOTP for user
const totpSetup = await authClient.auth.setupTotp('My App');
console.log('QR Code:', totpSetup.qr_code);
console.log('Backup Codes:', totpSetup.backup_codes);

// 2. Verify TOTP setup with code from authenticator app
await authClient.auth.verifyTotpSetup('123456');

// 3. Get TOTP status
const status = await authClient.auth.getTotpStatus();
console.log('TOTP Enabled:', status.is_enabled);

// 4. Login with TOTP (2-step authentication)
const loginResult = await authClient.auth.login('[email protected]', 'password');
if (loginResult.totp_required) {
  // User has TOTP enabled, need to verify
  const totpToken = '123456'; // From authenticator app
  const session = await authClient.auth.verifyTotpLogin(
    totpToken,
    undefined, // backup_code (optional)
    loginResult.access_token, // temp_token from initial login
  );
  console.log('Login successful:', session.user);
}

// 5. Disable TOTP
await authClient.auth.disableTotp();

// 6. Regenerate backup codes
const newCodes = await authClient.auth.regenerateTotpBackupCodes();
console.log('New backup codes:', newCodes.backup_codes);

// User Management
const users = await authClient.user.search({
  search: 'john',
  page: 1,
  page_size: 20,
});
const user = await authClient.user.get(userId);
const newUser = await authClient.user.create(userData);
const updated = await authClient.user.update(userId, userData);
await authClient.user.delete(userId);

// Health Check
const health = await authClient.health.check();
const isValid = await authClient.health.validateApiKey();

// License Information
const licenseResponse = await authClient.license.getInfo();
if (licenseResponse.data.success && licenseResponse.data.data) {
  const license = licenseResponse.data.data;
  console.log('License:', license.name);
  console.log('Expires:', license.expires_at);
  console.log('Modules:', license.module_codes);
}

// LINE Messaging
await authClient.line.sendTextMessage({
  userId: 'user-123',
  message: 'สวัสดีครับ!',
});

await authClient.line.sendNotification({
  userId: 'user-123',
  title: 'งานใหม่',
  message: 'คุณมีงานใหม่ที่ต้องดำเนินการ',
  type: 'info',
  action_url: 'https://app.example.com/tasks/123',
  priority: 'high',
});

// OTP Service (for external applications)
// Send OTP via SMS
const otpResult = await authClient.otp.sendOTP({
  method: 'sms',
  recipient: '0812345678',
  context: 'login',
});

console.log('OTP ID:', otpResult.otp_id); // UUID for verification
console.log('Ref Code:', otpResult.ref_code); // OTP-XXXXXX (for tracking/billing)
console.log('Expires at:', otpResult.expires_at);
console.log('Masked recipient:', otpResult.masked_recipient);

// Send OTP via Email
const emailOtpResult = await authClient.otp.sendOTP({
  method: 'email',
  recipient: '[email protected]',
  context: 'verification',
});

// Verify OTP
const verifyResult = await authClient.otp.verifyOTP({
  otp_id: otpResult.otp_id,
  code: '123456', // OTP code from user
});

if (verifyResult.verified) {
  console.log('OTP verified successfully');
}

// Email Service (for external applications)
// Send email with template
await authClient.email.sendEmailWithTemplate(
  'welcome_email',
  { userName: 'สมชาย ใจดี', activationLink: 'https://app.com/activate?token=xxx' },
  '[email protected]',
  { recipient_name: 'สมชาย ใจดี', language: 'th' },
);

// Send email with raw HTML
await authClient.email.sendRawEmail(
  '<h1>ยินดีต้อนรับ</h1><p>ขอบคุณที่สมัครสมาชิก</p>',
  'ยินดีต้อนรับสู่ระบบ',
  '[email protected]',
  { text_body: 'ยินดีต้อนรับ ขอบคุณที่สมัครสมาชิก' },
);

// Todo Management
const todo = await authClient.todo.create({
  title: 'ซื้อของใช้ในบ้าน',
  priority: 'normal',
  user_id: 'user-123',
  description: 'ซื้อน้ำยาล้างจาน, กระดาษทิชชู่',
  due_date: new Date('2024-01-20'),
  category: 'shopping',
  labels: ['บ้าน', 'จำเป็น'],
  is_send_notification: true,
});

// Workflow Management
// Create workflow instance
const workflowInstance = await authClient.workflow.createInstance({
  workflow_definition_id: 'wf-def-123',
  document_id: 'doc-123',
  document_type: 'purchase_request',
  document_title: 'ขอซื้อคอมพิวเตอร์',
  priority: 'high',
  form_data: { amount: 150000, items: ['Laptop', 'Monitor'] },
});

// Get user tasks
const userTasks = await authClient.workflow.getUserTasks({
  status: ['assigned', 'in_progress'],
  page: 1,
  limit: 20,
});

// Perform task action (approve/reject/delegate)
await authClient.workflow.performTaskAction('task-123', {
  action_key: 'approve',
  comments: 'อนุมัติตามขั้นตอน',
});

// Organization Management
// Search organizations
const organizations = await authClient.organization.search({
  search: 'แผนก',
  page: 1,
  limit: 20,
});

// Get organization hierarchy
const ancestors = await authClient.organization.getAncestors('org-123');
const reportingLine = await authClient.organization.getReportingLine('org-123');
const users = await authClient.organization.getUsers('org-123', true); // include descendants

// Webhook Proxy (for external applications)
// Send webhook through proxy
const webhookResult = await authClient.webhook.proxy({
  webhook_url: 'https://api.example.com/webhooks/users',
  event_type: 'user.created',
  payload: { data: { id: 'user-123', email: '[email protected]' } },
  headers: { Authorization: 'Bearer token' },
  secret: 'webhook-secret',
  timeout_ms: 30000,
  max_retries: 3,
});

// Check webhook status
const webhookLog = await authClient.webhook.getStatus(webhookResult.webhook_log_id);
console.log('Status:', webhookLog.status); // 'success', 'failed', 'pending'

For custom endpoints, use direct HTTP methods:

// GET request
const response = await authClient.get('/api/v1/custom/endpoint');

// POST request
const response = await authClient.post('/api/v1/custom/endpoint', data);

3. Use with Next.js API Routes

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import authClient from '@/lib/auth-client';

export async function GET(request: NextRequest) {
  try {
    const response = await authClient.post('/api/v1/users/search', {
      page: 1,
      page_size: 20,
    });
    return NextResponse.json(response.data);
  } catch (error: any) {
    return NextResponse.json(
      { error: error.response?.data?.message || 'Failed to fetch users' },
      { status: error.response?.status || 500 },
    );
  }
}

4. Environment Variables

# .env.local
NEXT_PUBLIC_API_KEY=app_your_api_key_here
NEXT_PUBLIC_API_URL=https://api.yourdomain.com

Backend Middleware (Express & NestJS)

The SDK provides middleware for Express and Guards for NestJS to easily authenticate requests using JWT tokens.

Quick Start - Express

import express from 'express';
import { authMiddleware } from '@win-portal/auth-sdk';

const app = express();

// Apply middleware globally
app.use(
  authMiddleware({
    baseURL: 'https://api.example.com',
    apiKey: 'your-api-key',
  }),
);

// Now all routes have access to req.user and req.token
app.get('/profile', (req, res) => {
  const user = req.user; // User
  res.json({
    email: user.email,
    permissions: user.permissions,
  });
});

Quick Start - NestJS

// auth/guards/auth.guard.ts
import { createAuthGuard } from '@win-portal/auth-sdk';

export const AuthGuard = createAuthGuard({
  baseURL: process.env.API_BASE_URL!,
  apiKey: process.env.API_KEY!,
});

// In controller
@Controller('users')
export class UsersController {
  @Get('profile')
  @UseGuards(AuthGuard)
  getProfile(@CurrentUser() user: User) {
    return user;
  }
}

📖 Complete Middleware Documentation

For complete documentation, examples, and advanced usage:

View Full Middleware Guide →

Includes:

  • Express middleware configuration
  • NestJS guards and decorators
  • Optional authentication
  • Custom token extractors
  • Cache management
  • Permission checking patterns
  • Full working examples

Backend (NestJS - API Client)

For template-api that needs to call main API:

import { AuthClient } from '@win-portal/auth-sdk';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ExternalApiService {
  private authClient: AuthClient;

  constructor() {
    this.authClient = new AuthClient({
      apiKey: process.env.MAIN_API_KEY!,
      baseURL: process.env.MAIN_API_URL,
    });
  }

  async fetchFromMainApi() {
    const response = await this.authClient.get('/api/v1/resources');
    return response.data;
  }
}

API Reference

AuthClient

HTTP client with automatic API key injection.

const client = new AuthClient({
  apiKey: string;              // Required: Your API key
  baseURL?: string;            // Optional: Base URL for requests
  apiKeyHeader?: string;       // Optional: Custom header name (default: 'X-API-Key')
  timeout?: number;            // Optional: Request timeout (default: 30000ms)
  advanced?: {                 // Optional: Advanced configuration
    refreshTimeoutMs?: number;    // Refresh token timeout (default: 5000ms)
  }
})

Advanced Configuration:

// ปรับแต่ง performance และ timeout settings
const client = new AuthClient({
  apiKey: 'your-api-key',
  baseURL: 'https://api.example.com',
  advanced: {
    refreshTimeoutMs: 10000, // เพิ่ม timeout สำหรับ slow network
  },
});

HTTP Methods

  • get<T>(url, config?) - GET request
  • post<T>(url, data?, config?) - POST request
  • put<T>(url, data?, config?) - PUT request
  • patch<T>(url, data?, config?) - PATCH request
  • delete<T>(url, config?) - DELETE request

Convenience Methods

Authentication:

  • auth.login(email, password) - Login user
  • auth.logout() - Logout current session
  • auth.profile() - Get current user profile
  • auth.refresh(refreshToken) - Refresh access token

User Management:

  • user.search(params?) - Search users with pagination
  • user.get(userId) - Get user by ID
  • user.create(userData) - Create new user
  • user.update(userId, userData) - Update user
  • user.delete(userId) - Delete user
  • user.sync(params?) - Sync users for external applications (paginated)

LINE Messaging:

  • line.sendTextMessage({ userId, message }) - Send text message
  • line.sendSticker({ userId, packageId, stickerId }) - Send LINE sticker
  • line.sendImage({ userId, originalContentUrl, previewImageUrl }) - Send image
  • line.sendMessages({ userId, messages }) - Send multiple messages (max 5)
  • line.sendNotification({ userId, title, message, type?, action_url?, priority? }) - Send formatted notification
  • line.checkMessagingAvailability(userId) - Check if user can receive LINE messages

📖 LINE Messaging Guide → - Complete documentation with examples

Health & Validation:

  • health.check() - Check API health
  • health.validateApiKey() - Validate if API key is still active

License Management:

  • license.getInfo() - Get current application license information (requires API Key)
    • Returns license details including module_codes, expires_at, is_expired, etc.
    • Automatically uses application from API key context
    • No need to provide application code or ID

OTP Service (for external applications):

  • otp.sendOTP({ method, recipient, context?, metadata? }) - Send OTP via SMS or Email

    • method: 'sms' | 'email' - Delivery method
    • recipient: Phone number or email address
    • context: Optional context (e.g., 'login', 'verification')
    • metadata: Optional JSON string for additional data
    • Returns: { otp_id, ref_code, expires_at, masked_recipient }
      • otp_id: UUID for verification
      • ref_code: Short reference code (OTP-XXXXXX) for tracking/billing
      • expires_at: OTP expiration timestamp
      • masked_recipient: Masked phone number or email
    • Requires API Key authentication
  • otp.verifyOTP({ otp_id, code }) - Verify OTP code

    • otp_id: OTP ID from sendOTP response
    • code: 6-digit OTP code
    • Returns: { verified: boolean }
    • Requires API Key authentication

Email Service (for external applications):

  • email.sendEmail(request) - Send email (auto-detect Template Mode or Raw HTML Mode)
  • email.sendEmailWithTemplate(templateCode, templateData, recipient, options?) - Send email with template
  • email.sendRawEmail(htmlBody, subject, recipient, options?) - Send email with raw HTML
    • Supports CC, BCC, attachments, priority, scheduled delivery
    • Requires API Key authentication

Todo Management:

  • todo.create(todoData) - Create a new todo item

Workflow Management:

  • workflow.getInstance(instanceId) - Get workflow instance by ID
  • workflow.createInstance(instanceData) - Create a new workflow instance
  • workflow.updateInstance(instanceId, instanceData) - Update workflow instance
  • workflow.searchInstances(params?) - Search workflow instances with pagination
  • workflow.getTask(taskId) - Get workflow task by ID
  • workflow.getUserTasks(params?) - Get user tasks with pagination and filters
  • workflow.performTaskAction(taskId, actionData) - Perform action on task (approve/reject/delegate/escalate)
  • workflow.getDefinition(definitionId) - Get workflow definition by ID
  • workflow.searchDefinitions(params?) - Search workflow definitions with pagination

Organization Management:

  • organization.search(params?) - Search organizations with pagination
  • organization.get(id) - Get organization by ID
  • organization.create(data) - Create new organization
  • organization.update(id, data) - Update organization
  • organization.delete(id) - Delete organization
  • organization.getUsers(id, includeDescendants?) - Get organization users
  • organization.getAncestors(id, includeRoot?) - Get parent organizations at all levels
  • organization.getDirectSupervisor(id) - Get immediate supervisor/parent organization
  • organization.getPath(id) - Get full path from root to organization
  • organization.getReportingLine(id) - Get reporting line (hierarchical and functional)
  • organization.getSiblings(id, includeSelf?) - Get sibling organizations
  • organization.getLevel(id) - Get organization level
  • organization.getCommonAncestor(orgA, orgB) - Get lowest common ancestor
  • organization.isAncestorOf(ancestorId, descendantId) - Check if organization is ancestor
  • organization.clearAll() - Clear all organization data (supports JWT and API Key for external systems)

Webhook Proxy (for external applications):

  • webhook.proxy(request) - Send webhook request through proxy endpoint

    • Supports retry mechanism, custom headers, timeout configuration
    • Returns: { webhook_log_id, status }
    • Requires API Key authentication
  • webhook.getStatus(webhookLogId) - Get webhook log detail by ID

    • Returns: { status, http_status, response_body, error_message, execution_time_ms, retry_count, ... }
    • Requires API Key authentication

Utility Methods

  • setToken(token, type?) - Set authentication token
    • type: 'jwt' (default) | 'oauth' | 'hybrid'
    • Recommended to specify type for better performance
  • getAuthType() - Get current authentication type
  • setAuthType(type) - Change authentication type
  • clearToken() - Clear authentication token
  • getTokenMasked() - Get masked token for display
  • setApiKey(apiKey) - Update API key
  • getApiKeyMasked() - Get masked API key for display
  • getAxiosInstance() - Get underlying axios instance

Session Management

Inactivity Detection:

  • enableInactivityDetection(options) - Enable inactivity detection ที่จับ user activity (mouse/keyboard/touch/scroll)
    • จับ user activity และ trigger callback เมื่อ inactivity timeout
    • ใช้ timeout และ warning time จาก session management config เป็น default
    • Support SSR (Next.js) โดยไม่ error
// Enable inactivity detection
await authClient.enableInactivityDetection({
  callbacks: {
    onInactivityWarning: (remainingSeconds) => {
      console.log(`Inactivity warning: ${remainingSeconds} seconds remaining`);
      // แสดง notification หรือ dialog
      alert(`Session will expire in ${remainingSeconds} seconds due to inactivity`);
    },
    onInactivityTimeout: () => {
      console.log('Inactivity timeout');
      // Redirect to login หรือ logout
      authClient.clearToken();
      window.location.href = '/login';
    },
  },
  timeoutSeconds: 1800, // 30 minutes (optional, default: จาก session management config)
  warningSeconds: 300, // 5 minutes warning (optional, default: จาก session management config)
  events: ['mousemove', 'keydown', 'touchstart', 'scroll'], // (optional, default: ทั้งหมด)
});

// Disable inactivity detection
authClient.disableInactivityDetection();

Session Expiration Monitoring:

  • enableSessionExpirationMonitoring(options) - Enable session expiration monitoring
    • ตรวจสอบ session expiration ตาม config จาก settings/sessions
    • เรียก callback เมื่อใกล้หมดอายุ
await authClient.enableSessionExpirationMonitoring({
  callbacks: {
    onSessionExpiring: (remainingMinutes, expirationType) => {
      console.log(`Session will expire in ${remainingMinutes} minutes (${expirationType})`);
      // แสดง notification หรือ dialog
    },
    onSessionExpired: () => {
      console.log('Session expired');
      // Redirect to login
    },
  },
  checkIntervalSeconds: 30, // ตรวจสอบทุก 30 วินาที
});

Authentication Types

The SDK supports three authentication types through the X-Auth-Type header:

JWT Authentication (Recommended for internal users)

// Login and get JWT token
const session = await authClient.auth.login('[email protected]', 'password');

// ✅ Specify 'jwt' for best performance (fast path validation)
authClient.setToken(session.token, 'jwt');

// All subsequent requests will include:
// Authorization: Bearer <jwt_token>
// X-Auth-Type: jwt

Use JWT when:

  • User logs in with username/password
  • Internal application users
  • Need session-based authentication

OAuth Authentication (Recommended for external apps)

// Exchange authorization code for tokens
const tokens = await authClient.oauth.exchangeCode(code, codeVerifier);

// ✅ Specify 'oauth' for best performance (fast path validation)
authClient.setToken(tokens.access_token, 'oauth');

// All subsequent requests will include:
// Authorization: Bearer <oauth_access_token>
// X-Auth-Type: oauth

Use OAuth when:

  • Third-party application integration
  • User consent-based access
  • Need scope-based permissions

Hybrid Mode (Backward compatible)

// Don't know token type or want automatic detection
authClient.setToken(someToken, 'hybrid');

// All subsequent requests will include:
// Authorization: Bearer <token>
// X-Auth-Type: hybrid

// API will try JWT first, then fallback to OAuth

Use Hybrid when:

  • Backward compatibility needed
  • Token type is unknown
  • Migrating from old implementation

Performance Comparison

| Auth Type | Validation | Performance | Use Case | | --------- | -------------------- | ------------------------- | ------------------------------ | | jwt | JWT only | ⚡ Fast (1 validation) | Internal users, password login | | oauth | OAuth only | ⚡ Fast (1 validation) | External apps, OAuth flow | | hybrid | JWT → OAuth fallback | 🐌 Slower (2 validations) | Unknown token type |

💡 Tip: Always specify 'jwt' or 'oauth' explicitly for ~50% faster authentication!

Security Best Practices

  1. Never commit API keys - Use environment variables
  2. Rotate keys regularly - Update API keys periodically through main API
  3. Use different keys per environment - Separate keys for dev, staging, production
  4. Monitor key usage - Track usage through main API dashboard
  5. Set expiration dates - Configure key expiration in main API
  6. Whitelist IPs - Configure IP restrictions in main API

Error Handling

try {
  const response = await authClient.get('/api/v1/data');
} catch (error: any) {
  if (error.response?.status === 401) {
    console.error('Invalid or expired API key');
  } else if (error.response?.status === 429) {
    console.error('Rate limit exceeded');
  } else {
    console.error('Request failed:', error.message);
  }
}

How to Get API Key

  1. Login to the main Win Portal application
  2. Navigate to Applications section
  3. Create or select your application
  4. Generate a new API key
  5. Copy and save the key (shown only once)
  6. Configure the key in your environment variables

Documentation

📚 Complete Documentation →

License

MIT