谷歌登录 django rest framework + allauth + rest-auth.



我正在尝试使用allauthrest-auth在 django 中实现Google 身份验证。经过几个小时的研究,我找到的解决方案都没有在我的项目中奏效。

我的代码基于一个github问题:https://github.com/Tivix/django-rest-auth/issues/403

还有一篇文章: https://medium.com/@gonzafirewall/google-oauth2-and-django-rest-auth-92b0d8f70575

我还创建了一个具有Google客户端ID客户端密钥社交应用程序对象

我在谷歌控制台中的项目设置:

授权的 JavaScript 来源:http://localhost:8000授权的重定向 URI:http://localhost:8000/api/v1/users/login/google/callback/

我的代码

providers.py

from allauth.socialaccount.providers.google.provider import GoogleProvider

class GoogleProviderMod(GoogleProvider):
def extract_uid(self, data):
return str(data['sub'])

adapters.py

from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from google.auth.transport import requests
from google.oauth2 import id_token
from myproj.users.providers import GoogleProviderMod

class GoogleOAuth2AdapterIdToken(GoogleOAuth2Adapter):
provider_id = GoogleProviderMod.id
def complete_login(self, request, app, token, **kwargs):
idinfo = id_token.verify_oauth2_token(token.token, requests.Request(), app.client_id)
if idinfo["iss"] not in ["accounts.google.com", "https://accounts.google.com"]:
raise ValueError("Wrong issuer.")
extra_data = idinfo
login = self.get_provider().sociallogin_from_response(request, extra_data)
return login

views.py

from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from rest_auth.registration.serializers import SocialLoginSerializer
from rest_auth.registration.views import SocialLoginView
from myproj.users.adapters import GoogleOAuth2AdapterIdToken

class GoogleLoginView(SocialLoginView):
adapter_class = GoogleOAuth2AdapterIdToken
callback_url = "http://localhost:8000/api/v1/users/login/google/callback/"
client_class = OAuth2Client
serializer_class = SocialLoginSerializer

urls.py

from allauth.socialaccount.providers.oauth2.views import OAuth2CallbackView
from django.urls import path
from myproj.users.adapters import GoogleOAuth2AdapterIdToken
from myproj.users.views import GoogleLoginView
app_name = "users"
urlpatterns = [
path(
"login/google/",
GoogleLoginView.as_view(),
name="google_login"
),
path(
"login/google/callback/",
OAuth2CallbackView.adapter_view(GoogleOAuth2AdapterIdToken),
name="google_callback"
),
]

settings.py

AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
]
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "http"

当我在登录页面上传递ID令牌(从Google"登录"按钮返回)作为代码参数时,发生错误:

allauth.socialaccount.providers.oauth2.client.OAuth2Error: Error retrieving access token: b'{n "error": "invalid_grant",n "error_description": "Malformed auth code."n}'响应代码为 400。

实际上,即使我将一些随机文本传递给代码,错误也是相同的。

感谢您的帮助!

我还集成了djangorestframework+ django-allauth + django-rest-auth +djangorestframework-jwt但是,我只是使用Google实现登录,因此用户无法手动注册。

这是我的代码:

views.py

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

class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client

urls.py

...
path('auth/google', GoogleLogin.as_view(), name='google_login'),
...

settings.py

INSTALLED_APPS = [
...
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
...
]
SITE_ID = 1
REST_USE_JWT = True    # this is for djangorestframework-jwt

[更新]
要在 SocialLoginView 端点上使用code参数:

  • 创建可访问的重定向 URI(我认为它在前端,或者您可以使用简单视图)
  • 使用 https 回调(我在本地开发中使用 ngrok 来做到这一点)
  • 直接访问 URL(不要使用 Google OAuth Playgroud 获取代码,因为client_id不同)或者只是在 OAuth Palygroud 上复制请求 URL 并更改redirect_uri和cliend_id。https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=<https_callback>&prompt=consent&response_type=code&client_id=<cliend_id>&scope=email&access_type=offline

您无需在provider.py文件中自定义GoogleProvider类。

请在adapters.pycomplete_login功能中添加extra_data['id'] = extra_data['sub']。然后,您可以在自定义的GoogleOAuth2Adapter适配器中使用GoogleProvider

from google.oauth2 import id_token
from google.auth.transport import requests
from allauth.socialaccount.providers.oauth2.views import (
OAuth2Adapter,
OAuth2CallbackView,
OAuth2LoginView,
)
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
class GoogleOAuth2AdapterIdToken(GoogleOAuth2Adapter):
def complete_login(self, request, app, token, **kwargs):
idinfo = id_token.verify_oauth2_token(token.token, requests.Request(), app.client_id)
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')
extra_data = idinfo
extra_data['id'] = extra_data['sub']
login = self.get_provider() 
.sociallogin_from_response(request,
extra_data)
return login
oauth2_login = OAuth2LoginView.adapter_view(GoogleOAuth2AdapterIdToken)
oauth2_callback = OAuth2CallbackView.adapter_view(GoogleOAuth2AdapterIdToken)

请参阅 https://gonzafirewall.medium.com/google-oauth2-and-django-rest-auth-92b0d8f70575 和 https://github.com/pennersr/django-allauth/issues/1983

您是否在项目的开发人员控制台中添加了 UI 的本地主机地址?我正在尝试使用Google身份验证执行类似于您的设置的操作。我最终在 Django 中创建了另一个应用程序,该应用程序对一个特定路由具有 csrf 豁免,以便在使用有效令牌调用时注册(更新或创建)用户。

class VerifyToken(APIView):
permission_classes = (AllowAny,)  # maybe not needed in your case
authentication_classes = (UnsafeSessionAuthentication,)
@csrf_exempt
def post(self, request):
print('hitting endpoint')
requestJson = json.loads(request.body)
request = requests.Request()
id_info = id_token.verify_oauth2_token(requestJson['token'], request, settings.CLIENT_ID)
if id_info['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
return Response("Unable to Validate User: Wrong Issuer")
# raise ValueError('Wrong issuer.')
if not id_info:
return Response("Unable to Validate User: Invalid Token")
# raise Exception("Unable to Validate Token")
id = id_info['email']
user_letters = id_info['given_name'][0].upper() + id_info['family_name'][0].upper()
# In this case, if the Person already exists, its name is updated
user, created = User.objects.update_or_create(
email=id, defaults={
"first_name": id_info['given_name'],
"last_name": id_info['family_name'],
"last_login": datetime.datetime.now(),
"email_verified": id_info['email_verified'],
"exp": id_info['exp'],
"locale": id_info['locale'],
"name": id_info['name'],
"picture": id_info['picture'],
"initials": user_letters,
"username": id_info['given_name'] + " " + id_info['family_name'],
}
)
if created:
serializer = UserSerializer(user)
return Response(serializer.data)

我不确定,但是,您的代码有两个错误。

第一: 您的适配器必须具有此行

oauth2_login = OAuth2LoginView.adapter_view(GoogleOAuth2AdapterIdToken)
oauth2_callback = OAuth2CallbackView.adapter_view(GoogleOAuth2AdapterIdToken)

第二: 您必须将此行添加到您的提供商

provider_classes = [GoogleProviderMod]

并且包含 prover.py 文件的应用程序必须添加到设置文件中所有应用程序的末尾,这是因为有一个映射器,其中包含您的 Django 应用程序中的所有提供程序,并且在它们(您的提供商和来自谷歌的提供商)中使用"google"的 id

我使用了您上面提到的两个链接,并且我有一些错误。

我知道这与你提到的疑问无关,但我来到这篇文章是为了寻找如何将 DRF 与谷歌登录集成,这个评论可以得到其他人的帮助

根据aijogja的回答,这是我的解决方案。使用 django-allauth 和 dj-rest-auth。

在 REST Google 登录视图中添加了一条callback_url:

class GoogleLogin(SocialLoginView):
"""Google login endpoint"""
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
callback_url = 'http://localhost:8000/accounts/google/login/callback/'

此网址与作为重定向网址添加到 Google API 控制台的网址相同。我将其用于allauth提供的标准Django登录和上面的REST登录视图。

将此视图映射到google_token URL:

urlpatterns = [
....,
path('google_token/', views.GoogleLogin.as_view(), name='google_token'),
....,
]

由于我正在为 Android 执行此操作,因此我不妨添加该部分,因为它可能会帮助某人:

GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestServerAuthCode(serverClientId)
.requestEmail()
.build();

GoogleSignInAccount account = completedTask.getResult(ApiException.class);
String authCode = account.getServerAuthCode();

将此 authCode 发送到上面定义为代码值的终结点(视图接受代码和 authToken),仅此而已。 使用 IdToken 而不是 authCode 时应该有一个工作流程,但似乎 dj-rest-auth 不支持这一点。

最新更新