「Cross origin redirect sign-in on Google Chrome M115+ is no longer supported」とは?

2024-07-27

クロスオリジンリダイレクト認証とは、WebサイトAからWebサイトBへユーザーをリダイレクトし、そこでログイン処理を行う認証方式です。この方式は、Firebase Authenticationなどの認証サービスでよく利用されていました。

この変更の影響を受けるのは、主に以下の開発者の方々です。

  • Firebase Authenticationなどの認証サービスを利用して、WebサイトやAndroidアプリのログイン機能を実装している開発者
  • WebViewコンポーネントを使用して、Webサイト内から他のWebサイトへログイン処理を呼び出している開発者

この変更への対策

この変更に対応するためには、以下のいずれかの方法でログイン方式を変更する必要があります。

  • **PKCE(Proof Key for Code Exchange)**を使った認証方式へ移行する
  • GoogleサインインなどのOAuth 2.0ベースの認証方式へ移行する
  • カスタム認証フローを構築する

この変更は、ユーザーのセキュリティを強化するためのものです。クロスオリジンリダイレクト認証は、悪意のあるサイトがユーザーをだまして認証情報を盗み取る可能性があるという脆弱性がありました。




まず、Firebaseプロジェクトを作成し、Androidアプリを登録する必要があります。

依存関係の追加

以下のライブラリをプロジェクトに依存関係として追加します。

implementation 'com.google.android.gms:play-services-auth:20.1.0'
implementation 'androidx.activity:activity-compose:1.5.1'

コードの作成

MainActivity.kt

package com.example.pkceexample

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.GoogleAuthProvider
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SignInScreen()
        }
    }
}

@Composable
fun SignInScreen() {
    var codeVerifier by remember { mutableStateOf("") }
    var codeChallenge by remember { mutableStateOf("") }
    var googleSignInAccount by remember { mutableStateOf<GoogleSignInAccount?>(null) }
    var showLoading by remember { mutableStateOf(false) }

    val auth = FirebaseAuth.getInstance()

    LaunchedEffect(Unit) {
        // コード検証とコード課題を生成
        codeVerifier = generateCodeVerifier()
        codeChallenge = generateCodeChallenge(codeVerifier)
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        if (showLoading) {
            CircularProgressIndicator()
        } else {
            Button(onClick = {
                showLoading = true
                launch {
                    try {
                        val signInIntent = getGoogleSignInIntent(codeChallenge)
                        startActivityForResult(signInIntent, RC_SIGN_IN)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        showLoading = false
                    }
                }
            }) {
                Text("Googleでログイン")
            }
        }
    }

    if (googleSignInAccount != null) {
        // Googleログイン完了後の処理
        launch {
            try {
                val firebaseUser = getFirebaseUser(googleSignInAccount, auth)
                // ログイン成功後の処理
                println("firebaseUser: $firebaseUser")
                showLoading = false
            } catch (e: Exception) {
                e.printStackTrace()
                showLoading = false
            }
        }
    }
}

// コード検証を生成
fun generateCodeVerifier(): String {
    // 43~128文字のランダムな文字列を生成
    return "YOUR_CODE_VERIFIER"
}

// コード課題を生成
fun generateCodeChallenge(codeVerifier: String): String {
    // SHA-256でハッシュ化し、base64urlエンコード
    return "YOUR_CODE_CHALLENGE"
}

// GoogleログインのIntentを取得
fun getGoogleSignInIntent(codeChallenge: String): Intent {
    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(CLIENT_ID)
        .requestScope("profile")
        .requestScope("email")
        .build()
    val signInClient = GoogleSignIn.getClient(this, gso)
    return signInClient.signInIntent(codeChallenge)
}

// ログイン結果を処理
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == RC_SIGN_IN) {
        val task = GoogleSignIn.getSignedInAccountFromIntent(data)
        try {
            val account = task.getResult(ApiException::



PKCEは、より安全でモダンな認証方式です。コード検証とコードチャレンジを使用して、ユーザー認証フローにおけるなりすまし攻撃を防ぎます。

Googleサインイン

Googleサインインは、OAuth 2.0ベースの認証方式で、ユーザーはGoogleアカウントを使用してログインできます。PKCEと同様に、なりすまし攻撃に対する保護機能が強化されています。

カスタム認証フロー

独自の認証フローを構築することもできます。ただし、この方法にはセキュリティ上のリスクが伴うため、十分な注意が必要です。

各方法のメリットとデメリット

方法メリットデメリット
PKCE高いセキュリティ、モダンな方式開発コストがやや高い
Googleサインイン簡単な実装、Googleアカウントとの連携ユーザーがGoogleアカウントを持っていない場合は利用できない
カスタム認証フロー完全なカスタマイズ性セキュリティリスクが高い、開発コストが高い

推奨される方法


java android firebase



AndroidでListViewにおける画像の遅延読み込みのコード例

ListViewはAndroidアプリで頻繁に使用されるUIコンポーネントですが、大量の画像を表示する場合、パフォーマンスが低下する可能性があります。これを回避するために、画像の遅延読み込み(lazy loading)を導入します。遅延読み込みとは、必要なときにのみ画像をロードする手法です。ListViewのスクロール時に表示範囲内の画像のみを読み込むことで、アプリの起動時間を短縮し、ユーザー体験を向上させます。...


Androidにおける横向きモード無効化のプログラミング例の詳細解説

AndroidManifest. xmlファイルでの設定最も一般的な方法は、AndroidManifest. xmlファイルでアクティビティの android:screenOrientation 属性を設定することです。portrait: 縦向きのみ許可...


Androidで画面サイズをピクセル単位で取得する方法

Androidアプリで画面サイズをピクセル単位で取得するには、以下の方法を使用します:DisplayMetricsクラスを使用して、デバイスのディスプレイに関する情報を取得します。WindowManagerクラスを使用して、アクティビティのウィンドウに関する情報を取得します。...


Android ソフトキーボードのプログラム的制御: コード解説

Android アプリケーションにおいて、ソフトキーボードをプログラム的に閉じるまたは隠す方法は、主に InputMethodManager クラスを利用します。このクラスは、入力メソッドの管理を担当するシステムサービスです。EditText インスタンスを取得します。これは、ソフトキーボードを表示する対象となるビューです。...


Android エミュレータの遅さについての解説と高速化方法

Android エミュレータが遅い理由:Android エミュレータは仮想マシン上で Android OS を実行するため、実際のデバイスよりも処理速度が遅くなります。主な原因は以下です。仮想化オーバーヘッド: 仮想化ソフトウェアがハードウェアとゲスト OS (Android) の間で仲介する際に発生するオーバーヘッド。...



java android firebase

Androidアプリでアクティビティの状態を保存する代替方法

Androidアプリでは、ユーザーがアプリを一時停止したり、画面を回転させたりすると、アクティビティが再作成されます。このとき、アクティビティの現在の状態を保持するために、saveInstanceState()メソッドを使用します。オーバーライドする: アクティビティクラスでsaveInstanceState()メソッドをオーバーライドします。


AndroidでTextViewのテキストを水平・垂直方向に中央揃えするコード例

android:gravity属性を使用します。水平方向の中央揃え: android:gravity="center"水平方向の中央揃え: android:gravity="center"TextViewオブジェクトを取得し、setGravityメソッドを使用します。水平方向の中央揃え: textView


Android画面回転時のActivity再起動に関するコード例解説

Androidでは、デバイスの画面が回転すると、デフォルトではActivityが再起動されます。これは、画面の向きが変わった際に、アプリが適切にレイアウトやリソースを調整するためです。レイアウト調整: 画面の向きが変わることで、UI要素の配置やサイズが適切でない場合があるため、再起動してレイアウトを再描画します。


AndroidでBitmapオブジェクトに画像をロードする際のOutOfMemoryErrorについて

OutOfMemoryErrorは、Androidアプリで画像をBitmapオブジェクトにロードする際に発生する一般的な問題です。これは、デバイスのメモリが不足しているため、画像を完全にロードすることができない場合に起こります。画像サイズが大きい: 高解像度またはサイズが非常に大きな画像をロードすると、メモリ不足を引き起こす可能性があります。


AndroidアプリでSQLiteデータベースを使用する方法

SQLite は、軽量で使いやすいオープンソースのデータベースエンジンです。Android には SQLite が標準搭載されているため、追加のライブラリをインストールする必要はありません。SQLite データベースを作成するには、以下の手順が必要です。