本文提供了使用 Clerk 在 NestJS 后端应用程序中实现身份验证和授权的全面分步指南。
Clerk 是一个综合平台,提供可嵌入的用户界面、灵活的 API 以及用于无缝用户身份验证和管理的直观且强大的仪表板。它涵盖了从会话管理和多因素身份验证到社交登录、魔术链接、电子邮件或短信一次性密码等的一切。
身份验证和安全要求、趋势和最佳实践总是在不断发展,因为数据保护和隐私变得越来越重要。通过将这些责任转移给专业服务提供商,您可以专注于构建应用程序的核心功能并更快地交付。
像 Clerk 这样的平台可以为您承担这些安全任务。
该项目需要一个新的或现有的 NestJS 项目、一个 Clerk 帐户和应用程序,以及 Passport、Passport Strategy 和 Clerk 后端 SDK 等库。
您可以使用 Nest CLI 轻松设置新的 NestJS 项目。使用您喜欢的任何包管理器,运行以下命令来创建新的 Nest 应用程序:
$ pnpm add -g @nestjs/cli $ nest new clerk-auth
查看 NestJS 文档以了解更多详细信息。
如果您还没有 Clerk 帐户,请创建一个 Clerk 帐户并在 Clerk 仪表板中设置一个新应用程序。您可以在 Clerk 网站上开始使用。
可以使用以下命令安装该项目所需的库:
$ pnpm add @clerk/backend @nestjs/config @nestjs/passport passport passport-custom
在项目的根目录中创建一个 .env 文件来管理不同环境、生产、开发或登台的变量。
添加以下变量,用从 Clerk 帐户仪表板获取的实际密钥替换占位符。
# .env CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY CLERK_SECRET_KEY=YOUR_SECRET_KEY
要使用 ConfigService 访问整个应用程序中的环境变量,请将 ConfigModule 导入根 AppModule。
// src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), ], }) export class AppModule {}
本节介绍如何在您的 NestJS 项目中集成和使用 Clerk 后端 SDK。
将 Clerk 客户端注册为提供程序,使其可以使用装饰器注入到类中,从而可以在整个代码库中的任何需要的地方使用它,如接下来的部分所示。
$ pnpm add -g @nestjs/cli $ nest new clerk-auth
接下来,您需要向 Nest 注册提供程序以启用依赖项注入。
$ pnpm add @clerk/backend @nestjs/config @nestjs/passport passport passport-custom
当用户通过 Clerk 的托管页面或前端应用程序注册或登录时,Clerk 会发出 JWT 令牌。然后,此令牌作为不记名令牌发送到向 NestJS 后端应用程序发出的请求的授权标头中。
在NestJS中,Passport是推荐的实现身份验证策略的方式。您将创建一个自定义 Clerk 策略,用于使用 Clerk 客户端验证令牌。
# .env CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY CLERK_SECRET_KEY=YOUR_SECRET_KEY
validate() 方法返回 NestJS 自动附加到 request.user 的用户数据。
创建一个 AuthModule,它提供 Clerk 策略并与 PassportModule 集成。然后,在AppModule中注册AuthModule。
// src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), ], }) export class AppModule {}
// src/providers/clerk-client.provider.ts import { createClerkClient } from '@clerk/backend'; import { ConfigService } from '@nestjs/config'; export const ClerkClientProvider = { provide: 'ClerkClient', useFactory: (configService: ConfigService) => { return createClerkClient({ publishableKey: configService.get('CLERK_PUBLISHABLE_KEY'), secretKey: configService.get('CLERK_SECRET_KEY'), }); }, inject: [ConfigService], };
受保护的路由是需要用户经过身份验证才能访问的路由。
守卫根据某些运行时条件确定特定请求是否应由路由处理程序处理。
如果您想默认保护应用程序中的所有路由,则需要执行以下步骤:
// src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ClerkClientProvider } from 'src/providers/clerk-client.provider'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), ], providers: [ClerkClientProvider], }) export class AppModule {}
// src/auth/clerk.strategy.ts import { User, verifyToken } from '@clerk/backend'; import { Injectable, Injectable, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-custom'; import { UsersService } from 'src/users/users.service'; import { Request } from 'express'; import { ClerkClient } from '@clerk/backend'; @Injectable() export class ClerkStrategy extends PassportStrategy(Strategy, 'clerk') { constructor( @Inject('ClerkClient') private readonly clerkClient: ClerkClient, private readonly configService: ConfigService, ) { super(); } async validate(req: Request): Promise<User> { const token = req.headers.authorization?.split(' ').pop(); if (!token) { throw new UnauthorizedException('No token provided'); } try { const tokenPayload = await verifyToken(token, { secretKey: this.configService.get('CLERK_SECRET_KEY'), }); const user = await this.clerkClient.users.getUser(tokenPayload.sub); return user; } catch (error) { console.error(error); throw new UnauthorizedException('Invalid token'); } } }
由于默认情况下您的大多数端点都会受到保护,因此您可以将身份验证防护配置为全局防护。
// src/auth/auth.module.ts import { Module } from '@nestjs/common'; import { ClerkStrategy } from './clerk.strategy'; import { PassportModule } from '@nestjs/passport'; import { ClerkClientProvider } from 'src/providers/clerk-client.provider'; import { ConfigModule } from '@nestjs/config'; @Module({ imports: [PassportModule, ConfigModule], providers: [ClerkStrategy, ClerkClientProvider], exports: [PassportModule], }) export class AuthModule {}
在这两个控制器中,AppController 中使用 Public 装饰器来将路由指定为公共。相比之下,AuthController 中不需要装饰器来将路由指定为受保护,因为身份验证防护默认情况下全局应用。
// src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ClerkClientProvider } from 'src/providers/clerk-client.provider'; import { AuthModule } from 'src/auth/auth.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), AuthModule, ], providers: [ClerkClientProvider], }) export class AppModule {}
// src/decorators/public.decorator.ts import { SetMetadata } from '@nestjs/common'; export const IS_PUBLIC_KEY = 'isPublic'; export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
注意:记得在AppModule中注册AppController,在AuthModule中注册AuthController。
Clerk 作为一个平台,负责处理身份验证和安全责任,紧跟最新趋势和最佳实践。这使您能够专注于构建应用程序的核心功能并加速您的开发过程。
在本指南中,我们介绍了实施 Clerk 身份验证的步骤,从设置项目到保护路由。这些基本步骤应该可以帮助您开始探索身份验证服务平台的可能性。
本文末尾包含该项目的完整功能示例。
在 NestJS 后端应用程序中使用 Clerk 身份验证和用户管理
这个 monorepo 包含以下软件包和应用程序:
每个包和应用程序都是 100% TypeScript。
这个 monorepo 已经为您设置了一些附加工具:
以上是在 NestJS 服务器应用程序中使用 Clerk 进行身份验证的详细内容。更多信息请关注PHP中文网其他相关文章!