Build OAuth2.0 Node.js

Prev Next

Available in Classic and VPC

This section describes the basic build method for Node.js-based OAuth2.0 Server. See the guide to integrate existing member and authentication system into services where the OAuth2.0 server is not implemented.

Project configuration

Describes how to configure a project.

Use version

The use version information is as follows:

  • Node.js Latest LTS (as of October 10, 2023)
    • node: 18.18.0
    • npm: 9.8.1

Use library

The use library information is as follows:

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

You can find more information on oauth2-server. For official examples using this library, see https://github.com/14gasher/oauth-example.

Scope of build guide

The build guide is a description of examples through a simple login page. When implementing an actual service, integrate the existing login page with the following OAuth2.0 Login API.

Note

An encryption algorithm, token generation algorithm, and more need to be developed according to the policies of each service. As they are beyond the scope of this guide, those areas are annotated.

node-oauth2-server

Node-oauth2-server implements a predefined model (details for the necessary features in the *OAuth2.0 process) and provides features in line with the logic of the implemented Model by calling the 3 options: authenticate, authorize, and token.
The features required for OAuth2.0 are abstracted through the Model, and implementing and injecting these contents allows you to easily use the features of authentication, authorization, and token issuance.

Provided features

The provided features are as follows:

Feature Description
Authenticate Provides authentication processing in API entry based on the issued token
Authorize Provides the authorization feature using methods such as code and password
Token Provides token issuing and storing features for authorized users

Library Model list

In the B2B PRISM Live Studio OAuth2.0 integration process, only Authorization Code/Refresh Token Grant are required, and the feature for Scope is not provided.

Model list required for PRISM OAuth2.0

The list of Models covered here is as follows:

Note

For the complete list of Models, see the following:

Feature Description
generateAccessToken Responsible for the logic of generating Access Token
generateRefreshToken Responsible for the logic of generating Refresh Token
generateAuthorizationCode Responsible for the logic of generating Authorization Code used in the authorization process
getAccessToken Used to retrieve and validate the Access Token stored at the Token issuance stage
getRefreshToken Used to retrieve and validate the Refresh Token stored at the Token issuance stage
getAuthorizationCode Used to retrieve and validate the Authorization stored at the Token issuance stage
getClient Used to retrieve the Client information (Client ID, Client Secret, Redirect URI)
saveToken Used to save the token created at the Token issuance stage
saveAuthorizationCode Used to save the Authorization Code created at the Authorization stage

For roles and features of each, see the actual implementation examples. You can view more information, including the latest details about each model, here.
While the implementation of Models can be done using various methods such as Promise-based, async-await, callback, and more, we use the Promise-based method in the example. Check the following example syntax:

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 Issuance API)

The Models that need to be implemented for the Authorization Code issuance feature are as follows:

generateAuthorizationCode

This feature is optional. If not implemented, a 40-character code is automatically created. If a custom issuance algorithm is required, implement it directly.

Arguments

The description of the arguments is as follows:

Field Type Description
client Object
  • Object containing information about the Client generating the Access Token/Refresh Token
  • getClient() Model is called, and the information viewed from that Model is automatically forwarded
user Object Contains the information about the user for whom the Access Token/Refresh Token is generated
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

Returns as a String (Authorization Code value) type.

Example Code

The example syntax of generateAuthorizationCode is as follows:

{
    generateAuthorizationCode: function(client, user) {
        // Write custom logic to generate AccessToken, RefreshToken
        return new Promise(accessToken);
    }
}

getClient

This is a feature to retrieve Redirect URI and Token Lifetime information with Client ID and Client Secret information. Depending on the implementation, views information from DBs or other places and returns. The returned information is used in Code and Token issuance within each feature of OAuth2Server.

Arguments

The description of the arguments is as follows:

Field Type Description
clientId String Client ID for the Client handling authentication and authorization
clientSecret String Client Secret for the Client handling authentication and authorization
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
client Object Object containing the information about the Client generating the Access Token/Refresh Token
client.id String Client ID for the Client handling authentication and authorization
[client.redirectUris] Array<String> Array of the Redirect URIs for the Client; only the Redirect URIs in that URLs can be processed
[client.accessTokenLifetime] Number Expiration time of the Access Token (seconds)
[client.refreshTokenLifetime] Number Expiration time of the Refresh Token (seconds)

Example Code

The example syntax of generateAuthorizationCode is as follows:

{
    getClient: function(clientId, clientSecret) {
        // Retrieves information from DB and then creates and returns a Client Object
        return new Promise(client);
    }
}

saveAuthorizationCode

Serves the role of storing the Authorization Code generated by generateAuthorizationCode. Depending on the implementation, can be stored in DB or In Memory Storage. The stored Authorization Code can be retrieved from the getAuthorizationCode Model when issuing a Token to validate its validity.

Arguments

The description of the arguments is as follows:

Field Type Description
code Object Code to be stored
code.authorizationCode String Authorization Code
code.expiresAt Date Expiration time
code.redirectUri String Redirect URI used for the issued Authorization Code
client Object Client information of the issued Authorization Code
user Object User information of the issued Authorization Code
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
code Object Code to be stored
code.authorizationCode String Authorization Code
code.expiresAt Date Expiration time
code.redirectUri String Redirect URI used for the issued Authorization Code
code.client Object Client information of the issued Authorization Code
code.client.id String Client ID of the issued Authorization Code
code.user Object User information of the issued Authorization Code

OAuth2.0 Login API (Authorization Code Issuance API) example

The example syntax of authenticate is as follows:

const oauth = new OAuth2Server({model: ...}); // Forwards the previously implemented Model Object

function authorizeHandler(options) {
    return function(req, res, next) {
        let request = new Request(req);
        let response = new Response(res);
        Calls "return oauth.authorize(request, response, options) // calls oauth.authorize"; the previously implemented Models are internally called and the authorization code is issued
            .then(function(code) {
                res.locals.oauth = {code: code};
                next();
            })
            .catch(function(err) {
                // handle error condition
            });
    }
}

2. Issue Token API

Provides the feature to exchange/store the Authorization Code issued in the previous step for/as Access Token/Refresh Token. Or, it obtains a new Access Token through the Refresh Token.
The Models that need to be implemented for the Token issuance feature are as follows:

generateAccessToken/generateRefreshToken

This feature is optional. If not implemented, a 40-character token is automatically generated. If a custom token issuance algorithm is required, implement it directly. The 2 Models represent the same specification.

Arguments

The description of the arguments is as follows:

Field Type Description
client Object
  • Object containing information about the Client generating the Access Token/Refresh Token
  • getClient() Model is called, and the information viewed from that Model is automatically forwarded
user Object
  • Contains information about the user for whom the Access Token/Refresh Token is generated
  • For Authorization Code → Token, contains code.user viewed by Model#getAuthorizationCode, and for Refresh Token → Token, contains token.user viewed by Model#getRefreshToken
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

Returns as a String (Access Token, Refresh Token value) type.

Example Code

The example syntax for generateAccessToken is as follows:

{
    generateAccessToken: function(client, user) {
        // Write custom logic to generate AccessToken, RefreshToken.
        return new Promise(accessToken);
    }
}

getRefreshToken

Used to view the Refresh Token forwarded from the user, verifying whether the Refresh Token actually exists.
You can view the Refresh Token from DBs and other places. After the Refresh Token is verified, the Grant Refresh Token issuing procedure starts.

Arguments

The description of the arguments is as follows:

Field Type Description
refreshToken String Refresh Token to be checked
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
token Object Access Token to be checked
token.refreshToken String Refresh Token forwarded by getRefreshToken
token.refreshTokenExpiresAt Date Expiration time
token.client Object
token.client.id String Client ID for the Access Token

Example Code

The example syntax for getAccessToken is as follows:

{
    getRefreshToken: token => {
       if (!token || token === 'undefined') return false
        return new Promise(resolve => {
            // Checks the validity (existence) of the token forwarded from DB, and then responds accordingly
        })
    }
}

getClient

You can use the getClient implemented in the Authorization Code as is.

getAuthorizationCode

Serves the role of retrieving the Authorization Code stored by saveAuthorizationCode. Depending on the implementation, can be retrieved from DB or In Memory Storage. The stored AuthorizationCode can be retrieved from the getAuthorizationCode Model when issuing a Token to validate its validity.

Arguments

The description of the arguments is as follows:

Field Type Description
authorizationCode String Authorization Code to be viewed
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
code Object Code viewed
code.code String Authorization Code
code.expiresAt Date Expiration time
code.redirectUri String Redirect URI used for the issued Authorization Code
code.client Object Client information of the issued Authorization Code
code.client.id String Client ID of the issued Authorization Code
code.user Object User information of the issued Authorization Code

saveToken

Serves the role of storing the Token generated by generateAccessToken/generateRefreshToken. Depending on the implementation, can be stored in DB or In Memory Storage. Be careful when saving, as both Access Token and Refresh Token must be saved when saving in saveToken. Both tokens are saved within a single Model, but each token is viewed from individual Models.

Arguments

The description of the arguments is as follows:

Field Type Description
token Object Token to be saved
token.accessToken String Access Token to be saved
token.accessTokenExpiresAt Date Expiration time of the Access Token
token.refreshToken String Refresh Token to be saved
token.refreshTokenExpiresAt Date Expiration time of the Refresh Token
client Object Client information of the issued Token
user Object User information of the issued Token
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
token Object Saved Token
token.accessToken String Saved Access Token
token.accessTokenExpiresAt Date Expiration time of the Access Token
token.refreshToken String Saved Refresh Token
token.refreshTokenExpiresAt Date Expiration time of the Refresh Token
token.client Object Client information of the issued Token
token.client.id String Client ID information of the issued Token
token.user Object User information of the issued Token

Integrating Token issuance example

The example syntax of authenticate is as follows:

const oauth = new OAuth2Server({model: ...}); // Forwards the previously implemented Model Object
 
// Can set the following Handler to operate in /prism/v1/service/oauth2/token Router according to the PRISM OAuth2.0 guide
function tokenHandler(options) { // Options such as Token Life Time can be added to "options" (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);
    Calls "return oauth.token(request, response, options) // oauth.authenticate," the previously implemented Models internally called, and authorization code/refresh token starts to issue a new token
      .then(function(code) {
        res.locals.oauth = {token: token};
        next();
       })
      .catch(function(err) {
        // handle error condition
      });
  }
}

The Handler wrapping "oauth.token(..)" was implemented in the example, but "oauth.token(...)" of the above example can be used as a response for /prism/v1/service/oauth2/token API for PRISM OAuth2.0 integration by calling it in the Router requiring API authentication and so on.

3. Authenticate

node-oauth2-server also provides an authentication feature that uses the Access Token. The Model to be implemented for the authentication feature is getAccessToken.

getAccessToken Model

Used to view the accessToken forwarded by the user, verifying whether the accessToken actually exists. You can view the accessToken from DBs and other places.

Arguments

The description of the arguments is as follows:

Field Type Description
accessToken String Access Token to be checked
[callback] Function Callback function that can be used if not returning in Promise format

Return Value

The following describes the response body.

Field Type Description
token Object Access Token to be checked
token.accessToken String Access Token forwarded by getAccessToken
token.accessTokenExpiresAt Date Expiration time
token.client Object
token.client.id String Client ID for the AccessToken

Example Code

The example syntax for getAccessToken is as follows:

{
    getAccessToken: token => {
       if (!token || token === 'undefined') return false
        return new Promise(resolve => {
            // Checks the validity (existence) of the token forwarded from DB, and then responds accordingly
        })
    }
}

Authenticate example

The example syntax of authenticate is as follows:

const oauth = new OAuth2Server({model: ...}); // Forwards the previously implemented 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) // The implemented getAccessToken is internally called by calling "oauth.authenticate," and Token validation is performed
      .then(function(token) {
        res.locals.oauth = {token: token};
        next(); // The Access Token is successfully authenticated and proceeds to the next MiddleWare
      })
      .catch(function(err) {
        // handle error condition
      });
  }
}

By registering oauth.authenticate(...) as Middleware in Router requiring API authentication, you can separately authentication from business logic.