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
を作成しそこに格納した。
これが正しいかはわからない。