[ GraphQL ] 공식문서로 공부하기, '인증'

minidoo·2023년 3월 27일
0

GraphQL

목록 보기
3/4
post-thumbnail

공식문서 - 인증

인증 로직을 비즈니스 로직 레이어에 위임하세요

Presentation > Business Logic > Persistence > Database Layer

인증은 주어진 사용자/세션/컨텍스트가 작업을 수행하거나 데이터를 볼 수 있는 권한이 있는지 여부를 나타내는 비즈니스 로직입니다. 예를 들어,

  • "저자만 자신의 초안을 볼 수 있음"

이런 종류의 행동을 강제하는 것은 비즈니스 로직 레이어에서 일어나야합니다. GraphQL 레이어에 다음과 같이 인증 로직을 배치해야합니다. - Guard는 비지니스 로직 레이어에 속한다.

var postType = new GraphQLObjectType({
  name: ‘Post’,
  fields: {
    body: {
      type: GraphQLString,
      resolve: (post, args, context, { rootValue }) => {
        // return the post body only if the user is the post's author
        if (context.user && (context.user.id === post.authorId)) {
          return post.body;
        }
        return null;
      }
    }
  }
});

게시물의 authorId 필드가 현재 사용자의 id 와 같은지 확인하여 "저자가 게시물을 소유" 했는지 확인했습니다. 이렇게하면 문제가 무엇일까요? 이 코드를 서비스의 각 엔드포인트에 복사해야합니다. 모든 인증 로직이 완벽하게 일치하지 않는 경우, 사용자는 사용하는 API에 따라 다른 데이터를 보게될 수도 있습니다. 단일 소스(single source of truth) 인증을 통해 이를 피할 수 있습니다.

GraphQL이나 프로토타이핑을 배울 때는 resolver 내부에 인증 로직을 정의하는 것이 좋습니다. 하지만, 프로덕션 코드의 경우에는 1) 비즈니스 로직 레이어에 인증 로직을 위임하세요. 아래는 그 예입니다.

1) Business Login Layer - provider, guard

// 인증 로직은 postRepository 내부에 있습니다.
var postRepository = require('postRepository');

var postType = new GraphQLObjectType({
  name: ‘Post’,
  fields: {
    body: {
      type: GraphQLString,
      resolve: (post, args, context, { rootValue }) => {
        return postRepository.getBody(context.user, post);
      }
    }
  }
});

위 예제에서, 비즈니스 로직 레이어는 호출자가 user 객체를 제공해야 한다는 것을 알 수 있습니다. GraphQL.js를 사용하는 경우, user 객체는 context 인수 또는 resolver 의 네 번째 인수인 rootValue 에 전달되어져야 합니다.

확실한 토큰이나 API key 대신 완전한 user 객체를 비즈니스 로직 레이어에 전달하는 것이 좋습니다. 이렇게하면 요청 처리 파이프라인의 여러 단계에서 인증 문제를 처리할 수 있습니다.


인증과 인가

인증(Authentication)

사용자의 신원 확인 ex) 로그인, 회원가입

인가(Authorization)

신원이 확인된 사용자에게 리소스에 엑세스할 수 있는 권한 부여

  1. 인증 을 통해 AccessToken을 생성한다. 토큰에는 사용자 정보를 확인할 수 있는 정보가 포함된다.
  2. 사용자는 요청 보낼 때, 토큰을 함께 보낸다.
  3. 서버에서는 사용자가 보낸 토큰을 복호화하고, 복호화된 데이터를 통해 사용자 구별 정보를 얻는다.
  4. 사용자가 권한을 가지고 있으면 해당 요청을 처리하고, 그렇지 않으면 에러를 응답한다.

컨텍스트 레벨 인증

  • 가장 단순하지만 가장 안전한 인증
  • 리졸버가 실행되기 전에 인증이 수행
    • 컨텍스트 개체에는 특정 요청에 대해 모든 리졸버에서 공유되는 정보가 포함
    • 모든 요청에 대해 호출이 되고 요청 정보를 인자로 받기 때문에 인증 토큰을 검증하는 장소로 적합
    • 인증에 성공한다면, 해당 정보를 컨텍스트에 세팅하여 추후 리졸버에 접근할 수 있도록 해줌
  • ex) req.headers.authorization = Bearer 토큰

NestJS 에 적용

  • AuthGuard 미들웨어 사용 → JwtAuthGuard
  • @UseGuards(JwtAuthGuard) 데코레이터 사용하여, JwtAuthGuard 적용
  • Global하게 또는 class 단위로 적용

리졸버 레벨 인증

  • 쿼리 중 일부가 인증 없이도 실행 가능하다면 컨텍스트 레벨 인증처럼 일괄적으로 차단하는 것은 곤란함
    • ex) 클라이언트가 최초에 인증 토큰을 얻기 위해 호출하는 쿼리
  • 특정 부분만 인증해야 하거나 리졸버마다 다른 인증 메커니즘을 구현하려는 경우 유용
  • 리졸버 별로 인증이 수행

인증은 요청당 한 번만 수행하면 되므로 컨텍스트 수준 인증이 더 효율적이고 구현하기 쉽다.

그러나 리졸버마다 다른 수준의 인증이 필요한 경우와 같이 모든 사례에 적합하지 않을 수 있다. 리졸버 레벨 인증은 인증 및 권한 부여를 세밀하게 제어할 수 있지만 유지 관리가 복잡하고 어려울 수 있다.

NestJS 에 적용

  • AuthGuard 미들웨어 사용 → JwtAuthGuard
  • @UseGuards(JwtAuthGuard) 데코레이터 사용하여, JwtAuthGuard 적용
  • Resolver 단위로 적용

리졸버의 레이어는 무엇일까?

리졸버가 프리젠테이션 레이어인지, 비지니스 로직 레이어인지는 코드 구현 방법에 따라 다르다. 레드플랫폼은 리졸버가 프리젠테이션 레이어로 정의되어 개발된 것이다.

0개의 댓글