# nestjs-authnpaas

`nestjs-authnpaas` 是一个轻量级的 NestJS 模块，用于处理身份验证检查。它提供了一个全局守卫，以确保用户在访问端点之前已经登录。

## 目录

- [功能](#功能)
- [安装](#安装)
- [快速开始](#快速开始)
- [装饰器](#装饰器)
  - [@NeedLogin()](#needlogin)
- [服务](#服务)
  - [AuthNPaasService](#authnpaasservice)
- [工作原理](#工作原理)

## 功能

- **身份验证检查**: 确保用户在访问受保护端点之前已经登录。
- **登录重定向**: 自动将未经身份验证的用户重定向到登录 URL。
- **用户 ID 转换**: 支持妙搭用户 ID 与飞书用户 ID 互转。

## 安装

```bash
npm install @lark-apaas/nestjs-authnpaas
# 或者
yarn add @lark-apaas/nestjs-authnpaas
```

## 快速开始

在您的根模块 `app.module.ts` 中导入并配置 `AuthNPaasModule`。

```typescript
import { Module } from '@nestjs/common';
import { AuthNPaasModule } from '@lark-apaas/nestjs-authnpaas';

@Module({
  imports: [
    AuthNPaasModule.forRoot(),
  ],
  controllers: [/* ... */],
  providers: [/* ... */],
})
export class AppModule {}
```


## 装饰器

### @NeedLogin()

使用 `@NeedLogin()` 装饰器在特定端点上强制执行身份验证。

#### 控制器级别

```typescript
import { Controller, Get } from '@nestjs/common';
import { NeedLogin } from '@lark-apaas/nestjs-authnpaas';

@Controller('dashboard')
@NeedLogin() // 此控制器中的所有路由都需要登录
export class DashboardController {
  @Get()
  getDashboard() {
    return '您的个人仪表板';
  }
}
```

#### 方法级别

```typescript
import { Controller, Get } from '@nestjs/common';
import { NeedLogin } from '@lark-apaas/nestjs-authnpaas';

@Controller('api')
export class ApiController {
  @Get('public-data')
  getPublicData() {
    // 此端点不需要身份验证
    return '一些公共数据';
  }

  @Get('private-data')
  @NeedLogin() // 此端点需要身份验证
  getPrivateData() {
    return '一些秘密数据';
  }
}
```

## 服务

### AuthNPaasService

`AuthNPaasService` 提供妙搭用户 ID 与飞书用户 ID 的转换能力。模块注册后即可通过依赖注入使用。

```typescript
import { Injectable } from '@nestjs/common';
import { AuthNPaasService } from '@lark-apaas/nestjs-authnpaas';

@Injectable()
export class MyService {
  constructor(private readonly authn: AuthNPaasService) {}

  async example() {
    // 获取当前登录用户的飞书 user_id
    const larkUserId = await this.authn.getCurrentUserLarkUserId();
    // => 'xxx' | null

    // 批量转换妙搭 userId → 飞书 user_id（最多 100 个）
    const larkUserIds = await this.authn.getBatchLarkUserIds(['uid1', 'uid2']);
    // => ['lark_id_1', 'lark_id_2']  顺序与输入对应，找不到的为 null
  }
}
```

#### getCurrentUserLarkUserId

从请求上下文获取当前妙搭 userId，调用平台接口转换为飞书 user_id。

- **返回值**: `Promise<string | null>` — 飞书 user_id，未登录或转换失败返回 `null`

#### getBatchLarkUserIds

批量将妙搭 userId 转换为飞书 user_id。

- **参数**: `userIds: string[]` — 妙搭用户 ID 列表，最多 100 个
- **返回值**: `Promise<(string | null)[]>` — 飞书 user_id 数组，与输入顺序一一对应，转换失败的项为 `null`
- **异常**: 当 `userIds` 超过 100 个时抛出错误

## 工作原理

### AuthNPaasGuard

此模块将 `AuthNPaasGuard` 注册为全局守卫。在每个传入的请求上，守卫会执行以下步骤：

1. **检查 `@NeedLogin()`**: 它首先检查端点是否需要登录。
2. **确认身份验证状态**: 如果端点需要登录，守卫会确认 `request.userContext` 对象上的 `userId` 是否存在。
3. **如果未通过身份验证则重定向**: 如果端点标有 `@NeedLogin()` 且缺少 `userId`，它会将用户重定向到 `request.userContext` 上的 `loginUrl`。

