首页 > 解决方案 > 在 authlib 中,authorize_access_token 是否需要显式传递 client_id 和 client_secret?

问题描述

我正在使用具有非常标准的 OAuth2 流程的 authlib。

import pickle
import json
import os.path
from requests.models import Response

from authlib.integrations.flask_client import OAuth, OAuthError

class BaseApi(object):
    def __init__(self, oauth_client, config_directory, token_file=''):
        self.oauth_client = oauth_client

        if token_file: 
            self.token_file = token_file
        else:
            self.token_file = os.path.join(config_directory, 'token.pickle')

    @property
    def token(self):
        try: 
            print ("Token: %s" % self.__token)
            return self.__token
        except AttributeError: 
            if os.path.exists(self.token_file):
                with open(self.token_file, 'rb') as f:
                    self.__token = pickle.load(f)
                print ("Pickled Token: %s" % self.token)
                return self.__token
    @token.setter
    def token(self, new_token):
        self.__token = new_token


   # The authorizaiton flow sends us to the OAuth provider
    # with a redirect back to our app
    def login(self, state=None): 
        return self.oauth_client.authorize_redirect(self.auth_callback, state=state)

    # Our authorized endpoint. 
    def authorized(self,request): 
        # Get the access token!
        token = self.oauth_client.authorize_access_token(client_id=self.oauth_client.client_id,
                client_secret=self.oauth_client.client_secret)
        #if resp is None or resp.get('access_token') is None:
        #    return 'Access denied: error=%s resp=%s' % (
        #        request.args,
        #        resp
        #    )
        self.token = token


这都是在不同模块中的子类:


from __future__ import print_function
import json
from backend.oauth_base import BaseApi, OAuth, load_credentials_file
from urllib.parse import urlparse
from backend import app, url_for # This is the flask app
from flask import request, redirect, jsonify
import requests


import os


config_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config')


class BasecampApi(BaseApi):
    def __init__(self):
        oauth = OAuth(app)

        credentials =  load_credentials_file(os.path.join(config_directory, 
            'credentials.json'))

        oauth.register(
                name='basecamp',
                client_id=credentials['client_id'],
                client_secret=credentials['client_secret'],
                api_base_url='https://3.basecampapi.com/',

                request_token_url=None,
                request_token_params={'type': 'web_server'},

                access_token_url='https://launchpad.37signals.com/authorization/token',
                access_token_params={'type': 'web_server'},

                authorize_url='https://launchpad.37signals.com/authorization/new',
                authorize_params={'type': 'web_server'}
            )

        oauth_client = oauth.create_client('basecamp') 

        super().__init__(oauth_client, config_directory)

这里的一切都有效,但我对为什么需要在授权访问令牌中显式传递 client_id 和 client_secret 感到困惑。

        token = self.oauth_client.authorize_access_token()

导致 basecamp API 抛出一个错误,抱怨缺少 clien_id(然后是 client_secret)。

与文档不同(我承认这很令人困惑)。这是预期的行为吗?

标签: pythonflaskauthlib

解决方案


默认token_endpoint_auth_method值为client_secret_basic,它将在 HTTP 授权标头中发送客户端凭据。但是,根据您的描述,似乎提供程序需要client_idclient_secret在有效载荷中,应该是client_secret_post. 考虑将以下内容添加client_kwargs到您的.register

client_kwargs = {
    'token_endpoint_auth_method': 'client_secret_post',
}

推荐阅读