pythonjsonflaskresponse.redirect

Flask - send JSON data to client before redirecting with make_request


I have this callback url that has a redirection:

@spotify_auth_bp.route("/callback", methods=['GET', 'POST'])
def spotify_callback():

    SPOTIFY_TOKEN_URL = "https://accounts.spotify.com/api/token"

    CLIENT_ID =   os.environ.get('SPOTIPY_CLIENT_ID')
    CLIENT_SECRET = os.environ.get('SPOTIPY_CLIENT_SECRET')
    REDIRECT_URI = os.environ.get('SPOTIPY_REDIRECT_URI')

    auth_token = request.args['code']

    code_payload = {
        "grant_type": "authorization_code",
        "code": auth_token,
        "redirect_uri": REDIRECT_URI,
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
    }

    post_request = requests.post(SPOTIFY_TOKEN_URL, data=code_payload)

    # Auth Step 5: Tokens are Returned to Application
    response_data = json.loads(post_request.text)

    access_token = response_data["access_token"]
    refresh_token = response_data["refresh_token"]
    token_type = response_data["token_type"]
    expires_in = response_data["expires_in"]

    token_info = {'access_token': access_token,
                  'refresh_token': refresh_token,
                  'token_type': token_type,
                  'expires_in': expires_in}

    res = make_response(redirect('http://localhost/about', code=302))

    # I'd rather send token_info to `localStorage` at my client, instead of setting cookies
    #res.set_cookie('access_token', access_token)
    #res.set_cookie('refresh_token', refresh_token)
    #res.set_cookie('token_type', token_type)
    #res.set_cookie('expires_in', str(expires_in))

    return res

Is there a way of sending 'token_info' above with jsonify() (or else) to client via make_response() before redirection occurs?


Solution

  • Please read foot-notes if your concern is about the OAuth flow!

    According to HTTP 302 Redirect - is a message-body needed?, you can return a body in a 302 Redirect. Indeed RFC2616 doesn't forbid to send a response body.

    Well, I'm not a Flask expert, but this should do the job. It returns the JSON in the body of the 302 Redirect response. Pay attention that you should also set the right Content-Type header (not done in my example).

    import json
    from flask import Flask, redirect, Response
    app = Flask(__name__)
    
    def jsonResponseFactory(data):
        '''Return a callable in top of Response'''
        def callable(response=None, *args, **kwargs):
            '''Return a response with JSON data from factory context'''
            return Response(json.dumps(data), *args, **kwargs)
        return callable
    
    @app.route('/')
    def hello_world():
        token_info = {
            'access_token': '...',
            'refresh_token': '...',
            'token_type': '...',
            'expires_in': '...' 
        }
    
        return redirect(
            'http://localhost/about',
            302,
            jsonResponseFactory(token_info)
        )
    

    Btw, if your client needs to read the token, it should not automatically follow the redirect! Furthermore, I don't know if a 302 Redirect is the best answer to your OAuth callback endpoint. But all of this depends on the context.

    Edit: Here are some notes after feedback from comments.

    1. You should not store tokens in the localStorage. There is a good explanation from Auth0 website. Furthermore, browsers (starting with Safari) will now drop localStorage after 7 days without user interaction
    2. Again from Auth0 (and I fully agree) the token should be handled server-side.
    3. I'll let my answer as it is (even if the scope is not ideal) because it's actually answer to your HTTP related question.