GoogleのOauthでログインできるまで【Go言語】
10年ぐらいまでにGoogleのOauthでログインできるように実装した時がありました。
その時の記憶によればFacebookが一番シンプルで簡単に実装ができて
その次にTwitterが簡単でした。
Googleは一番、難しく時間がかかりました。
APIの数も多く、ドキュメントのページ構成も日に日に更新されて変わってしまいます。
その影響で、ググったページとGoogle APIの仕様とドキュメントに差異が出てややこしかったイメージがあります。
それから10年が経ち、GoogleのAPIを利用してログイン機能を実装してみました。
さらにさらに、難しく、ややこしくなってました。
ヴァージョンが変わって、どのドキュメントが正しいのかの把握に時間を取られてしまいました。
Google Cloud Platformのページ
Google Oauthのページ
Google Identityのページ
正解はGoogle Identityのページが今回の実装に必要な正解のページです。
それがわかるまで約1日かかりました。
具体的には、いろんなところに散在しているサンプルを試してはダメ試してはダメみたいに進めていきました。
この公式ドキュメントを読み進めていくと
このURLのHTMLサンプルをもとにclient-idを差し替えたHTMLページを作成すればいいとの事です。
早速、その通りHMTLページを作成して、サンプルにclient-idを差し替えて表示しました。
Googleのボタンは表示されたもの、ポップアップが真っ白
developer toolで確認すると下記のエラーが出ててハマってしまいこのエラーの対処方法で1日かかりました。
[GSI_LOGGER]: The given origin is not allowed for the given client ID.
ブラウザのdevelper toolのコンソールから上記のようなエラーが出ました
このエラー内容を丸々コピーしてググっても出てきた対策は
クロスドメインのエラーなので、クロスドメインを許可するべし
と出たので、nginxのconfigファイルを何度かいじって試しました
が、うまくいかず
HTMLのヘッダーに
<meta name="referrer" content="strict-origin-when-cross-origin" />
と入れてはみたもののうまくいかず
"Authorized JavaScript origins box" に許可したいドメインを追加すべし
という事でそれにあたる入力boxを探しに探すこと半日、なかなか見つかりません
挙げ句の果てはgoogle search consoleをアクティベートしないといけないと思い、アクティベートしました。
それでも効果なし。。。
打つ手なしの中、何度か認証情報を作成した時に大きな事に気づきました
https://console.cloud.google.com/
↓
APIとサービス
↓
認証情報
↓
認証情報を作成
↓
OAuthクライアントIDの作成
で
ウェブアプリケーションを選べる事に気づいた!!
以前にcalendar APIの導入の時にデスクトップを前提に導入しました。
なので、そもそもWEBアプリケーションの認証情報を作成できると思ってなかったです。
そう思わず、WEBアプリケーションもデスクトップと同じ意味なのではと勘違いしてました。
且つ、選択肢の一番上でドロップダウンを選ぶ前に「アプリケーションの種類」がプレースホルダーになっているので、一番上の選択肢がWEBアプリケーションではなくアプリケーションの種類と勘違いしてました。
UIが悪いんじゃないの〜って思うのは言い訳ですかねぇ〜。。。
その勘違いを訂正して、WEBアプリケーションを選択しました。
そうすると、"Authorized JavaScript origins" のエラーの対策にもなるような入力欄がありました。
その入力欄にリダイレクト先とサインインのページのドメインを入力しました。
なんとポップアップが表示され、Googleのマニュアル通りリダイレクト先にPOSTされました。
そのPOSTの内容のログをとるとCSRFとcredentialのデータがPOSTされています。
これはドキュメント通りなので、そのcredentialをJWTパーサーでパースしないといけないみたいです。
https://developers.google.com/identity/gsi/web/reference/html-reference
何千行も書かれているページの decode という小さなリンクを辿らなければcredentialのパースするサンプルコードには辿り着けません。
わざとか?って思うくらい複雑なドキュメント構成です。。。
https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
サンプルコードではJS, PHP, Pythonなどはありますが、Goがないので、githubから探します。
grepするなり、探しまくる事3時間、PHPのサンプルコードと同じ階層にあるはずという事でPHPのサンプルコード
https://github.com/googleapis/google-api-php-client/blob/main/examples/idtoken.php
と同じ階層にGoのサンプルがあるか探すが、サンプルコードは存在していないみたい。。
ドキュメントにはrecommended wayと言ってるくせに。。とほほ。
なので、Googleのライブラリからはあきらめて、「Go JWT」で検索してみつけます。
github.com/golang-jwt/jwt/v4
のライブラリが見つかったので、READ.MEを確認しながら実装を進めます。
jwt.Parse関数の戻り値でしっかり、subをはじめとしたデータがログで確認できました。
ほぼ完了かと思いきや、そのsubのデータの取得ができない。。
そこでも時間がかかってしまい、原因はtoken.Validがfalseでした。
googleが生成したJWTのtokenのシークレットIDなんて知るよしもないので、色々試しましたが、無視する事にしました。
無視して
if claims, ok := token.Claims.(jwt.MapClaims); ok {
fmt.Println(claims["sub"])
}
のコードでやっと、JWTのsubが取得できました。
ちなみにJWTの情報の中に有効期限などの情報があるため、Google Identityをセッション代わりに使う事も可能のように見えます。
しかし、Cognitoの件でも述べたようにセッションの確認毎にAPIサービスを利用するとなるとアクセスのスピードも遅くなりますし、処理も複雑になります。
単純にsubだけを利用して、セッション管理はredisかファイル、DBとかで管理したほうが簡単かと思います。
DBとかだとDBのI/Oに負荷がかかってしまいますが、
今回redisを入れるのも面倒なので、DBでセッション管理してみます。
右往左往したもののこれでGoogle IdentityのSign In機能を利用してログインを実装することができました。
肌感ですが、10年くらい前に比べて1.5倍くらい難しくなってる気がします。
oauthトークンの有効期限のドキュメントは下記です。念のため
https://developers.google.com/identity/protocols/oauth2#expiration
登録日:
更新日: