首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django-rest-auth + Allauth Twitter。错误89。无效或过期令牌

Django-rest-auth + Allauth Twitter。错误89。无效或过期令牌
EN

Stack Overflow用户
提问于 2018-03-09 17:39:06
回答 1查看 1.1K关注 0票数 2

我正在使用Django与Allauth +REST-8月为SPA社会登录,并成功建立Facebook,VK和谷歌授权,但面临一个问题,同时添加Twitter。它以{"code":89,“message”:“无效或过期令牌”结束。}}看来我遗漏了什么东西,因为Twitter的标准登录功能是应该的。

以下是我的尝试:

首先,我设置了Twitter登录端点,如doc中所述:

代码语言:javascript
复制
class TwitterLogin(SocialLoginView):
    serializer_class = TwitterLoginSerializer
    adapter_class = CustomTwitterOAuthAdapter

它具有post方法,期望access_token和token_secret,因此创建了重定向视图以接收来自twitter的重定向,完成登录,并通过模板呈现(带有几行JS行)将内部django令牌设置为浏览器localStorage:

代码语言:javascript
复制
class TwitterReceiveView(APIView):

    def get(self, request, *args, **kwargs):
        access_token = request.query_params.get('oauth_token')
        token_secret = request.query_params.get('oauth_verifier')
        params = {'access_token': access_token,
                  'token_secret': token_secret}
        try:
            result = requests.post(settings.DOMAIN + reverse('tw_login'), data=params).text
            result = json.loads(result)
        except (requests.HTTPError, json.decoder.JSONDecodeError):
            result = {}
        access_token = result.get('access_token')
        context = {'access_token': access_token}
        return render(request, 'account/local_storage_setter.html',
                      context, content_type='text/html')

值得一提的是,我尝试了两种方法来启动进程(获取初始令牌) 1.使用标准的allauth http://0.0.0.0:8080/accounts/twitter/login 2。创建了另一个视图(使用lib oauth2),可以从SPA中使用该视图:

代码语言:javascript
复制
class TwitterGetToken(APIView):

    def get(self, request, *args, **kwargs):
        request_token_url = 'https://api.twitter.com/oauth/request_token'
        authorize_url = 'https://api.twitter.com/oauth/authorize'

        app = SocialApp.objects.filter(name='Twitter').first()
        if app and app.client_id and app.secret:
            consumer = oauth.Consumer(app.client_id, app.secret)
            client = oauth.Client(consumer)

            resp, content = client.request(request_token_url, "GET")
            if resp['status'] != '200':
                raise Exception("Invalid response {}".format(resp['status']))

            request_token = dict(urllib.parse.parse_qsl(content.decode("utf-8")))

            twitter_authorize_url = "{0}?oauth_token={1}"\
                .format(authorize_url, request_token['oauth_token'])

            return redirect(twitter_authorize_url)

        raise Exception("Twitter app is not set up")

我甚至尝试为FacebookLoginView编写get方法,并将twitter回调直接传递给它。

代码语言:javascript
复制
class TwitterLogin(SocialLoginView):
    serializer_class = TwitterLoginSerializer
    adapter_class = TwitterOAuthAdapter

    def get(self, request, *args, **kwargs):
        data = {
            'access_token': request.query_params.get('oauth_token'),
            'token_secret': request.query_params.get('oauth_verifier')
        }
        self.request = request
        self.serializer = self.get_serializer(data=data,
                                              context={'request': request})
        self.serializer.is_valid(raise_exception=True)

        self.login()
        return self.get_response()

所有的方法都导致我提到了错误。你能不能请你在我的案子里提点建议。提前谢谢你!

更新:是如何为我工作的:

代码语言:javascript
复制
import json
import requests
import urllib.parse
import oauth2 as oauth
from requests_oauthlib import OAuth1Session

from django.urls import reverse
from django.conf import settings
from django.shortcuts import redirect, render
from rest_framework.views import APIView
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter, TwitterAPI
from rest_auth.social_serializers import TwitterLoginSerializer
from rest_auth.registration.views import SocialLoginView


class TwitterGetToken(APIView):
    '''
    Initiates Twitter login process
    Requests initial token and redirects user to Twitter
    '''

    def get(self, request, *args, **kwargs):
        request_token_url = 'https://api.twitter.com/oauth/request_token'
        authorize_url = 'https://api.twitter.com/oauth/authorize'

        app = SocialApp.objects.filter(name='Twitter').first()
        if app and app.client_id and app.secret:
            consumer = oauth.Consumer(app.client_id, app.secret)
            client = oauth.Client(consumer)

            resp, content = client.request(request_token_url, "GET")
            if resp['status'] != '200':
                raise Exception("Invalid response {}".format(resp['status']))

            request_token = dict(urllib.parse.parse_qsl(content.decode("utf-8")))

            twitter_authorize_url = "{0}?oauth_token={1}"\
                .format(authorize_url, request_token['oauth_token'])

            return redirect(twitter_authorize_url)

        raise Exception("Twitter app is not set up")



class TwitterLogin(SocialLoginView):
    '''
    Takes the final twitter access token, secret
    Returns inner django Token
    '''
    serializer_class = TwitterLoginSerializer
    adapter_class = TwitterOAuthAdapter


class TwitterReceiveView(APIView):
    '''
    Receives Twitter redirect, 
    Requests access token
    Uses TwitterLogin to logn and get django Token
    Renders template with JS code which sets django Token to localStorage and redirects to SPA login page
    '''

    def get(self, request, *args, **kwargs):
        access_token_url = 'https://api.twitter.com/oauth/access_token'
        callback_uri = settings.DOMAIN + '/accounts/twitter/login/callback/'

        app = SocialApp.objects.filter(name='Twitter').first()
        client_key = app.client_id
        client_secret = app.secret

        oauth_session = OAuth1Session(client_key,
                                      client_secret=client_secret,
                                      callback_uri=callback_uri)

        redirect_response = request.get_full_path()
        oauth_session.parse_authorization_response(redirect_response)
        token = oauth_session.fetch_access_token(access_token_url)

        params = {'access_token': token['oauth_token'],
                  'token_secret': token['oauth_token_secret']}
        try:
            result = requests.post(settings.DOMAIN + reverse('tw_login'),
                                   data=params).text
            result = json.loads(result)
        except (requests.HTTPError, json.decoder.JSONDecodeError):
            result = {}
        access_token = result.get('access_token')
        context = {'access_token': access_token,
                   'domain': settings.DOMAIN}
        return render(request, 'account/local_storage_setter.html',
                      context, content_type='text/html')
EN

回答 1

Stack Overflow用户

发布于 2019-03-15 04:27:23

很好的代码,谢谢你的张贴!

但是,我想补充的是,用户身份验证可以直接从前端完成,并且考虑到您正在编写SPA,这样做似乎是合乎逻辑的,而不是将后端的RESTful重定向到auth,然后发送响应。

我使用了以下基于vue身份验证的JS助手类。创建一个弹出窗口并从回调url获取信息

代码语言:javascript
复制
export default class OAuthPopup {
  constructor(url, name, redirectURI) {
    this.popup = null
    this.url = url
    this.name = name
    this.redirectURI = redirectURI
  }

  open() {
    try {
      this.popup = window.open(this.url, this.name)
      if (this.popup && this.popup.focus) {
        this.popup.focus()
      }
      return this.pooling()
    } catch(e) {
      console.log(e)
    }
  }

  pooling() {
    return new Promise((resolve, reject) => {
      let poolingInterval = setInterval(() => {
        if (!this.popup || this.popup.closed || this.popup.closed === undefined) {
          clearInterval(poolingInterval)
          poolingInterval = null
          reject(new Error('Auth popup window closed'))
        }

        try {
          var popupLocation = this.popup.location.origin + this.popup.location.pathname
          if (popupLocation == this.redirectURI) {
            if (this.popup.location.search || this.popup.location.hash ) {

              const urlParams = new URLSearchParams(this.popup.location.search);

              var params = {
                oauth_token: urlParams.get('oauth_token'),
                oauth_verifier: urlParams.get('oauth_verifier'),
                url: this.popup.location.href
              }

              if (params.error) {
                reject(new Error(params.error));
              } else {
                resolve(params);
              }

            } else {
              reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.'))
            }

            clearInterval(poolingInterval)
            poolingInterval = null
            this.popup.close()
          }
        } catch(e) {
          // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame.
        }
      }, 250)
    })
  }
}

不过,我所采用的方法与你的类似:

  1. 我向TwitterGetToken发出一个GET请求,并将Twitter的url作为响应返回
  2. 我在来自前端的响应中使用url打开一个弹出窗口,允许用户使用该弹出窗口。
  3. 我向TwitterReceiveView发送一个帖子请求,并在twitter之后附加回复url。

其他一切

在任何情况下,谢谢我在js和python中使用了大量的库,但是这只是最好的方法。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49199253

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档