Flask integration with Spotify Web API authentication flow

I’m working on a Flask application that connects to Spotify’s Web API using the authorization code flow. My current implementation works fine, but I’m running into an issue with how I handle the authentication process.

import os
import requests
import urllib.parse
from flask import Flask, request, session, redirect, url_for, jsonify
import base64

app = Flask(__name__)
app.secret_key = 'my_secret_key'

SPOTIFY_AUTH_URL = 'https://accounts.spotify.com/authorize'
SPOTIFY_TOKEN_URL = 'https://accounts.spotify.com/api/token'
SPOTIFY_API_URL = 'https://api.spotify.com/v1'

APP_ID = "your_client_id"
APP_SECRET = "your_client_secret"
CALLBACK_URL = "http://localhost:5000/auth_callback"

permissions = 'user-read-private user-read-email'

@app.route('/')
def index():
    return "Welcome <a href='/authenticate'>Connect Spotify</a>"

@app.route('/authenticate', methods=['GET'])
def authenticate():
    permissions = 'playlist-read-private user-library-read'
    query_params = {
        'client_id': APP_ID,
        'response_type': 'code',
        'redirect_uri': CALLBACK_URL,
        'scope': permissions,
        'show_dialog': True
    }
    
    spotify_auth_url = f"{SPOTIFY_AUTH_URL}?{urllib.parse.urlencode(query_params)}"
    return redirect(spotify_auth_url)

@app.route('/auth_callback', methods=['GET'])
def auth_callback():
    if 'error' in request.args:
        return jsonify({'error': request.args['error']})
    
    auth_code = request.args.get('code')
    if not auth_code:
        return jsonify({'error': 'Authorization code missing'})
    
    credentials = base64.b64encode(
        f"{APP_ID}:{APP_SECRET}".encode()
    ).decode()
    
    request_headers = {
        'Authorization': f'Basic {credentials}',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    token_data = {
        'grant_type': 'authorization_code',
        'code': auth_code,
        'redirect_uri': CALLBACK_URL
    }
    
    try:
        token_response = requests.post(SPOTIFY_TOKEN_URL, headers=request_headers, data=token_data)
        token_response.raise_for_status()
        
        return jsonify(token_response.json())
    
    except requests.exceptions.RequestException as error:
        return jsonify({
            'error': 'Token exchange failed',
            'message': str(error)
        }), 500

if __name__ == '__main__':
    app.run(port=5000, debug=True)

The problem I’m facing is that while this approach works correctly, I want to avoid redirecting users to the Spotify authorization URL with all those query parameters. Instead, I’d prefer to make a direct GET request and handle the response in my callback function. When I try this approach, I can retrieve the authorization code from the arguments, but I’m getting authentication errors when trying to exchange it for an access token via POST request. Is this behavior intentional on Spotify’s part to prevent certain types of requests, or am I missing something in my implementation? I’m specifically using the authorization code grant type for this integration.

Nah, you can’t skip the redirect - Spotify’s OAuth needs users to hit their auth page for security. Direct GET requests won’t work since users have to login and give consent on Spotify’s end. Your code looks good though. Just save the tokens in session after you get them and use refresh tokens when they expire.

You can’t bypass the redirect - OAuth 2.0 spec requires it. What you’re seeing is normal since Spotify needs users to authorize on their domain for security. Your code looks right, but I’d store that access token in the session instead of just returning JSON. Don’t forget to add token refresh logic since they expire after an hour. Also throw in a state parameter to prevent CSRF attacks.