Now.js Framework Documentation

Now.js Framework Documentation

AuthManager

TH 04 Sep 2026 01:40

AuthManager

ภาพรวม

AuthManager คือระบบจัดการ authentication ใน Now.js Framework รองรับ JWT, session และ OAuth flows

ใช้เมื่อ:

  • ต้องการ login/logout
  • ต้องการ JWT authentication
  • ต้องการ role-based access control
  • ต้องการ auto token refresh

ทำไมต้องใช้:

  • ✅ Multiple auth strategies (JWT, session)
  • ✅ Automatic token refresh
  • ✅ Rate limiting
  • ✅ Permission/role checking
  • ✅ HTTP interceptors
  • ✅ Security integration

การใช้งานพื้นฐาน

Initialization

await AuthManager.init({
  enabled: true,
  type: 'jwt-httponly',
  endpoints: {
    login: '/api/auth/login',
    logout: '/api/auth/logout',
    refresh: '/api/auth/refresh',
    user: '/api/auth/user'
  }
});

Login

try {
  await AuthManager.login({
    email: 'user@example.com',
    password: 'password123'
  });

  console.log('Logged in as:', AuthManager.getUser());
} catch (error) {
  console.error('Login failed:', error.message);
}

Logout

await AuthManager.logout();

การตั้งค่า

AuthManager.init({
  enabled: true,

  // Auth type: 'jwt', 'jwt-httponly', 'session'
  type: 'jwt-httponly',

  autoInit: true,

  endpoints: {
    login: '/api/auth/login',
    logout: '/api/auth/logout',
    refresh: '/api/auth/refresh',
    user: '/api/auth/user'
  },

  token: {
    // Storage: 'localStorage', 'sessionStorage', 'cookie'
    storage: 'cookie',
    key: 'auth_token',
    refreshKey: 'refresh_token',
    expiresKey: 'token_expires'
  },

  autoRefresh: {
    enabled: true,
    beforeExpiry: 60  // seconds before expiry
  },

  redirect: {
    afterLogin: '/',
    afterLogout: '/login',
    onUnauthorized: '/login'
  },

  rateLimit: {
    enabled: true,
    maxAttempts: 5,
    lockDuration: 300000  // 5 minutes
  }
});

API อ้างอิง

AuthManager.login(credentials, options)

Login user

Parameter Type Description
credentials object {email, password} หรือ custom
options.redirect boolean Redirect after login

Returns: Promise<Object> - User data

const user = await AuthManager.login({
  email: 'user@example.com',
  password: 'password123'
});

AuthManager.logout(callServer?, options?)

Logout user

Parameter Type Description
callServer boolean Call logout endpoint (default: true)
options.redirect boolean Redirect after logout
await AuthManager.logout();

AuthManager.isAuthenticated()

ตรวจสอบ authentication status

Returns: boolean

if (AuthManager.isAuthenticated()) {
  showProfileMenu();
}

AuthManager.getUser()

รับ current user data

Returns: Object|null

const user = AuthManager.getUser();
console.log(user.name, user.email);

AuthManager.hasRole(role)

ตรวจสอบ role

Parameter Type Description
role string Role name

Returns: boolean

if (AuthManager.hasRole('admin')) {
  showAdminPanel();
}

AuthManager.hasPermission(permission)

ตรวจสอบ permission

Parameter Type Description
permission string Permission name

Returns: boolean

if (AuthManager.hasPermission('users.edit')) {
  showEditButton();
}

AuthManager.refreshToken()

Refresh access token

Returns: Promise<boolean>

AuthManager.checkAuthStatus()

ถาม endpoint verify ว่าผู้ใช้ปัจจุบันคือใคร แล้วอัปเดตสถานะ session
พร้อมแคชโปรไฟล์ผู้ใช้ไว้ใน local storage สำหรับแสดงผล

Returns: Promise<Object> — {authenticated, user} และมี offline: true
เพิ่มมาเมื่อตอบจากแคช หรือมี error เมื่อล้มเหลว

พฤติกรรมตอนออฟไลน์: ถ้าเซิร์ฟเวอร์ ปฏิเสธ (401/403) จะล้าง session เหมือนเดิม
แต่ถ้า ติดต่อเซิร์ฟเวอร์ไม่ได้ จะไม่ล้าง — จะกู้โปรไฟล์จากแคชมาใช้แทน
แล้วใส่ offline: true มาในผลลัพธ์ เพื่อไม่ให้การหลุดการเชื่อมต่อเด้งผู้ใช้
กลับไปหน้า login ซึ่งจะทำให้แอปที่ออกแบบมาให้ทำงานออฟไลน์ได้ใช้งานไม่ได้เลย

const status = await AuthManager.checkAuthStatus();
if (status.authenticated && status.offline) {
  // ทำงานจากโปรไฟล์ในแคช แสดงป้าย "ไม่มีการเชื่อมต่อ"
}

ข้อแลกเปลี่ยนที่ต้องรู้: ถ้า session ถูกยกเลิกที่ฝั่งเซิร์ฟเวอร์ขณะเครื่องออฟไลน์อยู่
หน้าจอจะยังแสดงว่าผู้ใช้ล็อกอินอยู่จนกว่าจะกลับมาออนไลน์ ในสถานะนั้นอ่านหรือเขียนอะไรไม่ได้
เพราะทุก API call ยังถูกตรวจสิทธิ์ที่เซิร์ฟเวอร์ตามปกติ — ค้างแค่หน้าจอเท่านั้น
พอ verify สำเร็จครั้งถัดไป session จะถูกล้าง

AuthManager.isUnreachable(response)

แยกกรณี "ติดต่อเซิร์ฟเวอร์ไม่ได้" ออกจาก "เซิร์ฟเวอร์ปฏิเสธ"

simpleFetch คืน status 0 เมื่อ fetch ล้มเหลวเอง (ออฟไลน์ DNS ล่ม ฯลฯ)
และคืน 408 เมื่อ timeout ส่วนการปฏิเสธจริงจะมี HTTP status ของตัวเองเสมอ
กรณี navigator.onLine === false ก็นับว่าติดต่อไม่ได้เช่นกัน

พารามิเตอร์ ชนิด คำอธิบาย
response Object ผลลัพธ์จาก simpleFetch

Returns: boolean

AuthManager.restoreCachedUser()

คืนโปรไฟล์ผู้ใช้จากแคช แล้วทำเครื่องหมายว่า session ยัง authenticated อยู่

ใช้ เฉพาะ ตอนที่ติดต่อเซิร์ฟเวอร์ไม่ได้เท่านั้น ไม่ใช่เพื่อข้ามการตรวจสอบจริง
ถ้าไม่มีโปรไฟล์ในแคชที่ใช้ได้จะคืน null ซึ่งผู้เรียกต้องกลับไปล้าง session ตามเดิม

Returns: Object|null

AuthManager.requireAuth()

Redirect ถ้าไม่ authenticated

// Route guard
AuthManager.requireAuth();

AuthManager.requireGuest()

Redirect ถ้า authenticated

// Login page guard
AuthManager.requireGuest();

เหตุการณ์

Event เมื่อเกิด Detail
auth:initialized init() เสร็จ {authenticated, user}
auth:login Login สำเร็จ {user, timestamp}
auth:logout Logout สำเร็จ {timestamp}
auth:restored เลิกสวมสิทธิ์ผู้ใช้อื่น กลับเป็น admin เดิม {user, timestamp}
auth:token_refreshed ต่ออายุ session สำเร็จ {user, token, timestamp}
auth:social_login_start เริ่ม social login {provider}
auth:error เกิดข้อผิดพลาดด้าน auth {message, error, timestamp}
EventManager.on('auth:login', (data) => {
  console.log('User logged in:', data.user);
});

EventManager.on('auth:logout', () => {
  // Clear app state
  StateManager.reset();
});

ตัวอย่างการใช้งานจริง

Login Form

<form id="login-form">
  <input type="email" name="email" required>
  <input type="password" name="password" required>
  <button type="submit">Login</button>
</form>

<script>
document.getElementById('login-form').onsubmit = async (e) => {
  e.preventDefault();
  const form = e.target;

  try {
    await AuthManager.login({
      email: form.email.value,
      password: form.password.value
    });

    // Redirect to intended or dashboard
    RedirectManager.afterLogin();
  } catch (error) {
    NotificationManager.error(error.message);
  }
};
</script>

Protected Route

// Router middleware
RouterManager.beforeEach((to, from, next) => {
  const isProtected = to.meta?.requiresAuth;

  if (isProtected && !AuthManager.isAuthenticated()) {
    AuthManager.saveIntendedUrl(to.path);
    AuthManager.requireAuth();
    return;
  }

  next();
});

Role-Based UI

<nav>
  <a href="/" data-show-if="authenticated">Dashboard</a>

  <template data-if="hasRole('admin')">
    <a href="/admin">Admin Panel</a>
  </template>

  <a href="/login" data-hide-if="authenticated">Login</a>
  <a href="#" onclick="AuthManager.logout()" data-show-if="authenticated">Logout</a>
</nav>

API Request with Auth

// AuthManager auto-attaches token to requests
const response = await ApiService.get('/api/protected-resource');

// Handle auth errors
EventManager.on('auth:error', () => {
  AuthManager.requireAuth();
});

ข้อควรระวัง

⚠️ 1. Init ก่อนใช้

// ❌ เรียกก่อน init
AuthManager.login({...});

// ✅ Init ก่อน
await AuthManager.init({ enabled: true });
await AuthManager.login({...});

⚠️ 2. Check Auth Status

// ❌ เชื่อว่า authenticated โดยไม่ check
showProtectedContent();

// ✅ Check ก่อน
if (AuthManager.isAuthenticated()) {
  showProtectedContent();
}

เอกสารที่เกี่ยวข้อง