1

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?

8-Bit Borges
  • 9,643
  • 29
  • 101
  • 198

1 Answers1

2

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.
Community
  • 1
  • 1
Raphael Medaer
  • 2,528
  • 12
  • 18
  • thanks! it should not follow redirect, but send the body BEFORE redirect. I just need body sent so I can store data in `localStorage()`. does it make sense? – 8-Bit Borges Mar 30 '20 at 23:21
  • Actually the body is sent before the redirect is done. But if I well understand your use case this is a callback for the "Authorize Code Grant". If so, there are multiple things here: - IMO you should not store the access_token or the refresh_token in the localstorage (multiple reasons, a good guide here: https://auth0.com/docs/tokens/guides/store-tokens#don-t-store-tokens-in-local-storage) - IMO (again) you should deal with your access/refresh token in your backend (again a good guide here: https://auth0.com/docs/tokens/guides/store-tokens#if-a-backend-is-present) – Raphael Medaer Mar 30 '20 at 23:32
  • I'll edit my answer to give you more details about your comment... ;-) – Raphael Medaer Mar 30 '20 at 23:34
  • I know, and I have an active question with an open bounty regarding my doubts about this handling of tokens. [https://stackoverflow.com/q/60859658/3451339]. some say that keeping states at backend is not desirable, due to scaling afterwards, and staletess practices, and at least my secret credentials are not exposed. go for the answer! – 8-Bit Borges Mar 30 '20 at 23:36
  • I edited this one. :-) I'll take a look to the other question ASAP! I hope this one could already help Flask users which needs to response with a body in a `302 Redirect` even if it's not really your "end purpose". – Raphael Medaer Mar 30 '20 at 23:43