1

シナリオ:

アクセスを許可するときに、各アプリ ユーザーのデータストアにトークンを保存します。

さまざまなユーザーの連絡先にアクセスするために、いくつかの GData 連絡先承認済みクライアントを作成する必要があります。データストアから必要なユーザーのトークンを取得し、承認済み連絡先クライアントを作成します。たとえば、あるユーザーの連絡先を他のユーザーの連絡先にコピーします。

問題:

承認されたクライアントは期待どおりに機能し、トークンが使用されているユーザーの連絡先フィードを取得しますが、トークンを使用するとトークンの有効期限も保存され、1 時間または 45 分で有効期限が切れてから機能しなくなります。更新トークンはトークン オブジェクトにあり、短命のアクセス トークンの有効期限が切れている場合、トークンは自動的に新しいアクセス トークンを取得しますが、これは発生していません。

質問:

ユーザーがアクセスを取り消さない限り、ユーザーの連絡先にアクセスできるように、有効期限が切れている場合にデータストアから取得した後にトークンを更新するにはどうすればよいですか?

コード :

SCOPE = 'https://www.google.com/m8/feeds'
REDIRECT_URI = 'http://localhost:8888/mainPage'
USER_AGENT = 'shared-contacts'

token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID,
                              client_secret=CLIENT_SECRET,
                              scope=SCOPE,
                              user_agent=USER_AGENT)

class MyEntity(db.Model):
  userEmail = db.StringProperty()
    access_token = ObjectProperty()

class ObjectProperty(db.BlobProperty):
    def validate(self, value):
        try:
            result = pickle.dumps(value)
            return value
        except pickle.PicklingError, e:
            return super(ObjectProperty, self).validate(value)

    def get_value_for_datastore(self, model_instance):
        result = super(ObjectProperty, self).get_value_for_datastore(model_instance)
        result = pickle.dumps(result)
        return db.Blob(result)

    def make_value_from_datastore(self, value):
        try:
            value = pickle.loads(str(value))
        except:
            pass
        return super(ObjectProperty, self).make_value_from_datastore(value)

class MainHandler(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            self.redirect(token.generate_authorize_url(
                                                  redirect_uri=REDIRECT_URI,
                                                  access_type='offline')
                                                  )
        else:
            self.redirect(users.create_login_url("/"))

class MainPage(webapp2.RequestHandler):

    def get(self):
        # Getting the token from datastore, if token exists for this user then
      # proceed otherwise get the access token and save in datastore.

      accessToken = Fetch_Access_Token()

        if accessToken:
            pass
        else:
            url = atom.http_core.Uri.parse_uri(self.request.uri)
            code = url.query
            code = code['code']

            if 'error' in code:
                # User Did Not Grant The Access
                pass
            else:
                token_user_email = users.get_current_user().email()
                token.GetAccessToken(code)
                entity = MyEntity(userEmail=token_user_email, access_token=token)
                entity.put()

        self.response.out.write(template.render('index.html',{}))

class RPCMethods():
    def MoveContacts(self, *args):

      # All tokens except current user
      shared_group_users = shared_users(skip_current_user)

        contact_client = gdata.contacts.client.ContactsClient(source=USER_AGENT)

      for sg_user in shared_group_users.itervalues():

          shared_user_token = sg_user[1]

          # PROBLEM OCCURS HERE, IT WORKS FOR ONLY 1 HOUR AND HERE I NEED
          TO REFRESH THIS TOKEN.

          # For almost an hour i get other user's Data, but after that i
          get current user's Data.

          shared_user_authorized_client = shared_user_token.authorize(contact_client)
          shared_group_contact_feed = shared_user_authorized_client.GetContacts()

application = webapp2.WSGIApplication([
                                      ('/', MainHandler),
                                      ('/mainPage', MainPage),
                                      ('/rpc', RPCHandler),
                                      ], debug=True)

アップデート:

作業コード:

shared_user_credentials = StorageByKeyName(CredentialsModel, shared_user_credentials_key, 'credentials').get()

    if shared_user_credentials.access_token_expired == True:
        http = httplib2.Http()
        http = shared_user_credentials.authorize(http)
        shared_user_credentials.refresh(http)
4

1 に答える 1

2

したがって、OAuth 2.0 コードは「まったく問題ありません」ですが、トークンが必要になったときにトークンを取得する方法が問題です。

別の投稿で、OAuth2.0 ドキュメントのオフライン アクセス部分について次のように述べています。

アプリケーションが更新トークンを受け取ったら、将来の使用のためにその更新トークンを保存することが重要です。アプリケーションがリフレッシュ トークンを失った場合、別のリフレッシュ トークンを取得する前に、ユーザーに再度同意を求める必要があります。ユーザーに再度同意を求める必要がある場合はapproval_prompt、認証コード要求にパラメーターを含め、値を に設定しますforce

簡単な修正:

を呼び出すときは、次のようgenerate_authorize_urlに送信しますapproval_prompt='force'

self.redirect(token.generate_authorize_url(redirect_uri=REDIRECT_URI,
                                           access_type='offline',
                                           approval_prompt='force')

メソッドのシグネチャapproval_prompt既に含まれていることがわかります。generate_authorize_url

実際の修正:

トークンを保存する場合:

token_user_email = users.get_current_user().email()
token.GetAccessToken(code)
entity = MyEntity(userEmail=token_user_email, access_token=token)

現在のユーザーのエンティティ セットがあることがわかっています。代わりに保存することをお勧めします

entity = MyEntity(id=token_user_email, access_token=token).

ユーザーに OAuth 2.0 フローを通過させると、

user = users.get_current_user()
if user:
    self.redirect(token.generate_authorize_url(
    ...

最初に、指定されたユーザーのトークンが既に存在するかどうかを確認する必要があります。

token = MyEntity.get_by_id(user.email())
if token is not None:
    # do something

そして、本当に、本当に本当にtoken、グローバルオブジェクトとして使用しないでください!

正直なところ、これを間違える方法はたくさんありますが、おそらくOAuth2Decoratorfromを使用した方がよいでしょうgoogle-api-python-client

このデコレータを と組み合わせる方法については、別の質問に投稿しgdata-python-client回答をご覧ください。

別れのメモ:

ndb データストア ライブラリを使用する必要があります。その恐ろしい を書く必要がある代わりに、あなたの側の努力を必要とせずにObjectProperty簡単に使用できたはずです。ndb.PickleProperty

于 2013-02-12T18:10:29.787 に答える