title: Implicit Flow with silent refresh in React
body: <p><strong>Background</strong></p>
  
  <p>I'm testing <a href="https://auth0.com/docs/flows/concepts/implicit" rel="nofollow noreferrer">Implicit Flow</a> auth in my React app and trying to implement so-called  <strong>Silent Refresh</strong> capabilities, where I periodically ask for a new access token while the user is logged in, without the need to ask him for a new authorization.</p>
  
  <p>The following is the Flow schema, where the <code>Auth0 Tenant</code>, in my case, is Spotify:</p>
  
  <p><a href="https://i.stack.imgur.com/k9YA5.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/k9YA5.png" alt="enter image description here"></a></p>
  
  <p>While SPAs(single page applications) using the Implicit Grant <em>cannot</em> use Refresh Tokens, there are other ways to provide similar functionality:</p>
  
  <blockquote>
    <ul>
    <li><p>Use <code>prompt=none</code> when invoking the <code>/authorize</code> endpoint. The user will
    not see the login or consent dialogs. </p></li>
    <li><p>Call <code>/authorize</code> from a hidden iframe and
    extract the new Access Token from the parent frame. The user will not
    see the redirects happening.</p></li>
    </ul>
  </blockquote>
  
  <hr>
  
  <p>Another approach is the implementation of something like the package <a href="https://www.npmjs.com/package/axios-auth-refresh" rel="nofollow noreferrer">axios-auth-refresh</a>, a library that </p>
  
  <blockquote>
    <p>helps you implement automatic refresh of authorization via <code>axios</code> interceptors. You can easily intercept the original request when it fails, refresh the authorization and continue with the original request, without any user interaction.</p>
  </blockquote>
  
  <p><strong>Usage</strong>:</p>
  
  <pre><code>import axios from 'axios';
  import createAuthRefreshInterceptor from 'axios-auth-refresh';
  
  // Function that will be called to refresh authorization
  const refreshAuthLogic = failedRequest =&gt; axios.post('https://www.example.com/auth/token/refresh').then(tokenRefreshResponse =&gt; {
      localStorage.setItem('token', tokenRefreshResponse.data.token);
      failedRequest.response.config.headers['Authorization'] = 'Bearer ' + tokenRefreshResponse.data.token;
      return Promise.resolve();
  });
  
  // Instantiate the interceptor (you can chain it as it returns the axios instance)
  createAuthRefreshInterceptor(axios, refreshAuthLogic);
  
  // Make a call. If it returns a 401 error, the refreshAuthLogic will be run, 
  // and the request retried with the new token
  axios.get('https://www.example.com/restricted/area')
      .then(/* ... */)
      .catch(/* ... */);
  </code></pre>
  
  <hr>
  
  <p><strong>Set Up</strong></p>
  
  <p>This is my <code>Parent</code> component (please note that <code>isAuthenticated</code> state refers to my app authentication, not related to the Spotify token I need for <strong>Silent Refresh</strong>):</p>
  
  <pre><code>import SpotifyAuth from './components/spotify/Spotify';
  
  class App extends Component {
    constructor() {
      super();
      this.state = {
        isAuthenticated: false,
        isAuthenticatedWithSpotify: false,
        spotifyToken: '',
        tokenRenewed:'' 
      };
      this.logoutUser = this.logoutUser.bind(this);
      this.loginUser = this.loginUser.bind(this);
      this.onConnectWithSpotify = this.onConnectWithSpotify.bind(this);
    };
  
    UNSAFE_componentWillMount() {
      if (window.localStorage.getItem('authToken')) {
        this.setState({ isAuthenticated: true });
      };
    };
  
    logoutUser() {
      window.localStorage.clear();
      this.setState({ isAuthenticated: false });
    };
  
    loginUser(token) {
      window.localStorage.setItem('authToken', token);
      this.setState({ isAuthenticated: true });
    };
  
    onConnectWithSpotify(token){
      this.setState({ spotifyToken: token,
                      isAuthenticatedWithSpotify: true
      }, () =&gt; {
         console.log('Spotify Token', this.state.spotifyToken)
      });
    }
  
    render() {
      return (
        &lt;div&gt;
          &lt;NavBar
            title={this.state.title}
            isAuthenticated={this.state.isAuthenticated}
          /&gt;
          &lt;section className="section"&gt;
            &lt;div className="container"&gt;
              &lt;div className="columns"&gt;
                &lt;div className="column is-half"&gt;
                  &lt;br/&gt;
                  &lt;Switch&gt;
                    &lt;Route exact path='/' render={() =&gt; (
                      &lt;SpotifyAuth
                      onConnectWithSpotify={this.onConnectWithSpotify}
                      spotifyToken={this.state.spotifyToken}
                      /&gt;
                    )} /&gt;
                    &lt;Route exact path='/login' render={() =&gt; (
                      &lt;Form
                        formType={'Login'}
                        isAuthenticated={this.state.isAuthenticated}
                        loginUser={this.loginUser}
                        userId={this.state.id} 
                      /&gt;
                    )} /&gt;
                    &lt;Route exact path='/logout' render={() =&gt; (
                      &lt;Logout
                        logoutUser={this.logoutUser}
                        isAuthenticated={this.state.isAuthenticated}
                        spotifyToken={this.state.spotifyToken}
                      /&gt;
                    )} /&gt;
                  &lt;/Switch&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/section&gt;
        &lt;/div&gt;
      )
    }
  };
  
  export default App;
  </code></pre>
  
  <p>and the following is my <code>SpotifyAuth</code> component, whereby the user clicks on a button in order to authorize and authenticate his Spotify account with the app when he logs in.</p>
  
  <pre><code>import Credentials from './spotify-auth.js'
  import './Spotify.css'
  
  class SpotifyAuth extends Component {  
    constructor (props) {
      super(props);
      this.state = {
        isAuthenticatedWithSpotify: this.props.isAuthenticatedWithSpotify
      };
      this.state.handleRedirect = this.handleRedirect.bind(this);
    };
  
    generateRandomString(length) {
      let text = '';
      const possible =
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      for (let i = 0; i &lt; length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
      }
      return text;
      } 
  
    getHashParams() {
      const hashParams = {};
      const r = /([^&amp;;=]+)=?([^&amp;;]*)/g;
      const q = window.location.hash.substring(1);
      let e = r.exec(q);
      while (e) {
        hashParams[e[1]] = decodeURIComponent(e[2]);
        e = r.exec(q);
      }
      return hashParams;
    }
  
    componentDidMount() {
      //if (this.props.isAuthenticated) {
      const params = this.getHashParams();
  
      const access_token = params.access_token;
      const state = params.state;
      const storedState = localStorage.getItem(Credentials.stateKey);
      localStorage.setItem('spotifyAuthToken', access_token);
      localStorage.getItem('spotifyAuthToken');
  
      if (window.localStorage.getItem('authToken')) {
        this.setState({ isAuthenticatedWithSpotify: true });
      };
      if (access_token &amp;&amp; (state == null || state !== storedState)) {
        alert('Click "ok" to finish authentication with Spotify');
      } else {
        localStorage.removeItem(Credentials.stateKey);
      }
      this.props.onConnectWithSpotify(access_token); 
    };
  
  
    handleRedirect(event) {
      event.preventDefault()
      const params = this.getHashParams();
      const access_token = params.access_token;
      console.log(access_token);
  
      const state = this.generateRandomString(16);
      localStorage.setItem(Credentials.stateKey, state);
  
      let url = 'https://accounts.spotify.com/authorize';
      url += '?response_type=token';
      url += '&amp;client_id=' + encodeURIComponent(Credentials.client_id);
      url += '&amp;scope=' + encodeURIComponent(Credentials.scope);
      url += '&amp;redirect_uri=' + encodeURIComponent(Credentials.redirect_uri);
      url += '&amp;state=' + encodeURIComponent(state);
      window.location = url; 
    };
  
    render() {
        return (
          &lt;div className="button_container"&gt;
              &lt;h1 className="title is-4"&gt;&lt;font color="#C86428"&gt;Welcome&lt;/font&gt;&lt;/h1&gt;
              &lt;div className="Line" /&gt;&lt;br/&gt;
                &lt;button className="sp_button" onClick={(event) =&gt; this.handleRedirect(event)}&gt;
                  &lt;strong&gt;LINK YOUR SPOTIFY ACCOUNT&lt;/strong&gt;
                &lt;/button&gt;
          &lt;/div&gt;
        )
    }
  }
  export default SpotifyAuth;
  </code></pre>
  
  <p>Silent Refresh, however, would not need the button above, nor render anything.</p>
  
  <hr>
  
  <p>For the sake of completeness, this is the endpoint I use for my app authentication process, which uses <code>jwt</code> -json web tokens to encrypt tokens and pass them via cookies from server to client (but this encryption tool is not being used for Spotify token being passed to my client, so far): </p>
  
  <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:
          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><strong>QUESTION</strong></p>
  
  <p>Considering the options and the code above, how do I use my set up in order to add Silent refresh and handle the redirect to Spotify and get a new token every hour on the background?</p>
  
  <p>Something that sits in between <a href="https://stackoverflow.com/questions/54932922/how-to-persist-auth0-login-status-in-browser-for-react-spa">this solution</a> and my code?</p>