如何使用来自 iOS 应用程序的 ID 令牌在 Python 后端中授权 Google 用户?

How do I authorize a Google user in Python backend with ID token coming from iOS application?

解决方案

所以我认为这对任何人来说都不足为奇,但是 Google 的文档实在是太糟糕了。它太分散了,Python 文档仍然引用他们旧的折旧库。无论如何。

所以我真正需要看的是这个 link "Enabling Server Side Access for your App". This is not linked to anywhere. Keep in mind this is entirely different than "Authenticating with a Backend Server"

这是一个开始。在 iOS 方面,我们需要指定服务器或后端的 client_id.

...

GIDSignIn.sharedInstance().clientID = SBConstants.Google.IOS_CLIENT_ID
GIDSignIn.sharedInstance().serverClientID = SBConstants.Google.SERVER_CLIENT_ID

...

并从登录委托中的 sign 方法捕获 serverAuthCode

...

self.googleUser.userID = user.userID
self.googleUser.token = user.authentication.idToken
self.googleUser.serverAuthCode = user.serverAuthCode

...

现在当你想代表前端在后端执行一些操作时,我们传递捕获的 serverAuthCode 并将其作为参数发送。

这是最简单的部分。在后端,Google 似乎为 Python 记录了 13 个不同的 OAuth2 库。他们的示例使用 oauth2client 当然已弃用。

我们要用的是他们的'new'库google-api-python-client.

auth_token 传递到后端时,我们需要检查用户是否已经在我们的数据库中拥有访问令牌。如果是这样,我们需要刷新。否则,我们需要根据 auth_code 请求一个新的访问令牌。经过反复试验,这里是这样做的代码:

# we have record of this user
# we have record of this user
if user.exists:                                 
    # create new credentials, and refresh
    credentials = Credentials(                
        token=user.token,                     
        refresh_token=user.refresh_token,     
        client_id=CLIENT_ID,                  
        client_secret=CLIENT_SECRET,          
        token_uri='https://oauth2.googleapis.com/token')

    # now we have an access token       
    credentials.refresh(requests.Request())   
                                                
else:                            

    # get the auth_token              
    token_obj = json.loads(request.body)      
    code = token_obj.get('auth_code')         

    # request access token given auth_token                        
    auth_flow = flow.Flow.from_client_secrets_file(creds, scopes=scopes) 
    auth_flow.fetch_token(code=code)       

    # now have access token   
    credentials = auth_flow.credentials       

警告: 通过或失败,auth_token 仅适用于一个请求。这完全烧了我。这也意味着一旦后端交互成功,您必须存储用户的令牌信息,然后请求刷新而不是新的访问令牌。

希望这对某人有所帮助。


原版Post

按照文档 here,我试图在我的 iOS 应用程序中验证用户身份并将他们的 ID 令牌传递到我的后端。后端处理 Google API 应用的 iOS 交互。

我不知道如何在后端实际验证该用户。我阅读了有关 ID 令牌的文档 here,但我对服务帐户的作用感到困惑。

当前端点:

@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def google_token_info(request):
    try:
        token_obj = json.loads(request.body)
        token = token_obj['id_token']

        id_info = id_token.verify_oauth2_token(token, requests.Request(), settings.IOS_CLIENT_ID)
        
        # create session here - how?

一切正常。 ID 信息 returns 预期的解密 JWT 内容,此时我有用户的唯一 Google ID。

在测试时,我通过后端设置了身份验证。我有这样的代码:

def google_auth(request):
    web_flow = flow.Flow.from_client_secrets_file(creds, scopes=scopes)
    web_flow.redirect_uri = request.build_absolute_uri(reverse('api.auth:oauth_callback'))
    auth_url, state = web_flow.authorization_url(access_type='offline', include_granted_scopes='true', prompt='consent')
    request.session['state'] = state
    return redirect(auth_url)

def oauth_callback(request):

    success_flow = flow.Flow.from_client_secrets_file(creds, scopes=scopes, state=request.session.get('state'))
    success_flow.redirect_uri = request.build_absolute_uri(reverse('api.auth:oauth_callback'))

    auth_response = request.build_absolute_uri()
    success_flow.fetch_token(authorization_response=auth_response)

    credentials = success_flow.credentials
    if not request.session.get('google_credentials'):
        request.session['google_credentials'] = _credentials_to_dict(credentials)

    return redirect(reverse('api.auth:success'))

为用户设置会话凭据。我假设我需要类似的东西,但我不确定如何在没有实际凭据的情况下创建会话。

解决方案

所以我认为这对任何人来说都不足为奇,但是 Google 的文档太糟糕了。它太分散了,Python 文档仍然引用他们旧的折旧库。无论如何。

所以我真正需要看的是这个 link "Enabling Server Side Access for your App". This is not linked to anywhere. Keep in mind this is entirely different than "Authenticating with a Backend Server"

这是一个开始。在 iOS 方面,我们需要指定服务器或后端的 client_id.

...

GIDSignIn.sharedInstance().clientID = SBConstants.Google.IOS_CLIENT_ID
GIDSignIn.sharedInstance().serverClientID = SBConstants.Google.SERVER_CLIENT_ID

...

并从登录委托中的 sign 方法捕获 serverAuthCode

...

self.googleUser.userID = user.userID
self.googleUser.token = user.authentication.idToken
self.googleUser.serverAuthCode = user.serverAuthCode

...

现在,当您想代表前端在后端执行某些操作时,我们传递捕获的 serverAuthCode 并将其作为参数发送。

这是最简单的部分。在后端,Google 似乎为 Python 记录了 13 个不同的 OAuth2 库。他们的示例使用 oauth2client 当然已弃用。

我们要用的是他们的'new'库google-api-python-client.

auth_token 传递到后端时,我们需要检查用户是否已经在我们的数据库中拥有访问令牌。如果是这样,我们需要刷新。否则,我们需要根据 auth_code 请求一个新的访问令牌。经过反复试验,这里是这样做的代码:

# we have record of this user
# we have record of this user
if user.exists:                                 
    # create new credentials, and refresh
    credentials = Credentials(                
        token=user.token,                     
        refresh_token=user.refresh_token,     
        client_id=CLIENT_ID,                  
        client_secret=CLIENT_SECRET,          
        token_uri='https://oauth2.googleapis.com/token')

    # now we have an access token       
    credentials.refresh(requests.Request())   
                                                
else:                            

    # get the auth_token              
    token_obj = json.loads(request.body)      
    code = token_obj.get('auth_code')         

    # request access token given auth_token                        
    auth_flow = flow.Flow.from_client_secrets_file(creds, scopes=scopes) 
    auth_flow.fetch_token(code=code)       

    # now have access token   
    credentials = auth_flow.credentials      

警告: 通过或失败,auth_token 仅适用于一个请求。这完全烧了我。这也意味着一旦后端交互成功,您必须存储用户的令牌信息,然后请求刷新而不是新的访问令牌。