pythonrestpython-requests

Making an API Request with python based on Mozilla Authentication


I'm creating a class called AnbimaApiClient , aiming to facilitate data requests for the Anbima API (api documentation link: https://developers.anbima.com.br/en/autenticacao/). I've successfully implemented a method called _get_new_anbima_token that generates an access_token for the api calls. The problem is in the get_fidc_method.

The objective of this function is to pull data of Brazilian hedge funds based on a unique identification code (called CNPJ). (Here is a link on the documentation of how the API pulls data of hedge funds: https://developers.anbima.com.br/en/apis-de-fundos-2/#submenu-titles-2).

The code

import requests as r

from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import BackendApplicationClient

import os, json, oauthlib


class AnbimaApiClient:
    

    __client_id = "client_id"
    __client_secret = "client_secret"
    __client_access_token = 'client_access_token'
    __token_url =  'https://api.anbima.com.br/oauth/access-token' # token generation
    __fidc_url = 'https://api.anbima.com.br/feed/fundos/v1/fundos-estruturados' # url that I'm trying to 
                                                                                # pull data from

    def __init__(self):

        client = BackendApplicationClient(client_id=self.__client_id)
        self.oauth = OAuth2Session(client=client)
        self.session = r.Session()

        # setting session headers
        self.session.headers.update({
            'Content-Type': 'application/json',
            "Authorization": f"Basic {self.__access_token}"
        })


    def _get_new_anbima_token(self, only_token=True):

       # NOTE: This is working perfectly
        token = self.oauth.fetch_token(token_url=self.__token_url,
                                  client_id=self.__client_id,
                                  client_secret=self.__client_secret)

        if only_token:
            return token['access_token']

        return token
     
    def get_fidc_data(self, field, value):
 
       params = {
            field:  value 
       }
        
       response = self.session.get(self.__fidc_url, params=params)
       return response.text

def main():

    """
        A simple test of the get_fidc_data method
    """

    anbima_client = AnbimaApiClient()
    data = anbima_client.get_fidc_data('cnpj_fundo', '29.494.037/0001-03')
    print(data)

if __name__ == '__main__':
   main()

The problem

Every time I run this script, this is the output of the response.text:

Could not find a required APP in the request, identified by HEADER client_id.

I know that the API follows this authentication process because it's what's written in the API documentation: "The authentication type must be 'Basic Authentication', as specified in: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization." I've tried passing client_id as auth, in the headers but still nothing worked.

What am I getting wrong here? Is in the headers?


Solution

  • According to Anbima's website, the authentication should be in base64:

    "As informações passadas para o campo Authorization devem ser codificadas em base 64. Assim, para gerar o header Authorization do par client_id = aC2yaac23 e client_secret = 1bhS45TT, por exemplo, deve ser gerada a base64 da string aC2yaac23:1bhS45TT, que resultará na chave YUMyeWFhYzIzOjFiaFM0NVRU. Desta forma, o header ficaria: ‘Authorization’: ‘Basic YUMyeWFhYzIzOjFiaFM0NVRU’"

    Try using base64 lib:

    import requests, json, base64
    
    aut_ascii = 'client_id:client_secret' # for example "aC2yaac23:1bhS45TT"
    message_bytes = aut_ascii.encode('ascii')
    message_64 = base64.b64encode(message_bytes).decode('ascii')
    
    header_aut = {"Content-Type":"application/json",
                 "Authorization":"Basic %s"%message_64
                }
    data_aut = {"grant_type":"client_credentials"}
    
    aut = requests.post(url="https://api.anbima.com.br/oauth/access-token",headers=header_aut,data=json.dumps(data_aut),allow_redirects=True)
    
    print(aut.content)