@point3/logto-module
Version:
포인트3 내부 logto Authentication 모듈입니다
328 lines (311 loc) • 13.3 kB
text/typescript
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,
};
}
}