OAuth2.0 Node.js の構築

Prev Next

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
      
参考

詳細は oauth2-serverで確認でき、当該ライブラリを使った公式例は 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リストは以下をご参照ください。

機能 説明
generateAccessToken Access Tokenを作成するロジックを担当
generateRefreshToken Refresh Tokenを作成するロジックを担当
generateAuthorizationCode 認可過程で使用される Authorization Codeを作成するロジックを担当
getAccessToken Token発行段階で保存されている Access Tokenを取得して検証するために使用
getRefreshToken Token発行段階で保存されている Refresh Tokenを取得して検証するために使用
getAuthorizationCode Authorization段階で保存されている Authorization Codeを取得して検証するために使用
getClient Client情報(Client ID、Client Secret、Redirect URI)を取得するために使用
saveToken Token発行段階で作成されたトークンを保存
saveAuthorizationCode Authorization段階で作成された 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

因子の説明は次の通りです。

フィールド タイプ 説明
client Object
  • Access Token/Refresh Tokenを作成する Client情報が入った Object
  • getClient() Modelが呼び出され、その Modelで照会された情報を自動的に転送
user Object Access Token/Refresh Tokenを作成する User情報が含まれている
[callback] Function Promiseで返さない場合に使える 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

因子の説明は次の通りです。

フィールド タイプ 説明
clientId String 認証/認可を処理する Clientの Client ID
clientSecret String 認証/認可を処理する Clientの Client Secret
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
client Object Access Token/Refresh Tokenを作成する Client情報が含まれている Object
client.id String 認証/認可を処理する Clientの Client ID
[client.redirectUris] Array<String> 当該 Clientの Redirect URIの配列で、その URIに存在する Redirect URIのみ処理が可能
[client.accessTokenLifetime] Number Access Tokenの期限切れ時間(秒)
[client.refreshTokenLifetime] Number Refresh 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

因子の説明は次の通りです。

フィールド タイプ 説明
code Object 保存する Code
code.authorizationCode String Authorization Code
code.expiresAt Date 期限切れ時間
code.redirectUri String 発行された Authorization Codeに使用される Redirect URI
client Object 発行された Authorization Codeの Client情報
user Object 発行された Authorization Codeの User情報
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
code Object 保存する Code
code.authorizationCode String Authorization Code
code.expiresAt Date 期限切れ時間
code.redirectUri String 発行された Authorization Codeに使用される Redirect URI
code.client Object 発行された Authorization Codeの Client情報
code.client.id String 発行された Authorization Codeの Client ID情報
code.user Object 発行された 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つのモデルは同じ明細を示しています。

Arguments

因子の説明は次の通りです。

フィールド タイプ 説明
client Object
  • Access Token/Refresh Tokenを作成する Client情報が入った Object
  • getClient() Modelが呼び出され、その Modelで照会された情報を自動的に転送
user Object
  • Access Token/Refresh Tokenを作成する User情報が含まれている
  • Authorization Code → Tokenの場合は Model#getAuthorizationCodeで照会された code.userが含まれており、Refresh Token → Tokenの場合は Model#getRefreshTokenで照会された token.userが含まれている
[callback] Function Promiseで返さない場合に使える 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

因子の説明は次の通りです。

フィールド タイプ 説明
refreshToken String 検査する Refresh Token
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
token Object 検査する Access Token
token.refreshToken String getRefreshTokenに渡された Refresh Token
token.refreshTokenExpiresAt Date 期限切れ時間
token.client Object
token.client.id String Access 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

因子の説明は次の通りです。

フィールド タイプ 説明
authorizationCode String 照会する Authorization Code
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
code Object 照会された Code
code.code String Authorization Code
code.expiresAt Date 期限切れ時間
code.redirectUri String 発行された Authorization Codeに使用される Redirect URI
code.client Object 発行された Authorization Codeの Client情報
code.client.id String 発行された Authorization Codeの Client ID情報
code.user Object 発行された Authorization Codeの User情報

saveToken

generateAccessToken/generateRefreshTokenで作成された Tokenを保存するロールを担います。実装によっては、DBまたは In Memory Storageに保存できます。保存時に saveTokenでは Access Token/Refresh Tokenの両方を保存する必要があることにご注意ください。保存時は1つの Modelで、照会時はそれぞれの Modelで照会することになります。

Arguments

因子の説明は次の通りです。

フィールド タイプ 説明
token Object 保存する Token
token.accessToken String 保存する Access Token
token.accessTokenExpiresAt Date Access Tokenの期限切れ時間
token.refreshToken String 保存する Refresh Token
token.refreshTokenExpiresAt Date Refresh Tokenの期限切れ時間
client Object 発行された Tokenの Client情報
user Object 発行された Tokenの User情報
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
token Object 保存した Token
token.accessToken String 保存した Access Token
token.accessTokenExpiresAt Date Access Tokenの期限切れ時間
token.refreshToken String 保存した Refresh Token
token.refreshTokenExpiresAt Date Refresh Tokenの期限切れ時間
token.client Object 発行された Tokenの Client情報
token.client.id String 発行された Tokenの Client ID情報
token.user Object 発行された Tokenの User情報

トークン発行の統合 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を呼び出すことで先に実装した Modelsが内部的に呼び出され、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

因子の説明は次の通りです。

フィールド タイプ 説明
accessToken String 検査する Access Token
[callback] Function Promiseで返さない場合に使える callback関数

Return Value

レスポンスボディの説明は次の通りです。

フィールド タイプ 説明
token Object 検査する Access Token
token.accessToken String getAccessTokenに渡された Access Token
token.accessTokenExpiresAt Date 期限切れ時間
token.client Object
token.client.id String AccessTokenに該当する 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として登録することで、ビジネスロジックと分離して認証できます。