Bỏ qua, đến nội dung

@digiforce-nc/auth

Package@digiforce-nc/auth
Importimport { AuthManager, Auth } from '@digiforce-nc/auth'

Tổng quan

@digiforce-nc/authframework xác thực (authentication) của hệ thống Digiforce. Package cung cấp:

  • AuthManager - đăng ký và quản lý nhiều kiểu xác thực (email/password, SSO, API key, ...).
  • Auth (abstract) - lớp cơ sở cho từng kiểu xác thực, định nghĩa interface check(), signIn(), signUp().
  • JwtService - ký và verify JWT token, quản lý token lifecycle.
  • TokenBlacklistService - revoke token đã cấp phát.
  • Middleware - tích hợp vào Koa pipeline, tự động gán ctx.state.currentUserctx.state.currentRole.

Phân biệt Auth vs ACL

Auth xác thực "bạn là ai". ACL kiểm tra "bạn được làm gì". Auth chạy trước, đặt user/role vào context, sau đó ACL sử dụng thông tin này để kiểm tra quyền.


Kiến trúc tổng thể


AuthManager API

MethodMô tả
jwtProperty - instance JwtService. Secret lấy từ options.authKey hoặc getDefaultJWTSecret() nếu không cấu hình.
registerTypes(authType, authConfig)Đăng ký kiểu xác thực. authType: tên duy nhất (ví dụ: 'Email/Password'). authConfig: { auth: AuthClass, title?: string }.
setStorer(storer)Bắt buộc gọi trước get(). storer đọc cấu hình authenticator từ database.
setTokenBlacklistService(service)Đăng ký service revoke token.
setTokenControlService(service)Đăng ký service kiểm soát token (renew, expire, concurrent sessions).
get(name, ctx)Tạo instance Auth dựa trên authenticator name + request context. Sử dụng storer để lấy config.
middleware()Trả về Koa middleware xác thực request - xem Auth middleware.

Auth - lớp xác thực trừu tượng

Mỗi kiểu xác thực kế thừa Auth và implement các method:

MethodMô tả
check()Kiểm tra request hiện tại có hợp lệ không. Trả về User hoặc throw error.
checkToken()Kiểm tra trạng thái token (valid, expired, blacklisted).
signIn()Xử lý đăng nhập. Trả về token hoặc session info.
signUp()Xử lý đăng ký tài khoản mới.
signOut()Xử lý đăng xuất. Có thể blacklist token.
validate()Validate dữ liệu đầu vào (username, password format, ...).
skipCheck()Trả về true nếu action hiện tại không cần xác thực.
user getter/setterGet/set user model đã xác thực trên context.

Tạo custom Auth type

typescript
import { Auth } from '@digiforce-nc/auth';

class ApiKeyAuth extends Auth {
  async check() {
    const apiKey = this.ctx.get('X-API-Key');
    if (!apiKey) {
      throw new AuthError('API key is required');
    }

    const user = await this.findUserByApiKey(apiKey);
    if (!user) {
      throw new AuthError('Invalid API key');
    }

    this.user = user;
    return user;
  }

  async signIn() {
    const { email, password } = this.ctx.request.body;
    const user = await this.validateCredentials(email, password);
    const apiKey = await this.generateApiKey(user);
    return { apiKey };
  }

  private async findUserByApiKey(key: string) {
    const repo = this.ctx.db.getRepository('apiKeys');
    const record = await repo.findOne({ filter: { key, enabled: true } });
    return record?.user;
  }
}

JwtService

JwtService quản lý toàn bộ lifecycle của JWT token.

MethodMô tả
sign(payload, options?)Ký JWT. payload thường chứa { userId }. options ghi đè expiresIn, algorithm.
verify(token)Verify và decode token. Throw error nếu expired/invalid. Kiểm tra blacklist nếu có.

Token lifecycle


Auth middleware

Auth middleware được đăng ký vào Koa pipeline và chạy trước ACL middleware.

skipCheck() - bỏ qua xác thực

Một số route không cần xác thực (public API, health check, ...). Có thể đánh dấu bằng:

typescript
// Cách 1: Đăng ký resource là public trong ACL
acl.allow('app', 'getLang');

// Cách 2: Override skipCheck trong Auth class
class MyAuth extends Auth {
  skipCheck() {
    const publicPaths = ['/api/health', '/api/public/'];
    return publicPaths.some(p => this.ctx.path.startsWith(p));
  }
}

Khi skipCheck() trả về true, middleware sẽ gọi next() mà không kiểm tra token.


Error codes

AuthErrorCode enum chứa các mã lỗi chuẩn:

CodeMô tảHTTP Status
EMPTY_TOKENKhông tìm thấy token trong request401
INVALID_TOKENToken không hợp lệ hoặc đã hết hạn401
TOKEN_BLACKLISTEDToken đã bị revoke401
USER_NOT_FOUNDToken hợp lệ nhưng user không tồn tại401
USER_DISABLEDTài khoản đã bị vô hiệu hoá403
INVALID_CREDENTIALSSai username/password401
AUTHENTICATOR_NOT_FOUNDAuthenticator không tồn tại hoặc bị disable500

Token security

Token blacklist sử dụng cache làm storage mặc định. Trong production với multiple instances, phải dùng Redis-backed cache để đảm bảo blacklist đồng bộ giữa các instance.


Ví dụ tích hợp plugin

typescript
import { Plugin } from '@digiforce-nc/server';
import { Auth } from '@digiforce-nc/auth';

class OAuthAuth extends Auth {
  async check() {
    const token = this.ctx.getBearerToken();
    const payload = await this.ctx.app.authManager.jwt.verify(token);
    const user = await this.ctx.db.getRepository('users').findOne({
      filter: { id: payload.userId },
    });
    if (!user) throw new Error('User not found');
    this.user = user;
    return user;
  }

  async signIn() {
    const { code, redirectUri } = this.ctx.request.body;
    const oauthUser = await this.exchangeCodeForUser(code, redirectUri);

    let user = await this.findOrCreateUser(oauthUser);
    const token = this.ctx.app.authManager.jwt.sign({ userId: user.id });

    return { token, user };
  }

  async signOut() {
    const token = this.ctx.getBearerToken();
    await this.ctx.app.authManager.jwt.blacklist?.add(token);
  }
}

class MyOAuthPlugin extends Plugin {
  async load() {
    this.app.authManager.registerTypes('OAuth', {
      auth: OAuthAuth,
      title: 'OAuth 2.0',
    });
  }
}

Phụ thuộc

PackageVai trò
@digiforce-nc/actionsCung cấp action handler cho auth routes (signIn, signUp, ...)
@digiforce-nc/cacheBackend cho TokenBlacklistService
@digiforce-nc/databaseĐọc user, authenticator config từ DB
@digiforce-nc/resourcerĐăng ký auth resource/action vào router

Đọc thêm