title: Flask and React - Handling tokens after Spotify Authorization
body: <p>I have implemented JWT for user login in my app (before Spotify Auth), like so:</p>
  
  <h2>Flask</h2>
  
  <pre><code>@auth_blueprint.route('/auth/login', methods=['POST'])
  def login_user():
      # get post data
      post_data = request.get_json()
      response_object = {
          'status': 'fail',
          'message': 'Invalid payload.'
      }
      if not post_data:
          return jsonify(response_object), 400
      email = post_data.get('email')
      password = post_data.get('password')
      try:
          # fetch the user data
          user = User.query.filter_by(email=email).first()
          if user and bcrypt.check_password_hash(user.password, password):
              auth_token = user.encode_auth_token(user.id)
              if auth_token:
                  response_object['status'] = 'success'
                  response_object['message'] = 'Successfully logged in.'
                  response_object['auth_token'] = auth_token.decode()
                  return jsonify(response_object), 200
          else:
              response_object['message'] = 'User does not exist.'
              return jsonify(response_object), 404
      except Exception:
          response_object['message'] = 'Try again.'
          return jsonify(response_object), 500
  </code></pre>
  
  <hr>
  
  <p>These are the methods of my SQLAlchemy <code>User(db.Model)</code></p>
  
  <pre><code>def encode_auth_token(self, user_id):
          """Generates the auth token"""
          try:
              payload = {
                  'exp': datetime.datetime.utcnow() + datetime.timedelta(
                      days=current_app.config.get('TOKEN_EXPIRATION_DAYS'), 
                      seconds=current_app.config.get('TOKEN_EXPIRATION_SECONDS')
                  ),
                  'iat': datetime.datetime.utcnow(),
                  'sub': user_id
              }
              return jwt.encode(
                  payload,
                  current_app.config.get('SECRET_KEY'),
                  algorithm='HS256'
              )
          except Exception as e:
              return e
  
  @staticmethod
  def decode_auth_token(auth_token):
          """
          Decodes the auth token - :param auth_token: - :return: integer|string
          """
          try:
              payload = jwt.decode(
                  auth_token, current_app.config.get('SECRET_KEY'))
              return payload['sub']
          except jwt.ExpiredSignatureError:
              return 'Signature expired. Please log in again.'
          except jwt.InvalidTokenError:
              return 'Invalid token. Please log in again.'
  </code></pre>
  
  <h2>React</h2>
  
  <p><strong>App.jsx</strong></p>
  
  <pre><code>  loginUser(token) {
      window.localStorage.setItem('authToken', token);
      this.setState({ isAuthenticated: true });
      this.getUsers();
      this.createMessage('Welcome', 'success');
    };
  
  (...)
  
  &lt;Route exact path='/login' render={() =&gt; (
    &lt;Form
      isAuthenticated={this.state.isAuthenticated}
      loginUser={this.loginUser}
    /&gt;
  )} /&gt;
  </code></pre>
  
  <p>and</p>
  
  <p><strong>Form.jsx</strong></p>
  
  <pre><code>handleUserFormSubmit(event) {
      event.preventDefault();
      const data = {
        email: this.state.formData.email,
        password: this.state.formData.password
      };
      const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/${formType.toLowerCase()}`;
      axios.post(url, data)
        .then((res) =&gt; {
          this.props.loginUser(res.data.auth_token);
      })
  </code></pre>
  
  <h2>Third Party Authorization + Second App Authentication</h2>
  
  <p>Now I'd like to add a second layer of authentication and handle tokens after Spotify callback, like so: </p>
  
  <pre><code>@spotify_auth_bp.route("/callback", methods=['GET', 'POST'])
  def spotify_callback():
  
      # Auth Step 4: Requests refresh and access tokens
      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"]
  
      # At this point, there is to generate a custom token for the frontend
      # Either a self-contained signed JWT or a random token?
      # In case the token is not a JWT, it should be stored in the session (in case of a stateful API)
      # or in the database (in case of a stateless API)
      # In case of a JWT, the authenticity can be tested by the backend with the signature so it doesn't need to be stored at all?
  
      res = make_response(redirect('http://localhost/about', code=302))
  
      return res
  </code></pre>
  
  <hr>
  
  <p><strong>Note</strong>: this a possible endpoint for getting new Spotify tokens:</p>
  
  <pre><code>@spotify_auth_bp.route("/refresh_token", methods=['GET', 'POST'])
  def refresh_token():
          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')
  
          code_payload = {
              "grant_type": "refresh_token",
              "refresh_token": refresh_token,
          }
  
          encode = 'application/x-www-form-urlencoded'
          auth = base64.b64encode("{}:{}".format(CLIENT_ID, CLIENT_SECRET).encode())
          headers = {"Content-Type" : encode, "Authorization" : "Basic {}".format(auth)} 
  
          post_request = requests.post(SPOTIFY_TOKEN_URL, data=code_payload, headers=headers)
          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"]
  
          return access_token
  </code></pre>
  
  <hr>
  
  <p>What is the best way of handling my tokens after Spotify callback?</p>
  
  <p>Considering that, once user is logged with the app, he will also be logged with Spotify non-stop, having to refresh Spotify's access token every 60 minutes:</p>
  
  <ul>
  <li><p>Is Authorization Code a server-to-server flow only to protect secret app credentials, and then it is safe to have tokens at frontend?</p></li>
  <li><p>Should I keep both Access token and refresh tokens stored at frontend, and have a Stateless JWT?</p></li>
  <li><p>Should I keep only temporary access token and keep refresh tokens at database, having a Stateful JWT?</p></li>
  <li><p>Should I opt for a Session, persisted only server-side, instead?</p></li>
  </ul>
  
  <p>What is the safest way of handling my sensitive data here? And, considering the code above, how so?</p>