简体   繁体   中英

Python (requests library) ETL: Spotify API "Authorization Code Flow" - Request Access Token Problem

Context:

I'm working on a side project to pull data from the Spotify API into a Microsoft SQL Server database as part of a refreshing ETL job. I need to use the "Authorization Code Flow" so I can authorize/authenticate programmatically, so my table will populate each day.

I'm using the Python requests library for this, and I don't want to make an Object Oriented Solution for this if possible (not my preference).

Problem:

I'm having trouble getting the Access Token after authenticating. Looking at similar issues, it's very similar to this one: Spotify API Authorization Code Flow with Python .

I'm not sure why I'm getting a Response 400 (Bad Request) from this. Can someone please advise here?

Code:

# used to to encode byte string from CLIENT_ID : CLIENT_SECRET, then decode for Authentication Header
import base64

# used to make HTTP requests from Spotify API
import requests

# used to access the environment variables
import os

def request_user_authorization():
    '''
        HTTP GET request to gain access to data (Authorization Code Flow)
        HTTP POST request to send the code and receive an Authorization Token (current issue)

        https://developer.spotify.com/documentation/general/guides/authorization/code-flow/
    '''
    # URLs
    AUTH_URL = 'https://accounts.spotify.com/authorize'
    TOKEN_URL = 'https://accounts.spotify.com/api/token'
    BASE_URL = 'https://api.spotify.com/v1'

    SPOTIFY_URI = 'https://api.spotify.com/v1/me/player/recently-played'

    # sensitive items
    CLIENT_ID = os.environ.get('SPOTIFY_CLIENT_ID_ENV')
    CLIENT_SECRET = os.environ.get('SPOTIFY_CLIENT_SECRET_ENV')

    # make a request to the /authorize endpoint to get an authorization code
    user_authorization_code = requests.get(
        AUTH_URL, {
            'client_id': CLIENT_ID,
            'response_type': 'code',
            'redirect_uri': SPOTIFY_URI,
            'scope':  'user-read-recently-played',
        }
    )

    # Code 200 = "OK"
    print(user_authorization_code)

    #----------------------------------------------------------#
    api_header_string = base64.urlsafe_b64encode((CLIENT_ID + ':' + CLIENT_SECRET).encode('ascii'))

    api_headers={
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic %s' % api_header_string.decode('ascii')
    }

    api_payload = {
        'grant_type': 'authorization_code',
        'code': user_authorization_code,
        'redirect_uri': SPOTIFY_URI,
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET
    }

  #issue here: 

    # Make a request to the /token endpoint to get an access token
    access_token_request = requests.post(url=TOKEN_URL, data=api_payload, headers=api_headers)
    
    # returns <Response [400]>

    # https://datatracker.ietf.org/doc/draft-ietf-httpbis-semantics/
        # 15.5.1.  400 Bad Request

        #    The _400 (Bad Request)_ status code indicates that the server cannot
        #    or will not process the request due to something that is perceived to
        #    be a client error (e.g., malformed request syntax, invalid request
        #    message framing, or deceptive request routing).

#     print(access_token_request)
    #----------------------------------------------------------#


request_user_authorization()

You seem to have misunderstood how the Authorizatuon Code Flow works.

The redirect_uri in this kind of flow is used by the provider api (here spotify) as a callback to give you the authorization code.

The spotify API will call this url with a code parameter that you can use to ask for a token.

Meaning that for this flow to work you need a web server ready to receive requests on the uri that you have given in your code request (and specified when creating your app on the spotify developer portal). You might be better off using the Client Credentials Flow for your use case.

Also you should always use the name of the keywords arguments when using requests.get , requests.post ... It makes the code clearer and the order of the arguments differ for each method so it can get confusing if you don't.

@Speedlulu you're correct, that was the problem.

For anyone in the future reading this question: this is what I learned since posting the question:

What I misunderstood was the flow of data, and that Client Credentials Flow (Application to Spotify only) was the better choice because I don't need to have a "User" portion to this program.

Spotify's Client Credentials Flow Documentation: https://developer.spotify.com/documentation/general/guides/authorization/client-credentials/

# used to access environment variables securely (sensitive data)
import os

# used to encode strings into bytes and back
import base64

# used to convert JSON data into strings
import json

# endpoint that I'm connecting to on Spotify's servers
token_request_url = "https://accounts.spotify.com/api/token"

CLIENT_ID = os.environ.get('SPOTIFY_CLIENT_ID_ENV')
CLIENT_SECRET = os.environ.get('SPOTIFY_CLIENT_SECRET_ENV')

# encode credentials into bytes, then decode into a string for the HTTP POST request to Spotify to authenticate
BASE64_ENCODED_HEADER_STRING = base64.b64encode(bytes(f"{CLIENT_ID}:{CLIENT_SECRET}", "ISO-8859-1")).decode("ascii")

#initializing dictionaries for HTTP POST request
headers = {}
data = {}

headers['Authorization'] = f"Basic {BASE64_ENCODED_HEADER_STRING}"

data['grant_type'] = "client_credentials"
data['json'] = True
data['scope'] = 'user-read-recently-played'

r = requests.post(url=token_request_url, headers=headers, data=data)

# prints the response from the server regarding the access token data (formatted to be easier to read)
print(json.dumps(r.json(), indent=2))    
  
# store the token value in a variable for HTTP GET request  
token = r.json()['access_token']

What was unclear is that I first need to POST my request with the credentials to get the token (using the specific URL to do), store the r.json()['access_token'] value in a variable, then use that as part of the following GET request to access my specific data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM