1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

こちらは エーピーコミュニケーションズ Advent Calendar 2024 の 9 日目の記事となります。
普段何気なく利用しているGoogle認証ですが、開発目線としてはどのような作業が必要になるのか実装方法について試してみました。

結論

非常に簡単な手順で実装することが可能なため、簡易的な認証などに利用する際には非常に便利に使えちゃいます。

作業の流れ

前提条件

Googleアカウントを持っていること。

Google Cloudへのプロジェクト作成

Google Cloudに専用のプロジェクトを作成して、認証機能を設定して行くことになります。

今回はGoogleアカウントについては取得済みであるものとして、プロジェクトの作成から試していきます。

プロジェクト名はOAuthTestとしました。
image.png

作成したプロジェクトに切り替えます。
image.png

OAuth 同意画面の作成

API と サービスをクリックします。
image.png

OAuth 同意画面を選択し、必要事項の入力へと進みます。
ここではWebアプリなどでの実装を想定しているため、User Typeは外部を選択します。
image.png

アプリ情報にアプリ名を入力します。
ここではOAuthTestとしています。
image.png

ユーザー サポートメール、デベロッパーの連絡先情報のメールアドレスはGoogleアカウントのものにします。
作成者のGmailアドレス以外を選択したい場合は、IAMユーザーをプロジェクトに招待することで選択ができます。

アプリ登録の編集のスコープについては他の権限を利用しない場合にはそのままで良いです。
こちらはGoogleカレンダーやDriveなどにアクセスする必要がある場合は必要なスコープを選択することになるのですが、単純な認証だけであれば不要です。
もしこちらのスコープを利用する場合、本番公開の際に非常に手間になる権限利用のイメージ動画を作成する必要が出てくるので注意です。
image.png

認証情報の作成

認証情報から OAuth 2.0 クライアント IDを作成します。
image.png

OAuth クライアント IDを選択します。
image.png

ウェブアプリケーションを選択します。
image.png

開発環境で利用するhttp://localhost:3000を入力します。
image.png

同様に開発環境で利用するhttp://localhost:3000を入力します。
image.png

作成画面からクライアント IDを控えます。
image.png

テスト用認証ページの作成

以下の様なhtmlを作成します。
ここではテストのためファイルに直接取得したクライアント ID情報を埋め込んで作成しますが、セキュリティ的な観点からはトークンのベタ書きは推奨されません。
参考にされる際にはアクセス環境や環境に合わせて承認済みの JavaScript 生成元承認済みのリダイレクト URIの設定も考慮しつつ修正する必要があります。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Google OAuth Simple Test</title>
    <style>
        body {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
        .container {
            text-align: center;
            padding: 20px;
        }
        .user-info {
            margin-top: 20px;
            display: none;
        }
        .user-info img {
            width: 100px;
            height: 100px;
            border-radius: 50%;
            margin-bottom: 10px;
        }
        button {
            padding: 10px 20px;
            margin-top: 20px;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Google OAuth Simple Test</h1>
        <div id="buttonDiv"></div>
        <div id="userInfo" class="user-info">
            <img id="userPicture" alt="User Picture">
            <h2 id="userName"></h2>
            <p id="userEmail"></p>
            <button onclick="handleSignOut()">ログアウト</button>
        </div>
    </div>

    <script src="https://accounts.google.com/gsi/client" async defer></script>
    <script>
        function handleCredentialResponse(response) {
            // JWTをデコード
            const responsePayload = decodeJwtResponse(response.credential);
            
            // ユーザー情報を表示
            document.getElementById('buttonDiv').style.display = 'none';
            document.getElementById('userInfo').style.display = 'block';
            document.getElementById('userPicture').src = responsePayload.picture;
            document.getElementById('userName').textContent = `ようこそ、${responsePayload.name}さん!`;
            document.getElementById('userEmail').textContent = `メールアドレス: ${responsePayload.email}`;
        }

        function decodeJwtResponse(token) {
            const base64Url = token.split('.')[1];
            const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
            const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
                return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
            }).join(''));
            return JSON.parse(jsonPayload);
        }

        function handleSignOut() {
            google.accounts.id.disableAutoSelect();
            document.getElementById('buttonDiv').style.display = 'block';
            document.getElementById('userInfo').style.display = 'none';
        }

        window.onload = function () {
            google.accounts.id.initialize({
                client_id: 'xxxxxxxxxx',
                callback: handleCredentialResponse
            });
            google.accounts.id.renderButton(
                document.getElementById("buttonDiv"),
                { 
                    theme: "outline", 
                    size: "large",
                    type: "standard"
                }
            );
        };
    </script>
</body>
</html> 

ローカル環境での動作確認

こちらアクセスすると簡易的なログイン画面が表示されます。
image.png

ポップアップでログイン画面の表示、メールアドレスとパスワードを入力。
image.png

承認画面の表示に対しメッセージを確認して次へをクリック。
image.png

無事にログインすることができました!
image.png

ここまでの実装でも認証自体は問題なくできていたのですが、細かい警告やエラーの様なものが出力されていたため、以下の様にGoogleCloud側の設定範囲を広げることにしました。
image.png

本番環境を想定した動作確認

現在はテスト環境として動作している状態です。
外部URLを認証元として設定する場合、テスト環境の場合はテスト用に登録したユーザーのみが利用可能です。

テストユーザーを追加することで動作確認は可能なのですが、ここでは段階的なテストは不要なため省略し、本番環境として公開をします。
image.png

本番環境として、Amazon S3に動作確認用のバケットを用意しました。
静的Webホスティングとして公開設定をし、本番環境で利用するにはhttpsである必要があるため、オブジェクトURLのhttpsで始まるURLを入力します。
image.png

image.png

本番環境公開

アプリの公開をクリックすると確認画面が表示されるため本番環境へと公開します。
image.png

以下の様な条件の際にはGoogleの検証を受ける必要がありますがそのまま確認をクリック。
image.png

公開がされました。
image.png

実際に本番環境のURLからアクセスしてみたところ、問題なく認証できることが確認できました。
image.png

おわりに

単純なログイン機能だけの実装であれば思った以上に簡単に利用できます。
認証機能は付けたい場合でも他での認証機能を構築する手間などを省きたい場合には、パパっと利用できるとても良い選択肢となると思います。
ただし利用スコープがある場合には審査が厳しくなるため、覚悟して臨みましょう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?