はじめに
アプリのログイン認証フローについて、別タスクで SFSafariViewController から ASWebAuthenticationSession への移行を進めていました。
その過程で、これまで発生していた Universal Link が期待どおり動作しない問題が、結果的に解消されたため、本記事ではその経緯をまとめます。
なお、本記事で扱う Universal Link の問題は、認証フローを見直した結果として副次的に解消されたものになります。
結論
JS リダイレクト経由で Universal Link を発火させる構成はiOS の仕様上不安定であり、
ASWebAuthenticationSession への移行の結果、Universal Link に依存しない形でこの問題を回避できました。
発生していた問題
元々の構成としては、ログインフローをアプリ内ブラウザ(Custom Tabs / SFSafariViewController)で実装し、ログイン完了後に Universal Link を使ってアプリへコールバックし、認証情報を連携する形をしていました。
具体的には
- ブラウザでのログイン完了後、ユーザーがリンクをクリックして Universal Link を踏むケース
- JSによる自動リダイレクトで Universal Link に遷移させるケース
の両方を想定していました。
しかし、JSによる自動リダイレクト経由の場合のみで、体感でおよそ3回に1回の頻度で Universal Link が発火せず、アプリに戻らずに Safari 上でそのまま遷移してしまうケースが発生しました。
なぜ認証フローを ASWebAuthenticationSession に移行したのか
今回の Universal Link に関する問題に直接対応するためというよりも、以前から進めていた認証フローの見直しの一環として、SFSafariViewController から ASWebAuthenticationSession への移行を行いました。
Web 認証を前提に設計されており、コールバックの受け取りやセキュリティ面も含めて認証フローに適した API となっていることから、長期的な保守性や安定性の観点でも移行する判断としました。
そのため、本件における Universal Link の不安定な挙動は、ASWebAuthenticationSession への移行に伴って結果的に解消されたものになります。
Universal Link をリダイレクト経由で利用することについて
-
Apple の公式フォーラムでも、iOS 18 以降において Universal Link は
「直接叩けば動作するが、リダイレクト経由では期待どおり動作しない」といった報告が挙がっています。 -
公式ドキュメント(TN3155)でもユーザーがリンクをタップするのではなく、リダイレクト経由の場合は Safari を経由する可能性があることも記載されています。
このように、Universal Link をリダイレクト経由で利用する構成自体が、公式には推奨されていないことが分かります。
今回発生していた「JS による自動リダイレクト経由で Universal Link を発火させる」という構成は、この非推奨なパターンに該当していました。
なぜ ASWebAuthenticationSession だと問題にならないのか
ASWebAuthenticationSession では、認証完了後に指定された URL へ遷移する際、
Universal Link を OS にトリガーすることはなく、完了時の URL はセッションのコールバックとして直接アプリに返却される仕様になっています。
そのため、認証完了後に Universal Link をリダイレクト経由で発火させる必要がなく、結果として非推奨な構成を取らずに認証フローを完結させることができます。
ASWebAuthenticationSession では、Universal Link(https コールバック URL)をcallback URL として利用できるのは iOS 17.4 以降となっています。
つまり
- JS やサーバーサイドを含む、自動リダイレクトはそのまま利用できる
- Universal Link の発火可否に影響されない
- 認証結果は必ずセッション経由でアプリに返ってくる
という形になり、今回発生していた問題を回避できました。
補足 Flutter での実装について
本件は Flutter アプリにおける認証フローの改善として対応しました。
Flutter から ASWebAuthenticationSession を利用するため、実装には flutter_web_auth_2 パッケージを使用しました。
本記事では詳細な実装には触れませんが、iOS 側では ASWebAuthenticationSession を用いた認証セッションが開始され、認証完了後の callback URL はパッケージ経由でアプリに返却される構成になっています。
最後に
今回の問題が解消できたのは、
認証フローであったため ASWebAuthenticationSession に移行できた、
という前提があります。
SFSafariViewController 上で「JS 等による自動リダイレクト → Universal Link」という構成を取る場合、iOS の仕様上、これ以上安定させるのは難しいと感じました。
本件について、他にも有効なアプローチをご存知の方がいれば、ご意見いただけると嬉しいです。