Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.8k views
in Technique[技术] by (71.8m points)

python - In authlib, does authorize_access_token require the explicit passing of client_id and client_secret?

I am using authlib with a pretty standard OAuth2 flow.

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


This is all subclassed in a different module:


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)

Everything here works, but I'm confused as to why I need to pass client_id and client_secret explicitly in the authorize access token.

        token = self.oauth_client.authorize_access_token()

Causes the basecamp API to throw an error complaining about a lack of clien_id (and then client_secret).

This is different from the docs (which I admittedly found confusing). Is this the expected behavior?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The default token_endpoint_auth_method is client_secret_basic, which will send client credentials in HTTP Authorization header. However, according to your description, it seems that the provider needs client_id and client_secret in payload, which should be client_secret_post. Consider to add below client_kwargs into your .register:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...