📝

Django(RDF)のGoogleソーシャルログインの覚書

2024/01/14に公開

Django(RDF)のGoogleソーシャルログインの覚書

やりたいこと

バックエンドとフロントエンドを分離して、API経由でソーシャルログインを実現したいのでごちゃごちゃやっていて、色々見えてきたので、自分のメモとして書き置きしておきます。

使用したモジュール

  • dj-rest-auth

基本的には、公式ドキュメントの通りにやっておけば問題ないかと思います。

setting.pyの詳細な設定はここでは省略してます。

Googleの認証方法2つのやり方?

2023年12月調べたところ、Googleの認証方法には2通りあり

  • Authorization Code Grant
  • Implicit Grant
    があるらしく、これによってViewの書き方が一行変わります。

GPTによると

OAuth 2.0の認証フローについては、一般的にAuthorization Code Grantが推奨されています。
Implicit Grantフローは、以前はブラウザベースのアプリケーションで使用されていましたが、セキュリティ上の理由から現在は推奨されていません。特に、Implicit Grantフローではアクセストークンがブラウザを通じて直接クライアントに送信されるため、トークンが漏洩するリスクがあります。
一方、Authorization Code Grantフローでは、認証コードがクライアントに送信され、その後サーバーサイドでアクセストークンと交換されます。これにより、アクセストークンが公開されるリスクが軽減されます。

とのことなので、Authorization Code Grantを採用していきたいと思います。

実装方針

認証フローを何も知らないと、やってて訳わからなくなると思うので、一旦以下の神記事を読むことをお勧めします。

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

神記事の通りに実装する方法を書いていきます。
具体的なコードは下にまとめとくので、適時実装してください。

1. ユーザーにリンクを踏ませる

画像

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be より拝借

まずは、ユーザーにリンクを踏ませる必要があるので、以下のリンクにアクセスしてもらいます。

https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=<CALLBACK_URL_YOU_SET_ON_GOOGLE>&prompt=consent&response_type=code&client_id=<YOUR CLIENT ID>&scope=openid%20email%20profile&access_type=offline

ただし、ここで、

  • CALLBACK_URL_YOU_SET_ON_GOOGLE
  • YOUR CLIENT ID
    が必要となるので、GCPにアクセスして、クライアントIDを入手して、コールバックURLを設定してください。ここら辺は、いっぱい記事があるので省略します。

コールバックURLっては、googleでログインした後に飛ばされるページのことです。

飛ばされるページは以下のViewを設定して、そこに飛ばせば良いです。

View

from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from dj_rest_auth.registration.views import SocialLoginView


class GoogleLoginView(SocialLoginView):
    adapter_class = GoogleOAuth2Adapter
    client_class = OAuth2Client
    callback_url = "http://127.0.0.1:8000/api/social/login/google/"

ちなみに、
callback_url = ''は、Authorization Code Grantの場合必須になります。

2. リンクを踏んで遷移する

画像2

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be より拝借

あとは、dj-rest-authがよしなにやってくれます。

さっきのリンクを踏んでみて、ログインしてみましょう。
すると

スクリーンショット 2023-12-25 7.12.41.png

のような感じになると思います。

ここで、codeが上にあると思うので、それをコピペしてPOSTすれば終わりです!!!!
っと言いたいところですが、URLエンコードという概念があり、URL経由でコピペされたものはエンコードされてしまします。
なので、残念ですが、このやり方で確認したい場合、モジュールのコードを直いじりする必要があります。

lib/python3.10/site-packages/allauth/socialaccount/providers/oauth2/client.py

from urllib.parse import unquote

    def get_access_token(self, code):
        data = {
            "redirect_uri": self.callback_url,
            "grant_type": "authorization_code",
            "code": unquote(code), #unquoteを追加
        }

っとモジュールのソースコードを変更してあげれば、無事いつものソーシャルログインのように自動でユーザーが作られ、アクセストークンとリフレッシュトークンが発行されるはずです。

また、DRFのView経由ではなく、curlを使った場合

curl -d "client_id=*****.apps.googleusercontent.com" -d "client_secret=*****" -d "redirect_uri=http://127.0.0.1:8000/api/social/login/google/" -d "grant_type=authorization_code" -d "code=*****" https://oauth2.googleapis.com/token

でいけると思います。

沼ったところ

  • ソーシャルログイン自体を理解していなかった
    なんか適当にやってたけど、ちゃんとOAuthの概念を理解した方が結果的に早かった
  • モジュールのバージョン
    dj-rest-authのモジュールを昔のバーションで固定で入れてたので、URLが変わっていることに気が付かなかった
  • URLエンコード
    以下のエラーに死ぬほど悩まされた。
  File "/Users/****/next-drf-blog-auth-book/env/lib/python3.10/site-packages/allauth/socialaccount/providers/oauth2/client.py", line 109, in get_access_token
    raise OAuth2Error("Error retrieving access token: %s" % resp.content)
allauth.socialaccount.providers.oauth2.client.OAuth2Error: Error retrieving access token: b'{\n  "error": "invalid_grant",\n  "error_description": "Malformed auth code."\n}'

上記の通り、URLエンコードの概念を知らなかったので、URLのコピペでは認証コードが変換されてるから、そりゃ不正なコードっていいますわな。色々疑ってすまんかったな。ワイが無知やったで。

GitHubで編集を提案

Discussion