OAuth2.0 Node.js 구축
    • PDF

    OAuth2.0 Node.js 구축

    • PDF

    기사 요약

    Classic/VPC 환경에서 이용 가능합니다.

    Node.js 기반의 OAuth2.0 Server에 대한 기초적인 구축 방법을 설명합니다. 가이드를 참고하여 OAuth2.0 서버가 구현되어 있지 않은 서비스에서 기존 회원/인증 시스템과 통합을 실시할 수 있습니다.

    프로젝트 설정

    프로젝트 설정 방법을 설명합니다.

    사용 버전

    사용 버전 정보는 다음과 같습니다.

    • Node.js Latest LTS (2023년 10월 10일 기준)
      • node: 18.18.0
      • npm: 9.8.1

    사용 라이브러리

    사용 라이브러리 정보는 다음과 같습니다.

    • express
    • node-oauth2-server
      • https://github.com/oauthjs/node-oauth2-server
        npm install --save node-oauth2-server
        
    참고

    자세한 내용은 https://oauth2-server.readthedocs.io/en/latest에서 확인할 수 있으며, 해당 라이브러리를 사용한 공식 예시는 https://github.com/14gasher/oauth-example에서 확인할 수 있습니다.

    구축 가이드의 범위

    구축 가이드에서는 간단한 로그인 페이지를 통해서 예시를 설명합니다. 실제 서비스 구현 시 기존에 존재하는 로그인 페이지를 다음 OAuth2.0 Login API와 통합하여 개발합니다.

    참고

    암호화 알고리즘, 토큰 생성 알고리즘 등은 각 서비스 정책에 따라 개발하며, 이 가이드의 범주를 벗어나므로 해당 영역은 주석으로 안내합니다.

    node-oauth2-server

    node-oauth2-server는 사전에 정의된 모델(*OAuth2.0 과정에서 필요한 기능들의 명세)을 구현하고 인증(authenticate), 인가(authorize), 토큰(token)의 3가지 기능을 호출함으로써 구현된 Model의 로직에 맞추어 기능을 제공합니다.
    OAuth2.0에 필요한 기능들이 Model을 통해서 추상화되어 있으며, 해당 내용을 구현하고 주입함으로써 손쉽게 인증/인가/토큰 발급의 기능을 사용할 수 있습니다.

    제공 기능

    제공하는 기능은 다음과 같습니다.

    기능설명
    인증(authenticate)발급된 토큰을 바탕으로 API 인입에서 인증 처리 제공
    인가(authorize)code, password 등의 방식으로 인가 기능 제공
    토큰(token)인가된 사용자에 대하여 Token 발행 및 저장 기능 제공

    라이브러리 Model 리스트

    B2B PRISM Live Studio OAuth2.0 연동 과정에서는 Authorization Code/Refresh Token Grant만을 필요로 하고 있으며, Scope에 대한 기능은 제공하지 않습니다.

    PRISM OAuth2.0에 필요한 Model 목록

    여기에서 다루는 Model 목록은 다음과 같습니다.

    참고

    전체 Model 목록은 다음을 참고해 주십시오.

    기능설명
    generateAccessTokenAccess Token을 생성하는 로직 담당
    generateRefreshTokenRefresh Token을 생성하는 로직 담당
    generateAuthorizationCode인가 과정에서 사용되는 Authorization Code를 생성하는 로직 담당
    getAccessTokenToken 발행 단계에서 저장되어 있는 Access Token을 가져와서 검증하는 데 사용
    getRefreshTokenToken 발행 단계에서 저장되어 있는 Refresh Token을 가져와서 검증하는 데 사용
    getAuthorizationCodeAuthorization 단계에서 저장되어 있는 Authorization Code를 가져와서 검증하는 데 사용
    getClientClient 정보(Client ID, Client Secret, Redirect URI)를 가져오는 데 사용
    saveTokenToken 발행 단계에서 생성된 토큰을 저장
    saveAuthorizationCodeAuthorization 단계에서 생성된 Authorization Code를 저장

    각각의 역할과 기능에 대해서는 실제 구현 예시를 참고해 주십시오. 각 모델별 최신 상세 정보 등은 여기에서 확인할 수 있습니다.
    Model의 구현에 있어 Promise를 사용한 방식 혹은 async-await, callback 등의 방식이 존재하지만, 예시에서는 Promise를 이용한 방식으로 구현합니다. 다음 예시 구문을 확인해 주십시오.

    const model = {
      // We support returning promises.
      getAccessToken: function() {
        return new Promise('works!');
      },
     
      // Or, calling a Node-style callback.
      getAuthorizationCode: function(done) {
        done(null, 'works!');
      },
     
      // Or, using generators.
      getClient: function*() {
        yield somethingAsync();
        return 'works!';
      },
     
      // Or, async/wait (using Babel).
      getUser: async function() {
        await somethingAsync();
        return 'works!';
      }
    };
     
    const OAuth2Server = require('oauth2-server');
    let oauth = new OAuth2Server({model: model});
    

    1. OAuth2.0 Login API (Authorization Code 발급 API)

    Authorization Code 발급 기능을 위해서 구현해야 할 Model은 다음과 같습니다.

    generateAuthorizationCode

    이 기능은 선택 사항(Optional)입니다. 구현하지 않을 경우 40 글자의 code가 자동으로 생성됩니다. 자체적인 발급 알고리즘이 필요한 경우에는 직접 구현합니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    clientObject
    • Access Token/Refresh Token을 생성할 Client 정보가 담긴 Object
    • getClient() Model이 호출되고 해당 Model에서 조회된 정보가 자동으로 전달
    userObjectAccess Token/Refresh Token을 생성할 User 정보가 담겨 있음
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    String(Authorization Code 값) 타입으로 반환됩니다.

    Example Code

    generateAuthorizationCode 예시 구문은 다음과 같습니다.

    {
        generateAuthorizationCode: function(client, user) {
            // AccessToken, RefreshToken을 생성하는 자체적인 로직을 작성
            return new Promise(accessToken);
        }
    }
    

    getClient

    Client ID, Client Secret의 정보로, Redirect URI, Token Lifetime 정보를 가져오는 기능입니다. 구현에 따라 DB 등에서 정보를 조회 후 반환할 수 있습니다. 반환된 정보는 OAuth2Server의 각 기능 내에서 Code/Token 발급 등에서 사용됩니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    clientIdString인증/인가를 처리할 Client의 Client ID
    clientSecretString인증/인가를 처리할 Client의 Client Secret
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    clientObjectAccess Token/Refresh Token을 생성할 Client 정보가 담긴 Object
    client.idString인증/인가를 처리할 Client의 Client ID
    [client.redirectUris]Array<String>해당 Client의 Redirect URI의 배열로, 해당 URI에 존재하는 Redirect URI만 처리가 가능
    [client.accessTokenLifetime]NumberAccess Token의 만료 시간(초)
    [client.refreshTokenLifetime]NumberRefresh Token의 만료 시간(초)

    Example Code

    generateAuthorizationCode 예시 구문은 다음과 같습니다.

    {
        getClient: function(clientId, clientSecret) {
            // DB 등에서 정보를 가져와 client Object를 만들어 반환
            return new Promise(client);
        }
    }
    

    saveAuthorizationCode

    generateAuthorizationCode로 생성된 Authorization Code를 저장하는 역할을 합니다. 구현에 따라 DB 혹은 In Memory Storage에 저장할 수 있습니다. 저장된 Authorization Code를 Token 발행 시 getAuthorizationCode Model에서 꺼내서 유효성을 검증할 수 있습니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    codeObject저장할 Code
    code.authorizationCodeStringAuthorization Code
    code.expiresAtDate만료 시간
    code.redirectUriString발급된 Authorization Code에 사용되는 Redirect URI
    clientObject발급된 Authorization Code의 Client 정보
    userObject발급된 Authorization Code의 User 정보
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    codeObject저장할 Code
    code.authorizationCodeStringAuthorization Code
    code.expiresAtDate만료 시간
    code.redirectUriString발급된 Authorization Code에 사용되는 Redirect URI
    code.clientObject발급된 Authorization Code의 Client 정보
    code.client.idString발급된 Authorization Code의 Client ID 정보
    code.userObject발급된 Authorization Code의 User 정보

    OAuth2.0 Login API (Authorization Code 발급 API) 예시

    authenticate 예시 구문은 다음과 같습니다.

    const oauth = new OAuth2Server({model: ...});  // 앞서 구현된 Model Object를 전달
    
    function authorizeHandler(options) {
        return function(req, res, next) {
            let request = new Request(req);
            let response = new Response(res);
            return oauth.authorize(request, response, options) // oauth.authorize 를 호출함으로써 앞서 구현하였던 Model 들이 내부적으로 호출되고 authorization code 발급을 실시
                .then(function(code) {
                    res.locals.oauth = {code: code};
                    next();
                })
                .catch(function(err) {
                    // handle error condition
                });
        }
    }
    

    2. Token 발급 API

    앞선 단계에서 발급된 Authorization Code를 Access Token/Refresh Token으로 교환/저장하는 기능을 제공합니다. 또는 Refresh Token을 통해 새로운 Access Token을 발급받을 수 있습니다.
    Token 발급 기능을 위해서 구현해야 할 Model은 다음과 같습니다.

    generateAccessToken/generateRefreshToken

    이 기능은 선택 사항(Optional)입니다. 구현하지 않을 경우 40 글자의 토큰이 자동으로 생성됩니다. 자체적인 토큰 발급 알고리즘이 필요한 경우에는 직접 구현합니다. 2가지 Model은 동일한 명세를 나타내고 있습니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    clientObject
    • Access Token/Refresh Token을 생성할 Client 정보가 담긴 Object
    • getClient() Model이 호출되고 해당 Model에서 조회된 정보가 자동으로 전달
    userObject
    • Access Token/Refresh Token을 생성할 User 정보가 담겨 있음
    • Authorization Code → Token 의 경우에는 Model#getAuthorizationCode로 조회된 code.user가 담겨 있고, Refresh Token → Token의 경우에는 Model#getRefreshToken 로 조회된 token.user가 담겨 있음
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    String (Access Token, Refresh Token 값) 타입으로 반환됩니다.

    Example Code

    generateAccessToken 예시 구문은 다음과 같습니다.

    {
        generateAccessToken: function(client, user) {
            // AccessToken, RefreshToken을 생성하는 자체적인 로직을 작성하시면 됩니다.
            return new Promise(accessToken);
        }
    }
    

    getRefreshToken

    사용자로부터 전달된 Refresh Token이 실제로 존재하는 Refresh Token인지 조회하는 용도로 사용됩니다.
    DB 등에서 Refresh Token을 조회할 수 있습니다. Refresh Token이 검증된 후에 Grant Refresh Token의 Token 발급 절차가 이루어집니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    refreshTokenString검사할 Refresh Token
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    tokenObject검사할 Access Token
    token.refreshTokenStringgetRefreshToken으로 전달된 Refresh Token
    token.refreshTokenExpiresAtDate만료 시간
    token.clientObject
    token.client.idStringAccess Token에 해당하는 Client ID

    Example Code

    getAccessToken 예시 구문은 다음과 같습니다.

    {
        getRefreshToken: token => {
           if (!token || token === 'undefined') return false
            return new Promise(resolve => {
                // DB 등에서 전달된 token의 유효성(존재 여부)를 검사 후 응답
            })
        }
    }
    

    getClient

    Authorization Code에서 구현된 getClient를 그대로 사용할 수 있습니다.

    getAuthorizationCode

    saveAuthorizationCode로 저장된 Authorization Code를 가져오는 역할을 합니다. 구현에 따라 DB 혹은 In Memory Storage에서 가져올 수 있습니다. 저장된 AuthorizationCode를 Token 발행 시 getAuthorizationCode Model에서 꺼내서 유효성을 검증할 수 있습니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    authorizationCodeString조회할 Authorization Code
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    codeObject조회된 Code
    code.codeStringAuthorization Code
    code.expiresAtDate만료 시간
    code.redirectUriString발급된 Authorization Code에 사용되는 Redirect URI
    code.clientObject발급된 Authorization Code의 Client 정보
    code.client.idString발급된 Authorization Code의 Client ID 정보
    code.userObject발급된 Authorization Code의 User 정보

    saveToken

    generateAccessToken/generateRefreshToken으로 생성된 Token을 저장하는 역할을 합니다. 구현에 따라 DB 혹은 In Memory Storage에 저장할 수 있습니다. 저장 시 saveToken에서는 Access Token/Refresh Token을 모두 저장해야 한다는 점에 주의해 주십시오. 저장 시에는 하나의 Model에서, 조회 시에는 각각의 Model에서 조회하게 됩니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    tokenObject저장할 Token
    token.accessTokenString저장할 Access Token
    token.accessTokenExpiresAtDateAccess Token의 만료 시간
    token.refreshTokenString저장할 Refresh Token
    token.refreshTokenExpiresAtDateRefresh Token의 만료 시간
    clientObject발급된 Token의 Client 정보
    userObject발급된 Token의 User 정보
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    tokenObject저장한 Token
    token.accessTokenString저장한 Access Token
    token.accessTokenExpiresAtDateAccess Token의 만료 시간
    token.refreshTokenString저장한 Refresh Token
    token.refreshTokenExpiresAtDateRefresh Token의 만료 시간
    token.clientObject발급된 Token의 Client 정보
    token.client.idString발급된 Token의 Client ID 정보
    token.userObject발급된 Token의 User 정보

    Token 발급 통합 Example

    authenticate 예시 구문은 다음과 같습니다.

    const oauth = new OAuth2Server({model: ...});  // 앞서 구현된 Model Object를 전달
     
    // 아래 Handler를 PRISM OAuth2.0 가이드에 따라 /prism/v1/service/oauth2/token Router에서 동작하도록 설정 가능
    function tokenHandler(options) { // options 에는 Token Life Time 등의 옵션 추가 가능 (https://oauth2-server.readthedocs.io/en/latest/api/oauth2-server.html#token-request-response-options-callback)
      return function(req, res, next) {
        let request = new Request(req);
        let response = new Response(res);
        return oauth.token(request, response, options) // oauth.authenticate 를 호출함으로써 앞서 구현한 Model들이 내부적으로 호출되고 authorization code/refresh token -> new token 발급 실시
          .then(function(code) {
            res.locals.oauth = {token: token};
            next();
           })
          .catch(function(err) {
            // handle error condition
          });
      }
    }
    

    예시에서는 oauth.token(..)을 감싸는 Handler를 구현했지만 위 예시의 oauth.token(...)을 API 인증이 필요한 Router 등에서 호출함으로써 PRISM OAuth2.0 연동을 위한 /prism/v1/service/oauth2/token API의 응답으로 사용할 수 있습니다.

    3. Authenticate (인증)

    node-oauth2-server에서는 발급된 Access Token을 이용한 인증 기능 또한 제공합니다. 인증 기능을 위해서 구현해야 할 Model은 getAccessToken입니다.

    getAccessToken Model

    사용자로부터 전달된 accessToken이 실제로 존재하는 accessToken인지 조회하는 용도로 사용됩니다. DB 등에서 accessToken을 조회할 수 있습니다.

    Arguments

    인자에 대한 설명은 다음과 같습니다.

    필드타입설명
    accessTokenString검사할 Access Token
    [callback]FunctionPromise 형태로 반환하지 않을 경우 사용할 수 있는 callback 함수

    Return Value

    응답 바디에 대한 설명은 다음과 같습니다.

    필드타입설명
    tokenObject검사할 Access Token
    token.accessTokenStringgetAccessToken으로 전달된 Access Token
    token.accessTokenExpiresAtDate만료 시간
    token.clientObject
    token.client.idStringAccessToken에 해당하는 Client ID

    Example Code

    getAccessToken 예시 구문은 다음과 같습니다.

    {
        getAccessToken: token => {
           if (!token || token === 'undefined') return false
            return new Promise(resolve => {
                // DB 등에서 전달된 token의 유효성(존재 여부)를 검사 후 응답
            })
        }
    }
    

    authenticate 예시

    authenticate 예시 구문은 다음과 같습니다.

    const oauth = new OAuth2Server({model: ...}); // 앞서 구현된 Model Object를 전달
     
    function authenticateHandler(options) {
      return function(req, res, next) {
        let request = new Request(req);
        let response = new Response(res);
        return oauth.authenticate(request, response, options) // oauth.authenticate 를 호출함으로써 구현했던 getAccessToken이 내부적으로 호출되고 Token의 유효성 검사를 실시
          .then(function(token) {
            res.locals.oauth = {token: token};
            next(); // Access Token이 정상적으로 인증됐고, 다음 MiddleWare로 넘어감
          })
          .catch(function(err) {
            // handle error condition
          });
      }
    }
    

    예시의 oauth.authenticate(...)를 API 인증이 필요한 Router 등에서 Middleware로 등록함으로써 비즈니스 로직과 분리하여 인증이 가능합니다.


    이 문서가 도움이 되었습니까?

    Changing your password will log you out immediately. Use the new password to log back in.
    First name must have atleast 2 characters. Numbers and special characters are not allowed.
    Last name must have atleast 1 characters. Numbers and special characters are not allowed.
    Enter a valid email
    Enter a valid password
    Your profile has been successfully updated.