【Flutter】Google APIsで始めるOAuth2.0 ⭐️
Overview
OAuth2.0に準拠した認証認可をFlutterで実現したい。
サンプルとして、以下処理について調査
- Google Photoへのアクセス権限を認証し実行
- リフレッシュトークンを取得した上で、アクセストークンの更新処理を実装
- そのアクセストークン、リフレッシュトークンを使って、Cloud Functionsからも実行できるように実装
Architecture
Infrastracture: Firebase Firestore
BE: Cloud Functions
Client: Flutter
Approach
- まず
google_sign_in
パッケージを使ったGoogleサインイン - ただ調べたところ、リフレッシュトークンの発行は未対応の様子
- 念の為、内部実装を確認し、リフレッシュトークンの発行処理を追加できるのかも検討
- 最悪、シンプルにAPIをHTTP通信で叩いてGoogleサインイン等々を実装する
Dependencies
goole_sign_in
packageを使ったGoogle SignIn
/// Holds authentication data after sign in.
class GoogleSignInTokenData {
/// Build `GoogleSignInTokenData`.
GoogleSignInTokenData({
this.idToken,
this.accessToken,
this.serverAuthCode,
});
/// An OpenID Connect ID token for the authenticated user.
String? idToken;
/// The OAuth2 access token used to access Google services.
String? accessToken;
/// Server auth code used to access Google Login
String? serverAuthCode;
手順
1. OAuth 2.0クライアントとしてアプリを登録
- OAuthクライアントIDの作成
iOS
バンドルIDのみ必須。
登録後、以下の通り、OAuthクライアントIDが発行される。
Android
パッケージ名とSHA-1 署名証明書フィンガープリントが必須。
2. 各プラットフォーム用の設定を行う
iOS
以下をInfo.plist
に追加
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>[ここにクライアントIDを反転させたモノを記載]</string>
</array>
</dict>
</array>
Android
android/build.graadle
を以下の通り変更。
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// added for google sign in
+ classpath 'com.google.gms:google-services:4.3.15'
}
}
こちらはPlayStoreの認証をする際に必要かと思うので、リリースする場合のみ追加(デバッグ環境で試したいだけであれば不要と推測)
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Added for Google Sign in for Release apps
+ implementation 'com.google.android.gms:play-services-auth:20.4.1'
}
3. Flutterでログイン機能を実装
GoogleSignIn
クラスを生成
1. スコープを設定して//Default definition
GoogleSignIn googleSignIn = GoogleSignIn(
scopes: [
'profile',
'https://www.googleapis.com/auth/photoslibrary',
],
);
//If current device is Web or Android, do not use any parameters except from scopes.
if (kIsWeb || Platform.isAndroid ) {
googleSignIn = GoogleSignIn(
scopes: [
'profile',
'https://www.googleapis.com/auth/photoslibrary', ],
);
}
//If current device IOS or MacOS, We have to declare clientID
//Please, look STEP 2 for how to get Client ID for IOS
if (Platform.isIOS || Platform.isMacOS) {
googleSignIn = GoogleSignIn(
clientId:
"YOUR_CLIENT_ID.apps.googleusercontent.com",
scopes: [
'profile',
'https://www.googleapis.com/auth/photoslibrary',
],
);
}
2. サインイン
signIn()
メソッドでサインイン。戻り値としてGoogleSignInAccount
クラスを返却。
final GoogleSignInAccount? googleAccount = await _googleSignIn.signIn();
GoogleSignInAccountクラス
class GoogleSignInAccount implements GoogleIdentity {
...
final String? displayName;
final String email;
final String id;
final String? photoUrl;
final String? serverAuthCode;
...
}
3. サインアウト
signOut()
メソッドでサインアウト。戻り値に現在のユーザーのGoogleSignInAccount
クラスを返却。
final GoogleSignInAccount? googleAccount = await _googleSignIn.signOut();
オマケ1:サインイン状態の監視
onCurrentUserChanged
で認証状態に応じてGoogleSignInAccount
のStreamを取得する事が可能。
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
// 変更に応じた処理
});
オマケ2:signInSilentlyメソッド
まだサインインしていない場合のみ認証プロセスを発動し、サインインしている場合は認証プロセスを発動しないsignIn
メソッド。ユーザーがsignOut
もしくはdisconnect
の場合のみ認証プロセスに進む。
final GoogleSignInAccount? googleAccount = await _googleSignIn.signInSilently();
オマケ3:disconnectメソッド
サインイン中のユーザーをサインアウトと同時にアプリから切り離すメソッド。google photoへのアクセス許可などを行うとそれ以後は引き続きアクセス権限が保持されるが、disconnect()
を使うと権限が完全にリセットされる。
final GoogleSignInAccount? googleAccount = await _googleSignIn.disconnect();
メモ
- パッケージのドキュメントではFirebaseAuthを使う前提でFirebaseアカウントを作成する様に記載されているが、実際にはFirebaseへの登録で裏で「OAuth 2.0 クライアント」としてアプリが登録されているっぽい。
- その為、Firebase連携を行わずとも直接GCPに「OAuth 2.0 クライアント」として登録しておけば良さそう。
参考
Other Approaches
Packages
flutterのdartチームによる公式パッケージ
oauth2の次点で良さそうなパッケージ(oauth2はディスコンの可能性がある 参考)
refresh tokenなどをクライアント側に保存する場合に活用
FIDO
Google Auth via Cloud Function
Google Photo APIを使う前準備
GoogleSignInのOAuth認証を使って、Google Photo APIを実行する為、まずGoogle Photo APIを使う準備。
手順
1. GCPにてPhotos Library APIを有効にする
2. OAuth同意画面を設定
- UserType → 外部
- アプリ登録の編集
- アプリ情報(必須)
- アプリのロゴ
- アプリのドメイン
- デベロッパーの連絡先情報(必須)
- スコープ → 「スコープを追加または削除」から
.../auth/photoslibrary
を選択 - テストユーザー → テストユーザーのメールアドレスを登録
公開ステータスが「テスト中」に設定されている間は、テストユーザーのみがアプリにアクセスできます。
oauth2
packageを使ったGoogle SignIn
- Authorization Code Grant, Client Credentials Grantと Resource Owner Password Grantに対応
参考
Oauth2.0
OAuth2の4つのアクセス権限付与フロー
-
Authorization Code Grant
(認可コード・グラント) :- 最も一般的なOauth2.0フロー
- ユーザーからの権限委譲を必要とするフロー
- クライアントとリソースオーナーが異なる場合に使われる認可フロー
- Webやモバイルなどユーザーが存在し、クライアント自身とリソースオーナーが異なる為、権限委譲してもらう必要がある場合に使う
-
Client Credentials Grant
(クライアントクレデンシャル・グラント):- クライアントが自身のリソースに対してアクセスする場合に使われる認可フロー
- 自身がリソースオーナーなので、ユーザーなどによる権限委譲が必要ない場合に使う
-
Resource Owner Password Grant
(リソースオーナーパスワード・グラント):- リソースオーナーのユーザー名とパスワードがクライアントによって直接トークンエンドポイントに送信するフロー
- 従来のBasic認証と同じようなフロー
- 利用は推奨されていない
-
implicit Grant
(インプリシット・グラント) :- 認可コードを挟まず、ユーザーの認可後、アクセストークンがそのままクライアントに返されるフロー
- アクセストークン漏洩のリスクがある為、利用は推奨されていない
Authorization Code Grantの流れ
Web、モバイルアプリケーションのクライアント側で実装する一般的なOauth2の認可フローである「Authorization Code Grant (認可コード・グラント)」の流れについて整理
1.
参照