@point3/logto-module
v1.0.23
Published
포인트3 내부 logto Authentication 모듈입니다
Readme
point3-logto-module
NestJS 기반 Logto 인증/권한 통합 모듈
(서버/클라이언트, M2M, 사용자/역할 관리, OAuth, DI 기반 확장성 제공)
📦 개요
@point3-logto-module은 Logto 인증 시스템을 NestJS 환경에서 손쉽게 통합할 수 있도록 설계된 모듈 번들입니다.
OAuth, M2M(Machine-to-Machine), 사용자/역할 관리, 토큰 검증, 인증 가드 등 인증/권한 관련 기능을 일관된 DI 패턴으로 제공합니다.
동작 모드
LOGTO_CLIENT 환경변수 값에 따라 두 가지 모드로 동작합니다:
Stateless 모드 (
LOGTO_CLIENT=false또는 미설정):- API 서버와 같이 토큰 검증만 필요한 경우 사용
@LogtoProtected()가드와LogtoTokenVerifier만 활성화- 필수 환경변수:
LOGTO_JWKS_URI,LOGTO_AUTH_ISSUER만 필요
Stateful 모드 (
LOGTO_CLIENT=true):- 로그인/로그아웃 처리가 필요한 웹 애플리케이션이나 M2M 통신이 필요한 경우 사용
OAuthClient,LogtoM2MClient등 모든 클라이언트 기능 활성화- 추가 환경변수 필요 (OAuth, M2M 관련)
주요 특징
- 유연한 로깅 연동: 외부 로거 모듈/토큰을 DI로 주입받아, 다양한 로깅 시스템과 쉽게 통합
- NestJS Dynamic Module 패턴: 환경/구성에 따라 동적으로 모듈 생성
- Global 모듈 지원: 애플리케이션 전체에서 사용할 수 있는 글로벌 모듈로 설정 가능
- 실제 서비스에서 검증된 인증/권한 관리 기능: OAuth, M2M, 사용자/역할 관리, 토큰 검증, 인증 가드 등
🏗️ 주요 구성요소
1. LogtoModule (Dynamic Module)
- 외부에서 로거 모듈과 토큰을 주입받아, Logto 인증/권한 기능을 번들로 제공
logto.module.LogtoModule.forLogger(loggerModule, loggerToken, global?)패턴으로 사용- global 파라미터를 true로 설정하면 애플리케이션 전체에서 사용 가능한 글로벌 모듈로 등록
2. 핵심 서비스/토큰
- OAuthClient: OAuth 인증(로그인/로그아웃 URI, 토큰 발급 등)
- LogtoM2MClient: 서버 간(M2M) 인증 및 사용자/역할 관리
- LogtoLoginSession: 세션 기반 로그인 플로우 관리
- LogtoTokenVerifier: JWT 토큰 검증 및 권한 체크
- LogtoTokenGuard: NestJS 인증 가드(컨트롤러 보호)
- LogtoLoggerServiceToken: DI로 주입받는 외부 로거 토큰
- stateless: 미들웨어/핸들러 기반의 Stateless 인증/인가 유틸리티 및 가드 제공
3. 주요 타입/설정
- LogtoConfig: 인증/권한 기능을 위한 환경설정 객체
- Prompt, GrantType: OAuth 표준 파라미터 Enum
- LogtoUser, LogtoRole 등: 사용자/역할 관리용 타입
⚙️ 설치
npm install @point3/logto-module🚀 사용법
1. 외부 로거 모듈 준비
// my-logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonLoggerService } from './winston-logger.service';
export const MY_LOGGER_TOKEN = Symbol.for('LOGGER');
@Module({
providers: [
{
provide: MY_LOGGER_TOKEN,
useClass: WinstonLoggerService,
},
],
exports: [MY_LOGGER_TOKEN],
})
export class MyLoggerModule {}2. AppModule에서 LogtoModule 동적 생성
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as logto from '@point3/logto-module';
import { MyLoggerModule, MY_LOGGER_TOKEN } from './my-logger.module';
@Module({
imports: [
// 환경변수를 먼저 로드
ConfigModule.forRoot({ isGlobal: true }),
// LogtoModule을 글로벌 모듈로 설정 (세 번째 파라미터를 true로 설정)
logto.module.LogtoModule.forLogger(MyLoggerModule, MY_LOGGER_TOKEN, true),
],
})
export class AppModule {}3. 서비스 DI 및 사용 예시 (클래식 DI)
import { Injectable, Inject } from '@nestjs/common';
import { client } from '@point3/logto-module';
@Injectable()
export class AuthService {
constructor(
@Inject(client.OAuthClientToken)
private readonly oauthClient: InstanceType<typeof client.OAuthClient>,
@Inject(client.LogtoM2MClientToken)
private readonly m2mClient: InstanceType<typeof client.LogtoM2MClient>,
@Inject(client.LogtoLoginSessionToken)
private readonly loginSession: InstanceType<typeof client.LogtoLoginSession>,
) {}
// OAuth 로그인 URI 생성
async loginUri() {
return this.oauthClient.getSignInURI('admin');
}
// M2M 사용자 생성
async createUser(user) {
return this.m2mClient.createUser(user);
}
// 세션 기반 로그인 플로우 예시
async sessionLoginFlow(username: string, password: string) {
const session = await this.loginSession.createSignInSession(username);
await this.loginSession.verifyPassword(session.sessionId, password);
// ... 추가 플로우
}
}4. stateless 네임스페이스 활용 예시
⚠️ 중요한 주의사항
requiredScopes와requiredRoles에 사용되는 모든 스코프와 역할은 반드시 Logto 관리 콘솔에서 먼저 정의되어야 합니다.
- 스코프(Scopes): Logto 콘솔의 API Resources → 해당 리소스 → Scopes에서 정의
- 역할(Roles): Logto 콘솔의 User Management → Roles에서 정의
- 역할-스코프 연결: 각 역할에 필요한 스코프들을 Logto 콘솔에서 할당
코드에서 사용하는 스코프/역할 이름이 Logto에 정의되지 않았다면 토큰 검증이 실패합니다.
4-1. LogtoProtected 데코레이터를 사용한 라우트 보호
// user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { stateless } from '@point3/logto-module';
@Controller('users')
export class UserController {
// 기본 토큰 인증만 필요한 경우
@Get('profile')
@stateless.LogtoProtected()
getProfile() {
return { message: '인증된 사용자만 접근 가능' };
}
// 특정 스코프가 필요한 경우
@Get('admin')
@stateless.LogtoProtected({
requiredScopes: ['admin', 'user:read']
})
getAdminData() {
return { message: '관리자 스코프가 필요한 데이터' };
}
// 특정 역할이 필요한 경우
@Get('management')
@stateless.LogtoProtected({
requiredRoles: ['superuser', 'management-point3']
})
getManagementData() {
return { message: '관리자 역할이 필요한 데이터' };
}
// 스코프와 역할을 모두 요구하는 경우
@Get('secure')
@stateless.LogtoProtected({
requiredScopes: ['admin'],
requiredRoles: ['superuser']
})
getSecureData() {
return { message: '고급 권한이 필요한 데이터' };
}
}4-2. 커스텀 데코레이터로 유저 정보 추출
// user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
// LogtoTokenGuard가 request.user에 설정한 사용자 정보 반환
return request.user;
},
);
// 컨트롤러에서 사용
@Controller('me')
export class MeController {
@Get()
@stateless.LogtoProtected()
getMe(@CurrentUser() user: any) {
return {
userId: user.userId,
managerId: user.managerId,
clientId: user.clientId
};
}
}4-3. 전역 가드로 LogtoTokenGuard 등록
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { stateless } from '@point3/logto-module';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: stateless.LogtoTokenGuard,
},
],
})
export class AppModule {}4-4. 커스텀 가드에서 LogtoTokenGuard 확장
// custom.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { stateless } from '@point3/logto-module';
@Injectable()
export class CustomLogtoGuard extends stateless.LogtoTokenGuard {
async canActivate(context: ExecutionContext): Promise<boolean> {
// 기본 토큰 검증 수행
const isValid = await super.canActivate(context);
if (!isValid) return false;
// 추가 커스텀 로직
const request = context.switchToHttp().getRequest();
const user = request.user;
// 예: 특정 시간대에만 접근 허용
const currentHour = new Date().getHours();
if (currentHour < 9 || currentHour > 18) {
return false;
}
return true;
}
}4-5. 타입 안전성을 위한 역할 타입 정의
// types/roles.ts
export type UserRole = 'admin' | 'user' | 'moderator';
// 컨트롤러에서 타입 안전하게 사용
@Controller('typed')
export class TypedController {
@Get('admin-only')
@stateless.LogtoProtected<UserRole>({
requiredRoles: ['admin'] // 타입 체크됨
})
getAdminData() {
return { message: '타입 안전한 역할 기반 접근 제어' };
}
}🛠️ 주요 기능 및 역할
LogtoModule
- 외부 로거 모듈/토큰을 받아, 인증/권한 관련 서비스와 가드를 번들로 제공
- DynamicModule 패턴으로 다양한 환경에 유연하게 적용 가능
OAuthClient
- OAuth 로그인/로그아웃 URI 생성
- 인증 코드로 토큰 발급
- 토큰 해지(로그아웃)
LogtoM2MClient
- M2M 인증(서버 간 토큰 발급)
- 사용자/역할 생성, 조회, 수정, 삭제, 할당 등 관리
- 인증코드 발송/검증, 비밀번호 변경 등 부가 기능
LogtoLoginSession
- 세션 기반 로그인 플로우(단계별 API 호출)
- 비밀번호 검증, 사용자 식별, 동의 처리 등
LogtoTokenVerifier
- JWT 토큰 검증 및 권한 체크
- 인증 가드(LogtoTokenGuard)에서 활용
stateless
- 미들웨어/핸들러 기반의 Stateless 인증/인가 유틸리티 및 가드 제공
- Express/Fastify 등에서 직접 활용 가능한 인증 미들웨어, 데코레이터, 커스텀 가드 등 포함
📝 환경설정 예시 (LogtoConfig)
const config: LogtoConfig = {
endpoint: 'https://auth.example.com/oidc',
appId: 'my-client-id',
appSecret: 'my-client-secret',
grantType: GrantType.AuthorizationCode,
scopes: ['openid', 'profile', 'email'],
resources: ['https://api.example.com'],
prompt: Prompt.Login,
redirectUri: 'https://myapp.com/callback',
};🗂️ 환경변수 목록 및 설명
필수 환경변수 (Stateless/Stateful 모드 공통)
| 환경변수명 | 설명 | 예시 값 | |-----------------------------------|---------------------------------------------------|------------------------------------------| | LOGTO_JWKS_URI | JWT 검증용 JWKS 엔드포인트 | https://auth.example.com/oidc/jwks | | LOGTO_AUTH_ISSUER | JWT 발급자(iss) | https://auth.example.com/oidc |
선택적 환경변수
| 환경변수명 | 설명 | 예시 값 | 필요 조건 | |-----------------------------------|---------------------------------------------------|------------------------------------------|-----------------------------------------| | LOGTO_CLIENT | Stateful 모드 활성화 (클라이언트 기능 사용) | true | 클라이언트 기능 사용 시 | | LOGTO_AUTH_ENDPOINT | Logto 인증 서버 OIDC 엔드포인트 | https://auth.example.com/oidc | LOGTO_CLIENT=true | | LOGTO_CLIENT_ID | OAuth 클라이언트 ID | my-client-id | LOGTO_CLIENT=true | | LOGTO_CLIENT_SECRET | OAuth 클라이언트 시크릿 | my-client-secret | LOGTO_CLIENT=true | | LOGTO_RESOURCES | 접근할 리소스 서버(여러 개일 경우 쉼표로 구분) | https://api.example.com | LOGTO_CLIENT=true | | LOGTO_SCOPES | 요청할 OAuth 스코프(쉼표로 구분) | openid,profile,email | LOGTO_CLIENT=true | | LOGTO_PROMPT | OAuth prompt 파라미터 | login | LOGTO_CLIENT=true | | LOGTO_REDIRECT_URI | 인증 후 리다이렉트될 URI | https://myapp.com/callback | LOGTO_CLIENT=true | | LOGTO_SIGN_IN_URI | 기본 로그인 URI | https://auth.example.com | LOGTO_CLIENT=true | | LOGTO_DASHBOARD_SIGN_IN_URI | 대시보드 로그인 URI(선택) | https://dashboard.example.com | LOGTO_CLIENT=true | | LOGTO_M2M_CLIENT_ID | M2M 인증용 클라이언트 ID | my-m2m-client-id | LOGTO_CLIENT=true & M2M 기능 사용 시 | | LOGTO_M2M_CLIENT_SECRET | M2M 인증용 클라이언트 시크릿 | my-m2m-client-secret | LOGTO_CLIENT=true & M2M 기능 사용 시 | | LOGTO_M2M_RESOURCE | M2M 인증용 리소스 | https://api.example.com | LOGTO_CLIENT=true & M2M 기능 사용 시 | | LOGTO_M2M_API_URL | M2M API 서버의 base URL | https://api.example.com/api | LOGTO_CLIENT=true & M2M 기능 사용 시 |
⚠️ 중요:
- Stateless 모드(기본값):
LOGTO_JWKS_URI와LOGTO_AUTH_ISSUER만 설정하면 토큰 검증 기능을 사용할 수 있습니다.- Stateful 모드:
LOGTO_CLIENT=true로 설정하고 OAuth/M2M 관련 환경변수를 추가로 설정해야 합니다.
🧩 확장/커스터마이징
- 외부 로깅 시스템과의 연동이 필요할 경우, 원하는 로거 모듈/토큰을 DI로 주입
- 각 서비스별로 NestJS의 DI/Provider 패턴을 그대로 활용 가능
- 필요시 각 서비스/가드를 직접 DI 받아 커스텀 비즈니스 로직 구현 가능
❓ FAQ
Q. 로거 토큰이 다르면 어떻게 하나요?
→forLogger의 두 번째 인자로 원하는 토큰(Symbol)을 넘기면 됩니다.Q. 환경변수 기반 설정은 어떻게 하나요?
→ 각 서비스는 DI로ConfigService를 받아 환경변수 기반으로 설정을 자동 주입받습니다.Q. 인증/권한 외에 사용자/역할 관리도 가능한가요?
→ 네,LogtoM2MClient를 통해 사용자/역할 생성, 조회, 수정, 삭제, 할당 등 모든 관리가 가능합니다.Q. Stateless 모드와 Stateful 모드의 차이는 무엇인가요?
→ Stateless 모드는 토큰 검증만 필요한 API 서버에 적합하며, 최소한의 환경변수(LOGTO_JWKS_URI,LOGTO_AUTH_ISSUER)만 필요합니다.
→ Stateful 모드는LOGTO_CLIENT=true로 설정하여 로그인/로그아웃, M2M 통신 등 클라이언트 기능을 모두 사용할 수 있습니다.Q. global 파라미터는 언제 사용하나요?
→forLogger의 세 번째 파라미터를true로 설정하면 LogtoModule이 글로벌 모듈로 등록되어, 다른 모듈에서 별도의 import 없이 Logto 서비스들을 사용할 수 있습니다.Q. 토큰 검증만 필요한데 모든 환경변수를 설정해야 하나요?
→ 아니요. 토큰 검증만 필요하다면LOGTO_JWKS_URI와LOGTO_AUTH_ISSUER만 설정하면 됩니다.LOGTO_CLIENT를 설정하지 않거나false로 설정하면 자동으로 Stateless 모드가 활성화됩니다.
🏷️ 내보내는 토큰/서비스
OAuthClientToken,LogtoM2MClientToken,LogtoTokenVerifierToken,LogtoLoginSessionToken,LogtoTokenGuard- 각 서비스/가드는 NestJS DI로 바로 주입받아 사용 가능
📚 참고
📝 라이선스
MIT
