FlutterとSupabaseでgoogle認証【CI/CDパイプラインを用いたApp Distributionへのapkアップロードに挑戦】 Part1

# 概要
google認証機能をSupabaseを用いて、Flutterのアプリに組み込んだ話です。
せっかくなのでまとめてみました。

色々な動画を見てみましたが、ローカル環境で起動したアプリに組み込むケースが多かったです。もう少し実践的にしようと思って、以下の内容まで取り組んでみました。

- **OAuthクライアントID作成、Supabaseの設定、apkファイルをAppDistributionへアップロード（今回はこちら）**
- apkファイルアップロードを手動ではなく、Github Actions経由に切り替え

ボリュームがあるので、2回に分けました。

## スタック
主に関係する技術は以下の通りです。
- **[Flutter(v3.27)](https://docs.flutter.dev/?_gl=1*g40elp*_ga*MTM4NjQwMzc2MS4xNzM2MTE3Mzg2*_ga_04YGWK0175*MTczNjExNzM4Ni4xLjEuMTczNjExNzM5Ni4wLjAuMA)**
  - アプリケーション作成のためのFW
- **[Github Actions](https://docs.github.com/ja/actions)**
  - Flutter資材のデプロイからApp Distributionのデプロイまでのフロー作成
- **[Google Cloud](https://cloud.google.com/docs?_gl=1*rkz2qi*_up*MQ..&gclid=CjwKCAiAqfe8BhBwEiwAsne6gT0X39MRPvYX9uAdayxNfatyD4XhDe6OIsfpSb8tAzJ_Yhd5IAYeKhoCiAgQAvD_BwE&gclsrc=aw.ds&hl=ja)**
  - OAuthクライアント作成に利用 
- **[App Distribution](https://firebase.google.com/docs/app-distribution?hl=ja)**
  - apkファイルデプロイ先
- **[Supabase](https://supabase.com/)**
  - 認証のための実装および、認証のProviderとしてGoogleと連携

## 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cff5aed6-b428-6dcd-fd54-7f4a139a33de.png)

核となる箇所以外はサラッと記載します。（Flutterの実装について、細部まで説明は記載していません。）

## 認証のための実装を確認
認証機能を作ります。
Supabaseとの連携やgoogleサインインのためのライブラリをプロジェクトに追加します。

https://pub.dev/packages/supabase_flutter

https://pub.dev/packages/google_sign_in

実際の処理からこのクラスを呼び出して、認証処理を行います。
関連する実装の詳細は、本記事の最後記載しています。

```dart
/// 認証関係の処理を行うクラス
class AuthService {
  AuthService()
      : _googleSignIn = GoogleSignIn(
          clientId: AppConstants.googleClientId,
          serverClientId: AppConstants.googleWebServerClientId,
        );
  final supabase = Supabase.instance.client;
  final GoogleSignIn _googleSignIn;

  ///サインイン処理
  Future<void> signInWithGoogle() async {
    try {
      final googleUser = await _googleSignIn.signIn();
      final googleAuth = await googleUser!.authentication;
      final accessToken = googleAuth.accessToken;
      final idToken = googleAuth.idToken;

      if (accessToken == null) {
        throw Exception('No Access Token found.');
      }
      if (idToken == null) {
        throw Exception('No ID Token found.');
      }

      await supabase.auth.signInWithIdToken(
        provider: OAuthProvider.google,
        idToken: idToken,
        accessToken: accessToken,
      );
    } catch (error) {
      // print('Googleサインインでエラー: $error');
      rethrow;
    }
  }

  ///サインアウト処理
  Future<void> signOutWithGoogle() async {
    try {
      await supabase.auth.signOut();
      await _googleSignIn.signOut();
    } catch (error) {
      // print('サインアウトでエラー: $error');
      rethrow;
    }
  }
}
```

この中でも重要なのが、以下2カ所です。

**Googleサインインのための情報**
AppConstantsクラスでは環境変数を読み込んで設定しています。
（環境変数を読み込んでいる、と思っていただけると。）
```dart
_googleSignIn = GoogleSignIn(
    clientId: AppConstants.googleClientId,
    serverClientId: AppConstants.googleWebServerClientId,
);
```
**Supabaseの関数を呼び出し、ログイン処理**
supabaseの関数を呼び出して、実際にログインを実行します。
```dart
  await supabase.auth.signInWithIdToken(
    provider: OAuthProvider.google,
    idToken: idToken,
    accessToken: accessToken,
  );
```

## clientIdとserverClientIdの設定
（Supabaseを通じて）AndroidアプリでGoogle認証を行うためには、2つのIDが必要です。
- Web client ID
- Client ID（Android,iOS）
私はAndroidのみ対応しています。後で記載していますが、Client IDに関して、デバッグ用とリリース用、2種類を設定しているので、Web client ID1つと合わせて、トータルで3個になっています。

> Googleは、AndroidでのGoogleサインインを実行するために、AndroidクライアントIDとWebクライアントIDの両方を必要とするようにOAuth認証を設計したそうです。

https://supabase.com/docs/guides/auth/social-login/auth-google?queryGroups=platform&platform=flutter

AndroidクライアントIDとWebクライアントID、それぞれの設定方法を記載していきます。

### SHA-1の準備
いきなり難しそうな単語ができてきました。フィンガープリント、という単語が使われることもありますが、特定のデバイスを特定するために使われるものです。

https://www.videor.co.jp/digestplus/article/41277.html

このフィンガープリントですが、開発用とリリース用、2種類必要です。

https://developers.google.com/android/guides/client-auth?hl=ja

上記リンクを見るといくつか作り方がありますが、私は`keytool`で作成しました。
`keytool`の利用条件として、JREのインストールが必要なので、適宜インストールします。
Ubuntuであれば、以下コマンドでOKです。
```
sudo apt install -y openjdk-21-jre-headless
```
#### keystore作成
##### デバッグ用
アプリを動かしていれば既に生成されています。
`C:\Users\User\.android\debug.keystore`

##### リリース用
aliasは各自で設定をお願いします。
以下コマンドを実行すると、キーストアのパスワードを設定するように求められます。
また、いくつか項目を設定しますが、画像にある通り、何を設定するか決まっていなければ、そこはドットで問題ないようです。

**エイリアスとパスワードの組み合わせは忘れようにしてください！**
**また、リリース用は作成後、`android/app/release.keystore`に配置します。**

:::note warn
**git管理対象外に**
`release.keystore`、について、`android\.gitignore`に`**/*.keystore`と記載されているので、最初からgit管理対象外になっていますが、一応ご確認ください。

:::

```shell
keytool -genkey -v -keystore release.keystore -alias qiita_sample -keyalg RSA -keysize 2048 -validity 10000
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/414cad62-c593-439e-60a6-388662f62ca9.png)

#### SHA-1の値を確認
```
keytool -list -v -keystore release.keystore -alias qiita_sample
```
パスワードを求められるので、入力してください。
以下のように出力されるので、`SHA1`の部分の値をコピーしておいてください。

デバッグ用も同様です。
（デバッグ用のパスワードは特にいじっていなければ、`android`です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/02dae73c-7a20-0e5a-1f30-90d49ab698c8.png)

### OAuthクライアントIDの作成
Google Cloudのコンソールを開きます。
プロジェクトが無い方は適宜作成をお願いします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cf8daadd-8e92-bb16-5f6e-ab1dc652e4cb.png)

#### Webアプリケーション
`承認済みのリダイレクト URI`は、
Supabaseの設定のタイミングで設定します。今は空で問題ないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/97837e43-1ac6-4c3f-dd84-6bbbad9a026c.png)

#### Android
- `パッケージ名`：`AndroidManifest.xml`ファイル内、と記載されていますが、`android/build.gradle`内部の`applicationId`でOK
- `SHA-1 証明書フィンガープリント`：「SHA-1の値を確認」でコピーした値を貼り付けてください。デバッグ用を作成します。（リリース用は、後の工程で自動作成されます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/a8aa02ef-c703-2d86-3c22-b7593d0506e5.png)

作業後、以下のような状態になっていればOKです。
（Android1つ、ウェブアプリケーション1つ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/9793e8a4-5389-82d2-7520-9a3bfaa7a676.png)

この後、Supabaseの設定で各クライアントID、そしてWebアプリケーションのシークレットの値は使用するので、コピーしておいてください。

これで、clientIdとserverClientIdの情報が取得できました。
（リリース用のclientIdはこの後作成）

## App Distribution
apkファイルをアップロードする場所が必要なので、用意します。
#### アプリケーション追加
アプリを追加するところから始めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/ec0f6ed6-b0a5-040a-d176-d316aeb41b46.png)

Androidをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cffbdd1a-3d0f-0032-671f-240a961be555.png)

#### アプリの登録
以下2つの情報を設定して、「登録」をクリックします。
- `Android パッケージ名`：`android/build.gradle`内部の`applicationId`
- デバッグ用の署名証明書 SHA-1：デバッグ用で、取得した値を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/2d302d1e-1b74-4d04-5f6c-2e532b8a5cd5.png)

#### 構成ファイルのダウンロード
は後で行うので、「次へ」クリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/da416552-089a-9cee-fd27-0d3ae8e4afd3.png)

#### Firebase SDK の追加
後ほど、build.gradleの全体を共有するので、そちらで設定内容を確認いただく形でOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/9e9e7a09-65dd-2f5a-27af-f4993bf1e8e6.png)

App Disribtutionのトップ画面に戻りますので、「開始」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/2506df63-5c6c-60cb-e411-b0dd5f28d83a.png)

Apkファイルがアップロードできる状態になりました。

ローカルで以下コマンドを実行して、apkファイルを作成します。ドラッグアンドドロップでapkファイルをアップロードし、ご自身のメールアドレスに送信してください。

お持ちの端末等にダウンロードして、Google認証の処理以外の部分が動いているか、確かめてみてください。
```
flutter build apk
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/776b27bc-bc71-7e3c-1c83-84271c1840fc.png)

#### SHA 証明書フィンガープリントの設定
Google Cloudの方で、OAuthクライアント作成時に設定したSHA-1をここでも使います。

プロジェクトの設定に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/9801f30c-5146-9ae1-9d83-40b184e18422.png)

以下画像の赤枠内で、設定しているように、デバッグ用とリリース用、2つのSHA-1を設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/50766086-2b6d-62d6-7c00-0041f399663a.png)

「フィンガープリントを追加」をクリックすると入力できるので、入力して、「保存」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/e387531e-f667-a827-6d49-6d0ce05890af.png)

ここまで完了しましたら、
先ほどスキップした、構成ファイルのダウンロードを行っておきましょう。
ダウンロード後、`android/app/google-services.json`に配置します。

:::note warn
**git管理対象外に**
`google-services.json`は、git管理対象外にするので、gitignoreに記載を追加してください。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/bcbe3e69-422a-964e-c767-3e652e8821bf.png)

#### OAuth 2.0 クライアント IDの状態確認
SHA-1の設定実施後、Google Cloudに戻ります。
OAuth 2.0 クライアント IDが一つ増えています。赤枠で囲った箇所に記載されている通り、Firebaseによって自動生成されています。
中身を確認してみると、`release.keystore`のSHA-1の値をフィンガープリントの欄に設定されているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/df48a6d1-cf20-ae4c-1966-b21502946ab4.png)

これでOAuth 2.0 クライアント IDは完了です。

#### Authenticationの設定
最後にちょこっと作業します。ウェブクライアントIDとシークレットを設定します。

すでに設定してあるので、「保存」が押せませんが、無効から有効に設定する際はクリックできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/bce5c779-7bab-ea55-1e34-21bf6a0399d6.png)

これでGoogle Cloudの設定はほぼ終わりです。
（残りは、ウェブクライアントの`承認済みのリダイレクト URI`の設定のみ）
続いてSupabaseの話へ進みます。

## Supabaseの設定
プロジェクトが無い方は適宜作成をお願いします。

必要な情報は、Project URLとAPI Keysです。Project Settingから情報を取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cefc0c47-2ad4-d717-2606-38e61615a364.png)

#### OAuthクライアントIDの設定
Google認証に関する設定を行います。
メニューからAuthentication > Provider、クリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/0df93e9d-a518-9292-6fd1-3e667de527f7.png)

Client IDsとClient Secret (for OAuth)を設定します。
OAuthクライアントの作成で、取得できた値を設定していきます。
- `Client IDs`：OAuth 2.0 クライアントのID3つをカンマつなぎで設定（赤線箇所の通り）
- `Client Secret (for OAuth)`：「Webアプリケーション」の箇所で取得したシークレット値を設定
- `Callback URL (for OAuth)`：コピーしてください。この後、Google Cloud側で設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/745e213f-241f-7666-722f-23d4f9f2ee62.png)

####  Callback URL (for OAuth)の設定
Google Cloudのコンソールに戻ります。
作成済のOAuth 2.0 クライアント IDのうち、種類が`Webアプリケーション`のものを選択して、編集画面を開きます。以下画像の、現在赤くなっている箇所に先ほどコピーした値を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/9fa1c905-c62d-73bf-65dd-1170144c1e63.png)

これでSupabaseとGoogle Cloudの設定も完了です！

## 関連する実装と今後
この記事の最後に、認証に関係する実装と画面の情報を記載いたします。

#### 実装
最初に認証処理について記載しましたが、他に必要そうな情報を共有いたします。
（関連するクラスが複数ある関係で、ログイン後の遷移先画面は添付していません。）

- `auth_google_service.dart`：認証処理
- `env.dart`：環境変数の設定（[envied](https://pub.dev/packages/envied)を使った構築）
- `id_keys.dart`：環境変数の読みこみ処理
- `loginPage.dart`：画面と認証処理

<details><summary>コード詳細</summary>

https://gist.github.com/eno-conan/ffaf6ad9f98d135200753e2253742564

</details>

#### 画面
Googleログインボタンをタップすると、アカウント選択画面が表示されます。
選択後、読み込みが始まり（真ん中の画像）、認証が成功してホーム画面が表示されます。
（Starbucks Cloneに挑戦している関係でホーム画面がそれっぽいものになっています。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/6b78452a-64af-7fd5-29c4-6fc422f8e71c.png)

#### 今後

本記事はここまでとさせていただきます。
この後、GithubActionsを用いたapkファイルのアップロードに取り掛かります。

近いうちに、Part2は公開予定です。

## 参考記事
他にもあったかと思いますが、じっくり確認させていただいたものを中心に記載させていただきました。

https://supabase.com/docs/guides/auth/social-login/auth-google

https://supabase.com/docs/reference/dart/auth-onauthstatechange

https://zenn.dev/saisana299/articles/5f9d2426896423#android

https://zenn.dev/tsukatsuka1783/articles/flutter_firebase_auth_google_sign_in

https://stackoverflow.com/questions/78328414/why-both-web-and-android-client-ids-needed-for-google-authn-using-supabase-on-fl

https://zenn.dev/heyhey1028/scraps/15e6aa0cac7d73

https://qiita.com/yass97/items/ee502eba6c8aa5a3b809