Flutter SDKでNCMBのTwitter認証を行う

NCMBでは多様なプラットフォームに対してSDKを提供しています。公式ではiOS（Swift/Objective-C）、Android（Java/Kotlin）、JavaScript（Web/Node.js/Cordova/Monaca）、Unityがあります。さらにコミュニティSDKとしてRubyやPython、PHPなどのサーバーサイド言語、React NativeやFlutter向けのSDKも開発されています。

今回はそんなコミュニティSDKの一つ、Flutter SDKを使ってTwitter認証を実装します。ソーシャル認証であればパスワードやメールアドレスの管理を行わずに済み、手軽に利用できます。

## Twitterアプリの作成とキーの取得

Twitterの開発者向けサイトにて、アプリを作成します。そしてコンシューマーキーとコンシューマーシークレットキーを取得します。また、リダイレクトURLはアプリのURIスキームを設定してください。たとえば `ncmbauth://` になります。OAuthは2.0を有効にしました。

![FireShot_Capture_283_-_20220509113918_Twitter_Developers_-_developer_twitter_com.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/6758cee5-1e25-ffc2-176a-fd573d340c5a.jpeg)

## NCMBの設定

NCMBのSNS連携にて、先ほど作成したコンシューマーキーを設定します。Twitter連携も許可して保存してください。

![FireShot_Capture_284_-_20220509131235_ニフクラ_mobile_backend_-_console_mbaas_nifcloud_com.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/f8e34c93-8171-ac45-3751-be00e30f5d71.jpeg)

## Flutterの実装

ここからはFlutterでアプリを生成後の実装です。

### URIスキームの設定

#### Android

`android/app/src/main/AndroidManifest.xml` を開いて、以下を記述します。ncmbauthという文字列は、あなたのものに置き換えてください。

```xml
<intent-filter>
		<action android:name="android.intent.action.MAIN"/>
		<category android:name="android.intent.category.LAUNCHER"/>
		<category android:name="android.intent.category.BROWSABLE" />
		<!-- 以下を追加 -->
		<data android:scheme="ncmbauth" />
</intent-filter>
```

#### iOS

`ios/Runner/Info.plist` を開いて以下を記述します。こちらもncmbauthという文字列を、あなたのものに置き換えてください。

```xml
<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>CFBundleURLName</key>
		<string></string>
		<key>CFBundleURLSchemes</key>
		<array>
			<string>ncmbauth</string>
		</array>
	</dict>
</array>
```

### 実装について

#### ライブラリ、SDKのインストール

利用するライブラリ、SDKをインストールします。

```
flutter pub add twitter_login
flutter pub add ncmb
```

#### ライブラリのインポート

インストールしたライブラリ、SDKをインポートします。

```dart
import 'package:twitter_login/twitter_login.dart';
import 'package:ncmb/ncmb.dart';
```

#### NCMBの初期化

ニフクラ mobile backendにて取得したアプリケーションキー、クライアントキーを使って初期化します。

```dart
void main() {
  // NCMBの初期化
  NCMB('YOUR_APPLICATION_KEY', 'YOUR_CLIENT_KEY');
  runApp(const MyApp());
}
```

#### ステートクラスの変数

ステートクラスではTwitterのAPIキーなどをインスタンス変数として定義します。URIスキームはTwitterの開発者サイトで定義したものを指定してください。

```dart
class _MyHomePageState extends State<MyHomePage> {
  NCMBUser? _user;
  // TwitterのConsumer Key（APIキー）
  final _apiKey = 'YOUR_TWITTER_API_KEY';
  // TwitterのConsumer Secret Key（APIシークレットキー）
  final _apiSecretKey = 'YOUR_TWITTER_API_SECRET_KEY';
  // アプリのURIスキーム
  final _sheme = 'ncmbauth://';
```

#### ステートクラスの初期化

ステートクラスの初期化 `initState` では認証状態をチェックします。ログインしている場合には `await NCMBUser.currentUser()` でNCMBUserオブジェクトが取得できます。認証していない場合には null が返ってきます。

```dart
@override
void initState() {
	super.initState();
	Future(() async {
		// 現在ログインしているユーザー情報（未ログインの場合はnull）を取得
		final user = await NCMBUser.currentUser();
		setState(() {
			_user = user;
		});
	});
}
```

#### 画面構築

`build` は次のように記述しています。 `_user` の状態によって表示を変えています。そして認証していない場合には `login` 、認証している場合には `logout` を処理するボタンを配置しています。

```dart
@override
Widget build(BuildContext context) {
	return MaterialApp(
		home: Scaffold(
			appBar: AppBar(
				title: const Text('Twitter Login App'),
			),
			body: Center(
					child: _user == null
							// 未ログインの場合
							? TextButton(
									child: const Text('Login With Twitter'),
									onPressed: login,
								)
							// ログインしている場合
							: TextButton(
									child: Text(
											'Logged in by ${_user!.getString('displayName', defaultValue: 'No name')}'),
									onPressed: logout,
								)),
		),
	);
}
```

![Simulator Screen Shot - iPod touch (7th generation) - 2022-05-09 at 13.43.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/08400534-53da-920b-edd5-c302ccf0db8f.png)


#### login処理

ログイン処理です。ちょっと長いですが、TwitterLoginを使ってTwitter認証処理は完了します。その後、ログインステータスをチェックしてログイン成功していない場合（ユーザーキャンセルやエラー）は何もせず終了します。

ログイン成功した場合には、結果データを使ってNCMBでTwitter認証するデータを構築しています。注意点としては `id` を文字列として渡すことでしょう。

Twitter認証ではuserNameなどは自動生成された文字列でユーザーが作成されます。そこで displayName フィールドにTwitterのユーザー名を適用してデータ更新しています。

```dart
// ログイン処理
login() async {
	// Twitterでのログイン情報
	final twitterLogin = TwitterLogin(
		apiKey: _apiKey,
		apiSecretKey: _apiSecretKey,
		redirectURI: _sheme,
	);
	// ログインを実行して結果を受け取る
	final authResult = await twitterLogin.login();
	// 認証成功以外は何もせず終了
	if (authResult.status! != TwitterLoginStatus.loggedIn) return;

	// ログイン成功
	// ユーザーデータは繰り返し使うので取り出す
	final userData = authResult.user!;
	// 認証用データの作成
	final data = {
		'oauth_token': authResult.authToken,
		'oauth_token_secret': authResult.authTokenSecret,
		'oauth_consumer_key': _apiKey,
		'consumer_secret': _apiSecretKey,
		'id': userData.id.toString(), // 文字列で指定します
		'screen_name': userData.screenName,
	};
	// ログイン実行
	var user = await NCMBUser.loginWith('twitter', data);
	// 表示名を追加
	if (user.getString('displayName', defaultValue: '') == '') {
		user.set('displayName', userData.screenName);
		// 更新実行
		await user.save();
	}
	// 表示に反映
	setState(() {
		_user = user;
	});
}
```

![Simulator Screen Shot - iPod touch (7th generation) - 2022-05-09 at 11.04.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/080a0a41-720d-9772-7256-4e4b20828472.png)


#### ログアウト処理

`logout` メソッドは以下のようになります。 `NCMBUser.logout()` で認証データを削除し、 `_user` を null にすることで画面表示を更新しています。

```dart
// ログアウト処理
logout() async {
	await NCMBUser.logout();
	setState(() {
		_user = null;
	});
}
```

## まとめ

Twitterは日本では普及しているサービスなので、ユーザーアカウントを持っている人たちも多いでしょう。また、Facebookと比べると匿名性が高いので、利用に際して心理的な障壁は低いようです。

NCMBを使うことでユーザーごとにデータアクセス権限を制御したり、ソーシャルログイン機能が手軽に実装できます。ぜひお試しください。

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