# React NativeでMastodon認証を実装した
Table of Contents
Mastodonnのクラインアント用ネイティブアプリをReact NativeとExpoで作成している。
バージョンは下記の通り。
"expo": "~52.0.11", "react-native": "0.76.3",実装自体はできたんだけれど、正直いろいろと理解が足りていないので、整理してみる。
インストールしたPackage
"expo-auth-session": "^6.0.1","expo-secure-store": "^14.0.0"expo-auth-session
ブラウザベースの認証を処理するための API
最初は認証時にWebViewを開いて認証に成功したらリダイレクトURIを用いてアプリ画面に戻すという仕組みを実装しようとしたんだけれど、そもそもリダイレクトURIがわからない。Expoの開発用URLを指定してみたけど、
java.io.IOException: Remote update request not successfulという謎のエラーが出てアプリ側に戻らない。
そしてタスク管理でアプリに戻ると認証が成功しているという謎の現象が発生した。
色々悩んでいたけどexpo-auth-session
を使用したらリダイレクトURIの作成をいい感じにやってくれた。
Expo AuthSessionにはexpo-crypto
と一緒にインストールする必要があるって書いてあったけど、インストールしていない。今のところ問題はないので気にしないでおく。
expo-secure-store
Expo用のローカルストレージ。
// 保存await SecureStore.setItemAsync("key", value);
// 参照await SecureStore.getItemAsync("key");
// 削除await SecureStore.deleteItemAsync("kye");で、一通りの作業ができる。
認証に用いたMastodon API
${instanceUrl}/api/v1/apps${instanceUrl}/oauth/authorize?${instanceUrl}/oauth/token
認証までのフロー
アプリケーションを登録
クライアント アプリケーションを登録APIを叩く。
Bodyにclient_name、redirect_uris、scopes、websiteが必要になる。
client_nameはアプリ名を指定し、redirect_urisはexpo-auth-sessionを使用し作成する。
redirect_uris: AuthSession.makeRedirectUri({ native: "{APPNAME}://redirect",}),scopesは権限を指定し、websiteはアプリのウェブサイトをインプットする。
成功すると、
{ "id": "563419", "name": "Test Application", "website": "https://app.example", "scopes": ["read", "write", "push"], "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob"], "client_id": "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", "client_secret": "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw"}が取得できる。
client_id、client_secretは後ほど使用する。関数をまたいで使用するので、expo-secure-storeに保存した。
認証画面を開く
取得したclient_idを用い、認証URLを作成しブラウザで開く。
その際、expo-web-browserを使用した。Expo WebBrowserだけどインストールした記憶がないので、たぶん標準で入っていた。
react-native-webviewとは異なり、ネイティブなブラウザモーダルを使用するらしい。
使い分けの参考は下記の通り。
| 要素 | expo-web-browser | react-native-webview |
|---|---|---|
| 利用シーン | 外部リンクを簡単に開く場合 | ウェブページをアプリの一部として組み込む場合 |
| 操作性 | 操作不可 | DOM操作やイベントリスニングが可能 |
| UIの統合性 | 外部ブラウザ風のUI(独立性あり) | アプリ内に統合されたUI |
| インストールの必要性 | 不要(Expoに標準搭載) | 必要(個別にインストール) |
| パフォーマンス | 高速(ネイティブブラウザ) | やや劣る可能性あり |
で、expo-web-browserには認証フローのメソッドがある。
const result = await WebBrowser.openAuthSessionAsync( authUrl, // 認証を開始するURL redirectUri, // 認証後にリダイレクトされるURL);上記メソッドを実行すると下記フローが走る。
- 認証プロバイダーとのやり取り:
authUrlに指定したプロバイダー(例: GoogleやTwitter)に接続し、ユーザーが認証情報を入力できる画面を表示。 - リダイレクトハンドリング:
認証が成功すると、プロバイダーはredirectUriにリダイレクト。
アプリはそのリダイレクトURLを受け取り、アクセストークンや認証コードを抽出して利用。 - ネイティブモーダルの使用:
WebBrowser.openAuthSessionAsyncは、ネイティブの認証モーダル(Safari View ControllerやChrome Custom Tabs)を使用するため、認証が安全かつシームレスに行える。
アクセストークンの取得
認証が完了したら、認証結果に含まれるcodeを用い、アクセストークンを取得する。その際、先ほど保存したclient_id、client_secretをBodyに含める。成功後したら認証が成功したということなので、取得したアクセストークンをexpo-secure-storeに保存して、アクセストークンを持っている人用のページにリダイレクトする。
ルートに関係ないディレクトがルートに紐づく問題
app以下にmodelという型フォルダを作成したら、
"./model/App.ts" is missing the required default export. Ensure a React component is exported as default.というエラーが吐き出された。どうやらReact nativeはファイル構造に基づいてルートを自動生成するらしい。
しかしこれはルート判定してほしくない。除外判定の仕方がわからなかったので、appと同階層にsrcを作成しそこに格納した。
これが正しいかはわからない。