FJCT_ニフクラ mobile backend(mBaaS)お役立ちブログ

スマホアプリ開発にニフクラ mobile backend(mBaaS)。アプリ開発に役立つ情報をおとどけ!

Monaca + NCMBでチャットアプリを作る【その2:認証を作る】

f:id:mbaasdevrel:20201119154758p:plain

MonacaとNCMBで簡単なアプリを作ってみるハンズオンの資料です。今回のテーマはチャットアプリです。この記事では認証周りを実装します。なおコードはNCMBMania/chat_appにて公開しています。

アプリ全体の処理

まず www/js/app.js の中で、NCMBを初期化します。これはアプリ全体に関わる処理なので、JavaScriptファイルに記述しています。

const applicationKey = 'YOUR_APPLICATION_KEY';
const clientKey = 'YOUR_CLIENT_KEY';
const ncmb = new NCMB(applicationKey, clientKey);

なお、app.jsの中ではWebSocket周りを取り扱うChatクラスを作成しています。こちらは後述します。今回はPieSocketを利用しています。

認証の判定

まず大事なのは認証の判定です。今回は次のように実装しています。これは www/chat.html に実装しています。

ons.getScriptPage().onInit = async function() {
  const user = ncmb.User.getCurrentUser();
  if (!user) {
    // 未ログインの場合
    return $('#formNav')[0].pushPage('login.html');
  }
  try {
    // セッションの有効性を確認
    await ncmb.DataStore('Hello').fetch();
  } catch (e) {
    // セッションが無効な場合エラーになるので、その場合はlocalStorageの認証データを削除
    localStorage.removeItem(`NCMB/${ncmb.apikey}/CurrentUser`);
    ncmb.sessionToken = null;
    // ログイン画面に遷移
    return $('#formNav')[0].pushPage('login.html');
  }
}

ここで大事なのは、まず ncmb.User.getCurrentUser() でユーザデータの存在を確認しています。データがなければ未ログイン状態なのでログイン/新規登録画面 login.html へ遷移します。さらにもしデータがあったとしてもセッションが有効かどうかはリクエストしてみないと分かりません。そこで適当なクラス(今回はHello)へリクエストしています。ここでエラーが出る場合はセッションが無効になっています。

この無効な状態だとログアウト処理も失敗します。そこでlocalStorageに保存されているデータを直接削除し、さらにセッショントークンも削除してログイン/新規登録画面に遷移します。

ログイン/新規登録画面

ログイン/新規登録画面(login.html)ではユーザ名とパスワード、そして表示名(displayName)を入力しています。今回は処理を簡略化するため、新規登録とログイン処理を一つにまとめています。

認証処理

認証を実行する処理は #login をクリックした際のイベントで作成していきます。

$('#login').on('click', async e => {
  // この中に実装します
});

まず最初に必要な変数を揃えます。

const userName = $('#userName').val();
const password = $('#password').val();
const displayName = $('#displayName').val();

次にユーザ登録処理を行います。もしすでに登録されている場合はエラーになりますので、try/catchでエラーが出ないようにします。

try {
  await registerUser(displayName, userName, password);
} catch (e) {
}

// 新規ユーザ登録処理です
async function registerUser(displayName, userName, password) {
  const user = new ncmb.User();
  user
    .set('displayName', displayName)
    .set('userName', userName)
    .set('password', password);
  await user.signUpByAccount();
}

そしてユーザ登録が済んだら、そのユーザ名とパスワードでログイン処理を実行します。こちらはエラーをハンドリングします。エラーになるとしたら、パスワードの不一致になるでしょう。ユーザ名は表示してもいいように、Aclを誰でも表示可としています。ログイン処理が完了したら日報入力画面 form.html に戻ります。

この時、チャットでloginイベントを送信しています。すでにチャット画面にいるほかのユーザはこのメッセージを受け取ったら、ユーザ一覧の中に追加しています。

try {
  await loginUser(userName, password);
  // ログインをWebSocketで通知します
  chat.send('login', null);
  $('#formNav')[0].pushPage('form.html');
} catch (e) {
  ons.notification.alert('ログイン失敗しました。ユーザ名、パスワードを確認してください');
  return false;
}

// ユーザログイン処理です
async function loginUser(userName, password) {
  await ncmb.User.login(userName, password);
  const user = ncmb.User.getCurrentUser();
  const acl = new ncmb.Acl();
  acl
    .setPublicReadAccess(true)
    .setUserWriteAccess(user, true);
  await user.set('acl', acl).update();
}

全体の処理は次のようになります。

$('#login').on('click', async e => {
  const userName = $('#userName').val();
  const password = $('#password').val();
  const displayName = $('#displayName').val();

  // すでに登録済みの場合はエラーになるので、try/catchでエラーを潰します
  try {
    await registerUser(displayName, userName, password);
  } catch (e) {
  }
  try {
    // ログイン処理です。
    await loginUser(userName, password);
    // ログインをWebSocketで通知します
    chat.send('login', null);
    // ログインできたら日報入力画面に遷移します
    $('#formNav')[0].pushPage('form.html');
  } catch (e) {
    // エラーの場合ID/パスワード不一致になります
    ons.notification.alert('ログイン失敗しました。ユーザ名、パスワードを確認してください');
    return false;
  }
});

// 新規ユーザ登録処理です
async function registerUser(displayName, userName, password) {
  const user = new ncmb.User();
  user
    .set('displayName', displayName)
    .set('userName', userName)
    .set('password', password);
  await user.signUpByAccount();
}

// ユーザログイン処理です
async function loginUser(userName, password) {
  await ncmb.User.login(userName, password);
  const user = ncmb.User.getCurrentUser();
  const acl = new ncmb.Acl();
  acl
    .setPublicReadAccess(true)
    .setUserWriteAccess(user, true);
  await user.set('acl', acl).update();
}

まとめ

ここまでの処理で、認証されていなかった場合に新規登録/ログイン画面に遷移して、ログインしたら元の画面に戻るまでの処理ができあがりました。次回はチャット画面を作成します。

Monacaでチャットアプリ 記事一覧

中津川 篤司

中津川 篤司

NCMBエヴァンジェリスト。プログラマ、エンジニアとしていくつかの企業で働き、28歳のときに独立。 2004年、まだ情報が少なかったオープンソースソフトの技術ブログ「MOONGIFT」を開設し、毎日情報を発信している。2013年に法人化、ビジネスとエンジニアを結ぶDXエージェンシー「DevRel」活動をスタート。