Google OAuthを使用したAndroidでのセキュアなログインの作成
Published: 2024-03-11 00:53:48

2022年1月現在、45億5,000万人が[少なくとも1つのソーシャル・メディア・プロフィールを持って](https://www.statista.com/statistics/278414/number-of-worldwide-social-network-users/)いると推定されており、大半のアプリケーションが、ユーザーが簡単に参加できる方法として、ある種のソーシャル・サインインを持っていることは驚くことではない。これは、ユーザー・エクスペリエンスを向上させる鍵となる。開発の観点から見ると、フェイスブック、ツイッター、グーグルのようなソーシャルサインイン・プロバイダーには大きな共通点がある。その共通のパズルのピースが**OAuth**であり、より具体的には**OAuth 2.0**である。しかし、単にOAuthを使用するだけではなく、スコープ、OAuthクライアントID、OAuth同意画面について正しく理解し、最適な実装を行う必要があります。

認証（Authentication）と認可（Authorization）の違いは何ですか？
---------------------------------------------

[認証](https://www.pubnub.com/learn/glossary/what-is-authentication/)(authn)とは、ユーザーを識別または検証するプロセスを指し、この文脈ではGoogle OAuthを介して行うことができます。一方、[認可](https://www.pubnub.com/learn/glossary/what-is-authorization/)(authz) は、認証されたユーザが特定のリソースにアクセスするために_必要な権限を持って_いるかどうかに関係します。OAuthのパーミッションを定義するスコープが重要な役割を果たします。

authnとauthzの両方は密接に関連しており、通常OAuth 2.0のプロセスの中で見られます。どちらのプロセスでも、[クライアント ID とクライアントシークレットを](https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/)保持するサーバーが必要になりますが、これは Google Cloud を使って管理できます。

Authn と Authz の OAuth への適合性
---------------------------

**OAuthは厳密には認可のための標準**ですが、開発者の間で大きな誤解があるのは、Authn（認証）がその一部であるということです。OAuth 2.0は、特にGoogleのようなソーシャル・プラットフォームでは、ネイティブ・テクノロジー、Javascript、Kotlinを組み合わせて処理を連続して実行するauthnも取り入れている。

ここにプロセスの概要を示す：

![](https://www.pubnub.com/cdn/3prze68gbwl1/asset-17suaysk1qa1jgb/a9cc177f71949ce3ab9e9117b59323be/Oauth-Flow.png)

見ての通り、2つの主要なコンポーネントがある。最初に、ユーザーから必要な承認を得る必要がある。これはユーザーがサインインしている場合に行われ、多くのAPIはここでopenidを使用してOAuthの認証部分と結びつける。ユーザーアカウントの権限から許可を得たら、リソースの所有者と通信を開始することができます。認可サーバーにグラントを送信すると、アクセストークンが返ってきます。このアクセストークンを使って、ユーザーに関連する必要な情報にアクセスすることができます。

OAuth2.0を実装する際には、正しいリダイレクトURIとコールバックを設定することが重要です。これらのパラメータに少しでもミスがあると、authnとauthzのプロセスが失敗する可能性があるからです。さらに、セキュリティとユーザー体験を向上させる一環として、GoogleはOAuth 2.0用のクローム拡張機能を提供しています。

AndroidでのGoogleサインインによる認証
-------------------------

Androidのアプリケーション、特にGoogleサインインによる認証は、一般的なセキュリティ対策です。しかし、単にAndroidアプリケーションにGoogleを追加するだけではなく、OAuthを理解し、SDKとGoogle APIを掘り下げることから始まります。

Google Sign-inを始めるにあたり、サーバーとなるPubNubの機能である[Functionsを](https://www.pubnub.com/docs/functions/overview)使用する。これらの関数を使うことで、OAuthプロセスにおける通知の実装がよりシンプルになる。OAuthプロセスのコードスニペットもGitHubにあります。

また、[Google APIアカウントを](https://developers.google.com/identity/sign-in/android/start-integrating)正しいパッケージ名で設定し、OAuthクライアントIDとOAuth同意画面を設定していることを確認してください。

次に、Android アプリケーションに Google を追加してサインイン処理を開始できるようにします。まず、プロジェクトレベルのGradleファイルに`repositories { google() }という`行があることを確認してください。これでアプリケーションがGoogle SDKを利用できるようになります。

次に、アプリレベルの Gradle ファイルに移動し、Google Play services auth 依存関係を更新します。2022年1月現在、最新バージョンは`com.google.android.gms:play-services-auth:19.2.0`です。

```js
    implementation 'com.google.android.gms:play-services-auth:19.2.0'
```

さて、更新された依存関係がプロジェクトに追加されましたが、サインインプロセスはまだ実行できません。Androidマニフェストファイルを編集して、Firebaseに関するインターネットパーミッションを追加する必要があります：

```js
    <uses-permission android:name="android.permission.INTERNET" />
```

### Google サインインボタンの追加

この段階はかなり簡単です。サインインコンポーネントを`activity_main.xml`ファイルに追加します。サインインボタンはGoogleによってスタイリングされますが、念のため[Googleのブランドガイダンスを](https://about.google/brand-resource-center/)確認してください。デスクトップアプリでは優れたユーザーエクスペリエンスが重要であり、これにはサインインボタンで使用されるスタイリングや識別子が含まれることを忘れないでください。

```js
    <com.google.android.gms.common.SignInButton
                android:id="@+id/sign_in_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
                app:layout_constraintVertical_bias="0.665"
    />
```

Androidアプリでは、Googleサインインによる認証は一般的なセキュリティ対策です。ここでは、このプロセスが`MainActivityで`どのように動作するかの例を示します。このスニペットは、Kotlinを使ってGoogleサインインを実装するためのガイドとなります。

```js
class MainActivity : AppCompatActivity() {
    private val RC_SIGN_IN = 9001
    private var mGoogleSignInClient: GoogleSignInClient? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.server_client_id))
            .requestEmail()
            .build()
        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
    }
    public override fun onStart() {
        super.onStart()
        val mGmailSignIn = findViewById<SignInButton>(R.id.sign_in_button)
        val account = GoogleSignIn.getLastSignedInAccount(this)
        Log.w("Sign In: ", account.toString())
        mGmailSignIn.setOnClickListener {
                signIn()
        }
    }
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == RC_SIGN_IN) {
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            handleSignInResult(task)
        }
    }
    private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
        Log.w("Sign In: ", completedTask.toString())
        try {
            val account = completedTask.getResult(ApiException::class.java)
            val idToken = account!!.idToken
            Log.w("Sign In: ", idToken.toString())
            authCheck(idToken.toString())
        } catch (e: ApiException) {
            Log.w("Sign In: ", "signInResult:failed code=" + e.statusCode)
        }
    }
    private fun signIn() {
        val signInIntent = mGoogleSignInClient!!.signInIntent
        startActivityForResult(signInIntent, RC_SIGN_IN)
    }
    private fun authCheck(token: String) {
        // Instantiate the RequestQueue.
        val queue = newRequestQueue(this)
        val url = "https://pubsub.pubnub.com/v1/blocks/sub-key/sub-c-87dbd99c-e470-11e8-8d80-3ee0fe19ec50/google?token=$token"
        // Request a string response from the provided URL.
        val stringRequest = StringRequest(
            Request.Method.GET, url,
            Response.Listener<String> { response ->
                // Display the first 500 characters of the response string.
                Log.d("Response: ", response)
            },
            Response.ErrorListener {Log.d("Response: ", "Didn't Work!")})
        // Add the request to the RequestQueue.
        queue.add(stringRequest)
    }
}
```

上記のコードで、あなたのアプリはユーザーを認証・検証できるようになりました！認証に成功したら、次はアクセストークンを取得します。このプロセスは前のものとよく似ていますが、今回はトークンの URL -`https://www.googleapis.com/oauth2/v4/token` に`HTTP POST`リクエストを送信します。これはJavaScriptを使って行うことができ、コードはGitHubにありますので参考にしてください。

あなたのPubNubファンクションは以下のようになるでしょう：

```js
export default (request, response) => {
    const pubnub = require('pubnub');
    const xhr = require('xhr');
    let headersObject = request.headers;
    let paramsObject = request.params;
    let methodString = request.method;
    let bodyString = request.body;
    // Set the status code - by default it would return 200
    response.status = 200;
    // Set the headers the way you like
    
    var client_id = "your google client id"
    var client_secret = "your google client secret";
    var redirect = "https://pubsub.pubnub.com/v1/blocks/sub-key/sub-c-87dbd99c-e470-11e8-8d80-3ee0fe19ec50/Auth";
    
    response.headers['X-Custom-Header'] = 'CustomHeaderValue';
    var dataString = `client_id=${client_id}&client_secret=${client_secret}&redirect_uri=${redirect}&grant_type=authorization_code&code=CODE`;
    var url = `https://www.googleapis.com/oauth2/v4/token${dataString}`;
    var options = {
        "method": 'POST',
        "body": dataString,
    };
    
    console.log(dataString);
    return xhr.fetch(url, options).then((x) => {
        const body = JSON.parse(x.body);
        console.log(body);
         return response.send(body);
    }).catch((err) => {
        // console.log(err)
        return response.send("Malformed JSON body.");
    });
};
```

この[コードを](https://developers.google.com/identity/sign-in/android/backend-auth) `MainActivity.kt`ファイルに追加することで実現できます：

```js
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
    GoogleSignInAccount account = task.getResult(ApiException.class);
    String authCode = account.getServerAuthCode();
    // Show signed-un UI
    updateUI(account);
    // TODO(developer): send code to server and exchange for access/refresh/ID tokens
} catch (ApiException e) {
    Log.w(TAG, "Sign-in failed", e);
    updateUI(null);
}
```

![](https://www.pubnub.com/cdn/3prze68gbwl1/asset-17suaysk1qa1jgc/58eca79bde501d62117171964a0f2a36/Google-Oauth-Sign-in.gif?w=700&h=550)

これで、あなたのAndroidアプリにOAuth 2.0を使ったGoogleサインインを実装し、ユーザーエクスペリエンスをスムーズにし、アプリを安全に保つことができます。

オープンなインターネットでアプリケーションをテストした後、問題なくサインアッププロセス全体をナビゲートできるはずです。このプロセスには、アプリに設定できる様々なスコープ（権限）が付属している認証用のGoogle OAuthが関係するかもしれません。ユーザーエクスペリエンスはシームレスであるべきだということを忘れないでください。

さて、ここまででOAuthクライアントIDベースの認証と認可の違いについて理解が深まったと思います。この知識をもとに、OAuthの同意画面を通してユーザーがサインインした後に何ができるかを調べ始めましょう。

クライアント側 SDK のパッケージ名は、JavaScript、Kotlin、または別のプラットフォームのネイティブであっても、Google Cloud プロジェクトに登録されているものと一致する必要があります。このページでアクセスできない外部コンテンツがある場合は、[https://pubsub-quickstart-app.pubnub.com/signup](https://pubsub-quickstart-app.pubnub.com/signup) から表示することもできます。このページでは、OAuthに必要なリダイレクトURIとコールバックパラメータの正しい使い方を示す必要があります。

**PubNubの支援が必要ですか？**
--------------------

PubNubのプラットフォームは、Webアプリ、モバイルアプリ、デスクトップアプリ、IoTデバイス向けにリアルタイムのインタラクティブ機能を構築、配信、管理するためのリソースを開発者に提供します。高度なインタラクションのために、通知でユーザーを巻き込んだり、ユニークな識別子を持つプライベートFirebaseアプリケーションを作成することができます。

当社のプラットフォームのバックボーンは、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。Google Cloudサービスを採用し、Chromeと統合することでさらに機能を強化し、世界中に15以上の拠点を持ち、8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、トラフィックの急増による停止、同時実行数の制限、遅延の問題を心配する必要はありません。

### **PubNubの始め方**

[ライブツアーを](https://www.pubnub.com/tour/introduction/)チェックして、5分以内にすべてのPubNub搭載アプリの背後にあるコアコンセプトに慣れてください。OpenIDはこれらのコンセプトの重要な一部となります。[PubNubアカウントに](https://admin.pubnub.com/signup/)サインアップすると、PubNubキーに無料ですぐにアクセスできます。[PubNubのドキュメントは](https://www.pubnub.com/docs)、ユースケースや好みの[SDKに](https://www.pubnub.com/docs)関係なく、詳細なパラメータを持つコードスニペットまたは "スニペット "を含む、インストールとセットアップのプロセスを案内します。

#### 目次

[認証と認可の違いは何ですか？](#h-0)[AuthnとAuthzがOAuthにどのように適合](#h-1)するか[AndroidでGoogleサインインで](#h-2)[認証する](#h-1)[Googleサインインボタンを](#h-3)追加する[PubNubの支援が必要ですか？](#h-4)[PubNubを始めるには](#h-5)[？](#h-4)

PubNubはあなたのお役に立ちますか？
====================

この記事は[PubNub.comに](https://pubnub.com/blog/creating-a-secure-login-on-android-using-google-oauth/)掲載されたものです。

私たちのプラットフォームは、開発者がWebアプリケーション、モバイルアプリケーション、およびIoTデバイスのためのリアルタイムのインタラクティブ機能を構築、配信、管理するのに役立ちます。

私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。

PubNubを体験
---------

[ライブツアーを](https://www.pubnub.com/tour/introduction/)チェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ
------

[PubNubアカウントに](https://admin.pubnub.com/signup/)サインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める
---

[PubNubのドキュメントは](https://www.pubnub.com/docs)、ユースケースや[SDKに](https://www.pubnub.com/docs)関係なく、あなたを立ち上げ、実行することができます。