くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Dart/FlutterのDioで認証をいい感じにする

Dioを使ってAPIリクエストをする際、
APIトークンやJWTなどを自動で設定したいなと思い、
いろいろ調べたときの備忘録(*´ω`*)

Interceptorというrequest/response/errorをinterceptする
機能があるっぽいので、そこで追加するようにすればいいっぽい

認証用のInterceptor

独自のInterceptorクラスを作る場合には、
InterceptorQueuedInterceptorを継承すればOK

Interceptorは並列に実行されるので、
順序性がある場合には、QueuedInterceptorを利用する。

import "package:dio/dio.dart";

/// 現在のアクセストークンの取得
Future<String?> getAccessToken() async {
  return ...
}

/// リフレッシュトークンの取得
Future<String?> getRefreshToken() async {
  return ...
}

/// 認証用のInterceptor
class AuthInterceptor extends QueuedInterceptor {
  AuthInterceptor(this.dio);
  final Dio dio;

  @override
  Future<void> onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) async {
    // Authorizationヘッダーが空のときのみ、自動で設定する
    if (options.headers['Authorization']?.isEmpty ?? true) {
      final accessToken = await getCurrentToken();
      options.headers['Authorization'] = `Bearer $accessToken`;
    }

    return handler.next(options);
  }

  @override
  Future<void> onError(
    DioException err, 
    ErrorInterceptorHandler handler,
  ) async {
    // 期限切れの場合は、強制更新してリトライ
    // 条件はステータスコードで判定(仮)
    if (err.response?.statusCode == 401) {
      final newAccessToken = await getRefreshToken();
      err.requestOptions.headers['Authorization'] = `Bearer $newAccessToken`;
      return handler.resolve(await dio.fetch(err.requestOptions));
    }
    return handler.next(err);
  }
}

Interceptorの使い方

あとは作ったカスタムInterceptorをaddすればOK

import "package:dio/dio.dart";

/// Dioインスタンスの作成
Dio createApiClient() {
  final dio = Dio();
  dio.interceptors.add(AuthInterceptor(dio));
  return dio;
}

Firebase AuthのidTokenを利用する場合

Firebase AuthのidTokenを利用する場合は、こんな感じにすればOK

import 'package:firebase_auth/firebase_auth.dart';

/// 現在のアクセストークンの取得
Future<String?> getAccessToken() async {
  return await FirebaseAuth.instance.currentUser?.getIdToken()
}

/// リフレッシュトークンの取得
Future<String?> getRefreshToken() async {
  return await FirebaseAuth.instance.currentUser?.getIdToken(true)
}

以上!! 便利(*´ω`*)

参考にしたサイトさま