UNPKG

@point3/logto-module

Version:

포인트3 내부 logto Authentication 모듈입니다

328 lines (311 loc) 13.3 kB
import { DynamicModule, LoggerService, Provider, Type } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { LogtoLoggerServiceToken, LogtoLoginSessionToken, LogtoM2MClientToken, OAuthClient, OAuthClientToken, LogtoLoginSession, LogtoM2MClient, Prompt, } from './client'; import { LogtoTokenVerifier, LogtoTokenVerifierToken } from './token'; import { LogtoTokenGuard, LogtoTokenGuardToken } from './stateless'; /** LogtoModule 옵션 주입 토큰 */ export const LOGTO_MODULE_OPTIONS = Symbol('LOGTO_MODULE_OPTIONS'); /** 로거 설정 */ export interface LogtoLoggerOptions { /** 로거 모듈 (forRoot에서만 필요) */ module?: Type<any>; /** 로거 서비스의 Injection Token */ token: Symbol | string; } /** forRoot 옵션 */ export interface LogtoModuleOptions { /** 모듈을 전역으로 설정할지 여부 */ global?: boolean; /** 클라이언트 기능 활성화 (OAuthClient, LogtoM2MClient, LogtoLoginSession) */ enableClient?: boolean; /** 로거 설정 */ logger: LogtoLoggerOptions & { module: Type<any> }; } /** forRootAsync useFactory 반환 타입 */ export interface LogtoModuleFactoryOptions { /** 클라이언트 기능 활성화 */ enableClient?: boolean; } /** forRootAsync 옵션 */ export interface LogtoModuleAsyncOptions { /** 모듈을 전역으로 설정할지 여부 */ global?: boolean; /** 의존 모듈 목록 (로거 모듈 포함) */ imports?: Type<any>[]; /** 로거 서비스의 Injection Token */ loggerToken: Symbol | string; /** 옵션 팩토리 함수 */ useFactory: (...args: any[]) => LogtoModuleFactoryOptions | Promise<LogtoModuleFactoryOptions>; /** 팩토리 함수에 주입할 의존성 */ inject?: any[]; } /** * LogtoModule * * Logto 인증 및 토큰 검증, 클라이언트 기능을 NestJS 모듈로 제공합니다. * * - **Stateless 모드 (enableClient=false)**: * API 서버와 같이 토큰 검증만 필요한 경우 사용합니다. * `@LogtoProtected()` 가드와 `LogtoTokenVerifier`만 활성화됩니다. * * - **Stateful 모드 (enableClient=true)**: * 로그인/로그아웃 처리가 필요한 웹 애플리케이션이나, * M2M 통신으로 Logto의 User/Role 관리 API를 사용해야 할 경우에 사용합니다. * `OAuthClient`, `LogtoM2MClient` 등 모든 클라이언트 기능이 활성화됩니다. */ export class LogtoModule { /** * forRoot * * LogtoModule을 초기화합니다. (동기 설정) * * @param options - 모듈 설정 옵션 * @returns DynamicModule * * @example * // Stateless 모드: 토큰 검증만 필요한 경우 * LogtoModule.forRoot({ * global: true, * logger: { module: WinstonLoggerModule, token: 'LOGGER' }, * }) * * @example * // Stateful 모드: 클라이언트 기능이 필요한 경우 * LogtoModule.forRoot({ * global: true, * enableClient: true, * logger: { module: WinstonLoggerModule, token: 'LOGGER' }, * }) */ static forRoot(options: LogtoModuleOptions): DynamicModule { const { global = false, enableClient = false, logger } = options; const baseProviders: Provider[] = [ { provide: LogtoLoggerServiceToken, useExisting: logger.token, }, { provide: LogtoTokenVerifierToken, useFactory: (configService: ConfigService) => { return new LogtoTokenVerifier({ jwksUri: configService.getOrThrow<string>('LOGTO_JWKS_URI'), issuer: configService.getOrThrow<string>('LOGTO_AUTH_ISSUER'), }); }, inject: [ConfigService], }, { provide: LogtoTokenGuardToken, useClass: LogtoTokenGuard, }, ]; const clientProviders: Provider[] = enableClient ? [ { provide: OAuthClientToken, useFactory: (configService: ConfigService, loggerService: LoggerService) => { return new OAuthClient( { endpoint: configService.getOrThrow<string>('LOGTO_AUTH_ENDPOINT'), clientId: configService.getOrThrow<string>('LOGTO_CLIENT_ID'), clientSecret: configService.getOrThrow<string>('LOGTO_CLIENT_SECRET'), resources: [configService.getOrThrow<string>('LOGTO_RESOURCES')], scopes: configService.getOrThrow<string>('LOGTO_SCOPES').split(','), prompt: configService.getOrThrow<string>('LOGTO_PROMPT') as Prompt, redirectUri: configService.getOrThrow<string>('LOGTO_REDIRECT_URI'), signInUri: configService.getOrThrow<string>('LOGTO_SIGN_IN_URI'), dashboardSignInUri: configService.getOrThrow<string>('LOGTO_DASHBOARD_SIGN_IN_URI'), }, loggerService, ); }, inject: [ConfigService, LogtoLoggerServiceToken], }, { provide: LogtoLoginSessionToken, useFactory: ( configService: ConfigService, loggerService: LoggerService, oauthClient: OAuthClient, ) => { return new LogtoLoginSession( configService.getOrThrow<string>('LOGTO_M2M_API_URL'), loggerService, oauthClient, ); }, inject: [ConfigService, LogtoLoggerServiceToken, OAuthClientToken], }, { provide: LogtoM2MClientToken, useFactory: ( configService: ConfigService, tokenVerifier: LogtoTokenVerifier, loggerService: LoggerService, ) => { return new LogtoM2MClient( { endpoint: configService.getOrThrow<string>('LOGTO_AUTH_ENDPOINT'), clientId: configService.getOrThrow<string>('LOGTO_M2M_CLIENT_ID'), clientSecret: configService.getOrThrow<string>('LOGTO_M2M_CLIENT_SECRET'), resource: configService.getOrThrow<string>('LOGTO_M2M_RESOURCE'), apiUrl: configService.getOrThrow<string>('LOGTO_M2M_API_URL'), scopes: ['all'], }, tokenVerifier, loggerService, ); }, inject: [ConfigService, LogtoTokenVerifierToken, LogtoLoggerServiceToken], }, ] : []; const providers = [...baseProviders, ...clientProviders]; return { module: LogtoModule, global, imports: [logger.module], providers, exports: providers, }; } /** * forRootAsync * * LogtoModule을 초기화합니다. (비동기 설정, ConfigService 활용 가능) * * @param options - 비동기 모듈 설정 옵션 * @returns DynamicModule * * @example * LogtoModule.forRootAsync({ * global: true, * imports: [WinstonLoggerModule], * loggerToken: 'LOGGER', * useFactory: (configService: ConfigService) => ({ * enableClient: configService.get('LOGTO_CLIENT') === 'true', * }), * inject: [ConfigService], * }) */ static forRootAsync(options: LogtoModuleAsyncOptions): DynamicModule { const { global = false, imports = [], loggerToken, useFactory, inject = [] } = options; const asyncOptionsProvider: Provider = { provide: LOGTO_MODULE_OPTIONS, useFactory, inject, }; const baseProviders: Provider[] = [ asyncOptionsProvider, { provide: LogtoLoggerServiceToken, useExisting: loggerToken, }, { provide: LogtoTokenVerifierToken, useFactory: (configService: ConfigService) => { return new LogtoTokenVerifier({ jwksUri: configService.getOrThrow<string>('LOGTO_JWKS_URI'), issuer: configService.getOrThrow<string>('LOGTO_AUTH_ISSUER'), }); }, inject: [ConfigService], }, { provide: LogtoTokenGuardToken, useClass: LogtoTokenGuard, }, ]; const clientProviders: Provider[] = [ { provide: OAuthClientToken, useFactory: ( opts: LogtoModuleFactoryOptions, configService: ConfigService, loggerService: LoggerService, ) => { if (opts.enableClient) { return new OAuthClient( { endpoint: configService.getOrThrow<string>('LOGTO_AUTH_ENDPOINT'), clientId: configService.getOrThrow<string>('LOGTO_CLIENT_ID'), clientSecret: configService.getOrThrow<string>('LOGTO_CLIENT_SECRET'), resources: [configService.getOrThrow<string>('LOGTO_RESOURCES')], scopes: configService.getOrThrow<string>('LOGTO_SCOPES').split(','), prompt: configService.getOrThrow<string>('LOGTO_PROMPT') as Prompt, redirectUri: configService.getOrThrow<string>('LOGTO_REDIRECT_URI'), signInUri: configService.getOrThrow<string>('LOGTO_SIGN_IN_URI'), dashboardSignInUri: configService.getOrThrow<string>('LOGTO_DASHBOARD_SIGN_IN_URI'), }, loggerService, ); } return null; }, inject: [LOGTO_MODULE_OPTIONS, ConfigService, LogtoLoggerServiceToken], }, { provide: LogtoLoginSessionToken, useFactory: ( opts: LogtoModuleFactoryOptions, configService: ConfigService, loggerService: LoggerService, oauthClient: OAuthClient, ) => { if (opts.enableClient) { return new LogtoLoginSession( configService.getOrThrow<string>('LOGTO_M2M_API_URL'), loggerService, oauthClient, ); } return null; }, inject: [LOGTO_MODULE_OPTIONS, ConfigService, LogtoLoggerServiceToken, OAuthClientToken], }, { provide: LogtoM2MClientToken, useFactory: ( opts: LogtoModuleFactoryOptions, configService: ConfigService, tokenVerifier: LogtoTokenVerifier, loggerService: LoggerService, ) => { if (opts.enableClient) { return new LogtoM2MClient( { endpoint: configService.getOrThrow<string>('LOGTO_AUTH_ENDPOINT'), clientId: configService.getOrThrow<string>('LOGTO_M2M_CLIENT_ID'), clientSecret: configService.getOrThrow<string>('LOGTO_M2M_CLIENT_SECRET'), resource: configService.getOrThrow<string>('LOGTO_M2M_RESOURCE'), apiUrl: configService.getOrThrow<string>('LOGTO_M2M_API_URL'), scopes: ['all'], }, tokenVerifier, loggerService, ); } return null; }, inject: [LOGTO_MODULE_OPTIONS, ConfigService, LogtoTokenVerifierToken, LogtoLoggerServiceToken], }, ]; const providers = [...baseProviders, ...clientProviders]; return { module: LogtoModule, global, imports: [...imports], providers, exports: providers, }; } }