9
4

More than 3 years have passed since last update.

IBM Cloud App ID Node.js Webクイックスタート

Posted at

はじめに

IBM Cloud App IDは、ユーザー認証を手軽に組み込めるサービスです。
AWSでいうとCognitoに相当するサービスです。

今回は、IBM Cloudのドキュメントで公開されているNode.js Webクイックスタートを補足&若干の変更を加えてながらやってみたいと思います。

なお、本記事で紹介するソースコードは、ここからダウンロード可能です

環境

ローカルPC上のNode.js(v10.6.3)を利用します。
Node.js自体のセットアップ手順は割愛します。

クイックスタートでやること

クイックスタートでは、OAuth 2.0 認可コードフローを用いた認証するアプリケーションを作成します。
image.png

画面遷移

具体的な手順の前に、クイックスタートで作成する画面遷移を紹介します。

初期表示

index.htmlの初期表示は以下の通りです。 Loginリンクがあるだけです。
image.png

ログイン画面 by App ID

Loginをクリックすると、App IDが表示するログインフォームへ遷移します(画面はカスタマイズしてませんが)。
フロー図でいうと(3)に相当します。
image.png

ログイン後

ログイン後に表示するindex.htmlは以下の通りです。
ログインユーザーの情報を出力し、かつリンクがLogoutに変更されています。
フロー図でいうと(8)に相当します。
image.png

パスワード忘れ時の救済画面、ユーザー登録画面

なお、ログイン画面で、Forgot password? および Sign upをクリックすると、それぞれ以下の画面へ遷移します。

Forgot password
image.png
Sign up
image.png

App IDの設定

まず、App IDの設定を行います。

IBM Cloudへログイン後、CatalogからApp IDを検索します。
image.png

次画面で、RegionおよびPlanを選択し、Createします
image.png

サービス作成後、左メニューのManage Authenticationを選択し、Authentication Settingsタブを表示させます。
Add web redirect URLsに、認可コード取得後のリダイレクトURLを入力します。前述のフロー図では(5)のリダイレクト先になります。

image.png

サンプルユーザーを登録します。
左メニューのCloud Directory -> Userを選択し、次にCreate Userをクリックします
以下の画面が表示されるので、必要な情報を入力し、Saveします。
image.png

Save後、ユーザ一覧が表示されます。
image.png

最後に、Applicationを登録します。
Applicationは、OAuthクライアントを表す概念で、フロー図ではアプリ・サーバーが相当します。
左メニューのApplicationを選択し、Add Applicationをクリックし、必要情報を入力します。
今回はサーバー側で画面制御をするので、Typeは「Regular web application」を選択します。

image.png

登録後、一覧が表示されます。
以降のステップで、詳細情報が必要になるので確認しましょう。
image.png

Node.jsの設定

package.jsonの作成および、アプリに必要なpackageをインストールします。

まずはpackageから。
App IDのNode.js SDKを利用します。
このSDKは、PassportのStrategyとして機能します。
また、Passportはログイン結果をセッションに保存するので、セッション管理の仕組みとしてexpress-sessionも必要になります。

npm install --save express express-session passport log4js pug
npm install --save ibmcloud-appid

package.jsonも作成しましょう。
mainで起動するファイル名は、app.jsとします。

npm init

==省略==

package name: (node_appid) node-appid
version: (1.0.0)
description:
entry point: (index.js) app.js
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /work/node_appid/package.json:

{
  "name": "node-appid",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "dependencies": {
    "express": "^4.17.1",
    "express-session": "^1.17.0",
    "ibmcloud-appid": "^6.0.2",
    "log4js": "^6.1.0",
    "passport": "^0.4.0",
    "pug": "^2.0.4"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Is this OK? (yes) yes

app.jsの作成

パーツごとに解説を交えながら、コーディング内容を記載します。

初期化

まずは、初期化部分です。
ここで、WebAppStrategyの初期化で設定している各種値は、App IDでApplication登録時に取得した値および、Authentication Settingsで登録したRedirect URLになります。
また、express-sessionのデフォルトストアはメモリーなので、このままではスケールアウトはできないことに注意してください。スケールさせるためには、Redis等の外部サービスを保管先に指定する必要があります(本記事では触れません)。

app.js(初期化)
const express = require('express');                                 // https://www.npmjs.com/package/express
const log4js = require('log4js');                                   // https://www.npmjs.com/package/log4js
const session = require('express-session');                            // https://www.npmjs.com/package/express-session
const passport = require('passport');                                // https://www.npmjs.com/package/passport
const WebAppStrategy = require('ibmcloud-appid').WebAppStrategy;    // https://www.npmjs.com/package/ibmcloud-appid

// logger
var logger = log4js.getLogger('testApp');

// setup express-session. default store is memory
const app = express(); app.use(session({
  secret: 'hogehoge',
  resave: false,
  saveUninitialized: false
}));

// setup passport
app.use(passport.initialize()); // initialization is mandatory
app.use(passport.session());  // use session
passport.serializeUser((user, cb) => cb(null, user));
passport.deserializeUser((user, cb) => cb(null, user));
passport.use(new WebAppStrategy({ // WebAppStragegy is passport Strategy
  tenantId: "xxxxxxxx-xxxx-4460-xxxx-750f7a624e01",
  clientId: "xxxxxxxx-xxxx-4c42-xxxx-bb4765e130e7",
  secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  oauthServerUrl: "https://jp-tok.appid.cloud.ibm.com/oauth/v4/xxxxxxxxxx",
  redirectUri: "http://localhost:3000/appid/callback"
}));

ログイン

認証を行うには、passport.authenticateを利用します。
passportは、具体的な認証手段の実装であるStrategyをauthenticateの第1引数に設定します。
ここで設定するのが、App IDのSDKの1機能であるWebAppStrategyです。

WebAppStrategyのパラメーターですが、Login成功時のRedirect先は、index.htmlへ戻したいので/を設定しています。
また、ログイン有無に関係なく、ログインフォームを表示させたいのでforceLogin: trueを設定します。

この処理は、フロー図の(2)と(8)に相当します。

app.js(login)
// Login
app.get('/appid/login',
  passport.authenticate(WebAppStrategy.STRATEGY_NAME, {
    successRedirect: '/',
    forceLogin: true
}));

Callback

基本的には、passport.authenticateを実行するだけです。
認可コードをインプットに、アクセストークンを取得してくれます。
フロー図で言うと、(5)から始まり(6)、(7)に相当します。

処理の前半は、認可コードをログ出力しているだけで、必須ではありません。

app.js(callback)
// Callback from appid
app.get('/appid/callback',
  (req, res, next) => {
    logger.info('call back is called. authorized Code =' + req.query.code);
    next();
  },
  passport.authenticate(WebAppStrategy.STRATEGY_NAME)
);

ログアウト

これも簡単で、WebAppStrategy.logoutを実行するだけです。

app.js(logout)
// Logout
app.get('/appid/logout', (req, res) =>{
  WebAppStrategy.logout(req);
  res.redirect('/');
});

ユーザー情報取得

この処理は、ログイン後に表示するユーザー情報をjsonで応答します。
ログインユーザーの情報は、req.userで取得可能です
未ログインの場合、同オブジェクトは取得できないので、HTTP Status Code 401 Unauthorizedを返しています。

app.js(get_user)
// get user info api
app.get("/api/user",
  (req, res) => {
    if (!req.user) {
      res.status(401);
      res.send('');
    } else {
      logger.info(req);
      res.json({
        user: {
          name: req.user.name,
          email: req.user.email,
          given_name: req.user.given_name,
          family_name: req.user.family_name
        }
      });
    }
  }
);

その他

最後に、index.htmlを保存するディレクトリを登録し、port:3000でリクエストを待ち受けます。

app.js(etc)

// Server static resources
app.use(express.static('./public'));

// Start server
app.listen(3000, () => {
  console.log('Listening on http://localhost:3000');
});

index.html

最後に、index.htmlです。
非常に簡単な内容なので、解説は省略します。

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Simple Node Web App index</title>
</head>

<body>
  <h1>Hello from the app!</h1>
  <a href="/appid/login" id="login">Login</a>
  <a href="/appid/logout" id="logout" style="display: none;">Logout</a>
  <h3 id="name"></h3>
  <h3 id="email"></h3>
  <h3 id="given_name"></h3>
  <h3 id="family_name"></h3>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script>
    $().ready(() => {
      $.get("/api/user")
        .then((res) => {
          $("#name").text("name:" + res.user.name);
          $("#email").text("email:" + res.user.email);
          $("#given_name").text("given_name:" + res.user.given_name);
          $("#family_name").text("family_name:" + res.user.family_name);
          $("#logout").show();
          $("#login").hide();
        });
      });
  </script>
</body>

</html>

まとめ

App IDを使うと画面作成もDBも不要で、簡単にユーザー認証が組み込め流ことが、ご理解いただけたかと思います。

と言いつつ、実業務では、ログイン画面は自作するとか、ユーザー情報に項目追加したいとか、ユーザー管理用画面も別に必要だなど、さまざまなカスタマイズ要件が想定されます。

次回以降、それらの対応例も(可能であれば)投稿していきたいと思います。

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4