やりたいこと
バックエンドとフロントエンドを分離して、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を採用していきたいと思います。
実装方針
認証フローを何も知らないと、やってて訳わからなくなると思うので、一旦以下の神記事を読むことをお勧めします。
神記事の通りに実装する方法を書いていきます。
具体的なコードは下にまとめとくので、適時実装してください。
1. ユーザーにリンクを踏ませる
(https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be より拝借)
まずは、ユーザーにリンクを踏ませる必要があるので、以下のリンクにアクセスしてもらいます。
https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=&prompt=consent&response_type=code&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. リンクを踏んで遷移する
(https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be より拝借)
あとは、dj-rest-authがよしなにやってくれます。
さっきのリンクを踏んでみて、ログインしてみましょう。
すると
のような感じになると思います。
ここで、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のコピペでは認証コードが変換されてるから、そりゃ不正なコードっていいますわな。色々疑ってすまんかったな。ワイが無知やったで。