Framework7でNCMBの認証状態によってルーティングを振り分ける

設定画面をはじめとして、認証完了しているユーザーにだけ見せたい画面は良くあります。そうした画面を作成する際には、認証状態をチェックして、認証していない場合は認証画面に遷移させるというフローは良くあるものです。

![名称未設定 Resized.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/b8902850-e4c8-fb05-c5d7-dbcccac5260a.gif)


Framework7を用いてNCMB（ニフクラ mobile backend）の会員管理機能を用いた場合の処理について紹介します。

## ルーティングでチェックする

各画面で認証状態をチェックするのかと思ったのですが、こういった処理はルーティングで行うのが良いようです。ルーティング（基本は `js/routes.js` に用意します）の beforeEnter を使います。beforeEnterをasyncにしておくのがコツです。

```js
{
	// ↓ 飛び先のURL（認証していたら表示したい画面）
	path: '/settings',
	componentUrl: './pages/settings.html',
	//                   ↓ asyncにしておきます
	beforeEnter: async ({resolve, reject, router}) => {
	},
	// ↓ 認証画面（ログインしていなかったら表示する画面）
  {
    path: '/auth',
    componentUrl: './pages/auth.html',
  },
}
```

beforeEnterの中では、次のように実装します。

```js
beforeEnter: async ({resolve, reject, router}) => {
	// 現在ログインしているユーザー情報を取得
	const currentUser = ncmb.User.getCurrentUser();
	// 認証していない場合はnullになる
	if (!currentUser) {
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
		// 処理完了
		return;
	}
	try {
		// データストアにダミーアクセス
		await ncmb.DataStore('Hello').fetch();
		// アクセスできればresolveを実行（元々指定されている画面を表示）
		resolve();
	} catch (e) {
		// 駄目だった場合は認証情報を削除
		localStorage.removeItem("NCMB/"+ncmb.apikey+"/currentUser");
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
	}
},
```

以下はその実装内容の解説です。

### 現在ログインしているユーザー情報を取得

NCMBで会員情報を使って、現在ログインしているユーザー情報を取得する際には `ncmb.getCurrentUser()` を使います。

```js
// 現在ログインしているユーザー情報を取得
const currentUser = ncmb.User.getCurrentUser();
```

この `currentUser` は認証されていない場合、nullが返ります。もしnullだった場合（認証していない場合）はエラーとして `reject` を実行して、さらにFramework7のルーター機能で認証画面（ `/auth` ）に遷移します。

```js
if (!currentUser) {
	// rejectを実行
	reject();
	// 認証画面に遷移
	router.navigate('/auth');
	// 処理完了
	return;
}
```

### ダミーデータにアクセスする

もし認証情報があった場合ですが、この時点ではローカルデータを復元しただけであってセッションの有効性は確認していません。セッションが無効になっている可能性があるので、データストアへアクセスしてセッションの有効性を確認します（今回はHelloクラスとしていますが、何でもかまいません。存在しないクラス名の方が転送量は少なくて済みます）。

もしセッションが無効だった場合は401エラーになります（ `catch` に流れます）。その場合は、NCMBのログアウトAPIもエラーになってしまうので、強制的にlocalStorage内のデータを削除した上で認証画面に遷移します。

```js
try {
	// データストアにダミーアクセス
	await ncmb.DataStore('Hello').fetch();
	// アクセスできればresolveを実行（ /settingsを表示）
	resolve();
} catch (e) {
	// 駄目だった場合は認証情報を削除
	localStorage.removeItem("NCMB/"+ncmb.apikey+"/currentUser");
	// rejectを実行
	reject();
	// 認証画面に遷移
	router.navigate('/auth');
}
```

## カスタマイズ

この beforeEnter は関数化して、他にも認証必須の画面を作る際に読み込むと良さそうです。

```js
const beforeEnter = async ({resolve, reject, router}) => {
	// 現在ログインしているユーザー情報を取得
	const currentUser = ncmb.User.getCurrentUser();
	// 認証していない場合はnullになる
	if (!currentUser) {
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
		// 処理完了
		return;
	}
	try {
		// データストアにダミーアクセス
		await ncmb.DataStore('Hello').fetch();
		// アクセスできればresolveを実行（元々指定されている画面を表示）
		resolve();
	} catch (e) {
		// 駄目だった場合は認証情報を削除
		localStorage.removeItem("NCMB/"+ncmb.apikey+"/currentUser");
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
	}
};

const routes = [
  {
    path: '/',
    url: './index.html',
  },
  {
    path: '/auth',
    componentUrl: './pages/auth.html',
  },
  {
    path: '/settings',
    componentUrl: './pages/settings.html',
    beforeEnter, // 認証必須の時に読み込む
  },
  {
    path: '(.*)',
    url: './pages/404.html',
  },
];
```

## まとめ

各画面で `$f7router` を使ってもリダイレクトはできなかったのでご注意ください。アプリの場合はセッションの有効性チェックが必須だと思うので、忘れずに行ってください。

認証機能自体は拙作の[認証UIコンポーネント](https://qiita.com/goofmint/items/6dc28141da611426abc4)を使うと、ページを読み込むだけで実現できます。ぜひ合わせてご利用ください。

[mBaaSでサーバー開発不要！ | ニフクラ mobile backend](https://mbaas.nifcloud.com/)