
Research
/Security News
Coruna Respawned: Compromised art-template npm Package Leads to iOS Browser Exploit Kit
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.
@point3/logto-module
Advanced tools
NestJS 기반 Logto 인증/권한 통합 모듈 (서버/클라이언트, M2M, 사용자/역할 관리, OAuth, DI 기반 확장성 제공)
@point3-logto-module은 Logto 인증 시스템을 NestJS 환경에서 손쉽게 통합할 수 있도록 설계된 모듈 번들입니다.
OAuth, M2M(Machine-to-Machine), 사용자/역할 관리, 토큰 검증, 인증 가드 등 인증/권한 관련 기능을 일관된 DI 패턴으로 제공합니다.
enableClient 옵션에 따라 두 가지 모드로 동작합니다:
Stateless 모드 (enableClient: false 또는 미설정):
@LogtoProtected() 가드와 LogtoTokenVerifier만 활성화LOGTO_AUTH_ISSUER만 필요 (LOGTO_JWKS_URI는 선택)Stateful 모드 (enableClient: true):
OAuthClient, LogtoM2MClient, LogtoLoginSession 등 모든 클라이언트 기능 활성화LogtoModule.forRoot(options) 또는 LogtoModule.forRootAsync(options) 패턴으로 사용global 옵션을 true로 설정하면 애플리케이션 전체에서 사용 가능한 글로벌 모듈로 등록npm install @point3/logto-module
// 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 {}
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 }),
// Stateless 모드: 토큰 검증만 필요한 경우
logto.module.LogtoModule.forRoot({
global: true,
logger: {
module: MyLoggerModule,
token: MY_LOGGER_TOKEN,
},
}),
// Stateful 모드: 클라이언트 기능이 필요한 경우
// logto.module.LogtoModule.forRoot({
// global: true,
// enableClient: true,
// logger: {
// module: MyLoggerModule,
// token: MY_LOGGER_TOKEN,
// },
// }),
],
})
export class AppModule {}
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } 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 }),
logto.module.LogtoModule.forRootAsync({
global: true,
imports: [MyLoggerModule],
loggerToken: MY_LOGGER_TOKEN,
useFactory: (configService: ConfigService) => ({
enableClient: configService.get('LOGTO_CLIENT') === 'true',
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
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);
// ... 추가 플로우
}
}
⚠️ 중요한 주의사항
requiredScopes와requiredRoles에 사용되는 모든 스코프와 역할은 반드시 Logto 관리 콘솔에서 먼저 정의되어야 합니다.
- 스코프(Scopes): Logto 콘솔의 API Resources → 해당 리소스 → Scopes에서 정의
- 역할(Roles): Logto 콘솔의 User Management → Roles에서 정의
- 역할-스코프 연결: 각 역할에 필요한 스코프들을 Logto 콘솔에서 할당
코드에서 사용하는 스코프/역할 이름이 Logto에 정의되지 않았다면 토큰 검증이 실패합니다.
// 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: '고급 권한이 필요한 데이터' };
}
}
// 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
};
}
}
// 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 {}
// 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;
}
}
// 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: '타입 안전한 역할 기반 접근 제어' };
}
}
| 환경변수명 | 필수 | 설명 | 예시 값 |
|---|---|---|---|
| LOGTO_AUTH_ISSUER | ✅ | JWT 발급자(iss) | https://auth.example.com/oidc |
| LOGTO_JWKS_URI | ✅ | JWT 검증용 JWKS 엔드포인트 (기본값: http://localhost:3001/oidc/jwks) | https://auth.example.com/oidc/jwks |
enableClient: true)| 환경변수명 | 필수 | 설명 | 예시 값 |
|---|---|---|---|
| LOGTO_AUTH_ENDPOINT | ✅ | Logto 인증 서버 OIDC 엔드포인트 | https://auth.example.com/oidc |
| LOGTO_CLIENT_ID | ✅ | OAuth 클라이언트 ID | my-client-id |
| LOGTO_CLIENT_SECRET | ✅ | OAuth 클라이언트 시크릿 | my-client-secret |
| LOGTO_RESOURCES | ✅ | 접근할 리소스 서버 | https://api.example.com |
| LOGTO_SCOPES | ✅ | 요청할 OAuth 스코프 (쉼표로 구분) | openid,profile,email |
| LOGTO_PROMPT | ✅ | OAuth prompt 파라미터 | login |
| LOGTO_REDIRECT_URI | ✅ | 인증 후 리다이렉트될 URI | https://myapp.com/callback |
| LOGTO_SIGN_IN_URI | ✅ | 기본 로그인 URI | https://auth.example.com |
| LOGTO_DASHBOARD_SIGN_IN_URI | ✅ | 대시보드 로그인 URI (선택) | https://dashboard.example.com |
| 환경변수명 | 필수 | 설명 | 예시 값 |
|---|---|---|---|
| LOGTO_M2M_CLIENT_ID | ✅ | M2M 인증용 클라이언트 ID | my-m2m-client-id |
| LOGTO_M2M_CLIENT_SECRET | ✅ | M2M 인증용 클라이언트 시크릿 | my-m2m-client-secret |
| LOGTO_M2M_RESOURCE | ✅ | M2M 인증용 리소스 | https://api.example.com |
| LOGTO_M2M_API_URL | ✅ | M2M API 서버의 base URL | https://api.example.com/api |
⚠️ 중요:
- 필수 환경변수(✅)가 설정되지 않으면 모듈 초기화 시 즉시 에러가 발생합니다.
- Stateless 모드에서는
LOGTO_AUTH_ISSUER만 필수입니다.- Stateful 모드에서는 위 표의 모든 필수 환경변수가 설정되어야 합니다.
Q. 로거 토큰이 다르면 어떻게 하나요?
→ forRoot의 logger.token에 원하는 토큰(Symbol)을 넘기면 됩니다.
Q. 환경변수 기반 설정은 어떻게 하나요?
→ LogtoModule이 내부적으로 ConfigService를 사용하여 환경변수를 자동으로 읽습니다. ConfigModule.forRoot({ isGlobal: true })가 먼저 import되어 있어야 합니다.
Q. 인증/권한 외에 사용자/역할 관리도 가능한가요?
→ 네, LogtoM2MClient를 통해 사용자/역할 생성, 조회, 수정, 삭제, 할당 등 모든 관리가 가능합니다.
Q. Stateless 모드와 Stateful 모드의 차이는 무엇인가요?
→ Stateless 모드는 토큰 검증만 필요한 API 서버에 적합하며, LOGTO_AUTH_ISSUER만 필수입니다.
→ Stateful 모드는 enableClient: true로 설정하여 로그인/로그아웃, M2M 통신 등 클라이언트 기능을 모두 사용할 수 있습니다.
Q. global 옵션은 언제 사용하나요?
→ global: true로 설정하면 LogtoModule이 글로벌 모듈로 등록되어, 다른 모듈에서 별도의 import 없이 Logto 서비스들을 사용할 수 있습니다.
Q. 토큰 검증만 필요한데 모든 환경변수를 설정해야 하나요?
→ 아니요. 토큰 검증만 필요하다면 LOGTO_AUTH_ISSUER만 설정하면 됩니다. enableClient를 설정하지 않거나 false로 설정하면 자동으로 Stateless 모드가 활성화됩니다.
Q. 필수 환경변수가 없으면 어떻게 되나요?
→ 모듈 초기화 시점에 ConfigService.getOrThrow()를 사용하여 즉시 에러가 발생합니다. 이를 통해 런타임 오류를 사전에 방지할 수 있습니다.
OAuthClientToken, LogtoM2MClientToken, LogtoTokenVerifierToken, LogtoLoginSessionToken, LogtoTokenGuardMIT
FAQs
포인트3 내부 logto Authentication 모듈입니다
We found that @point3/logto-module demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.

Company News
As AI accelerates how code is written and shipped, Socket is scaling to protect the software supply chain from the growing wave of attacks targeting open source dependencies.

Company News
Socket is scaling to defend open source against supply chain attacks as AI accelerates software development.