title: INVALID_CLIENT: Invalid redirect URI error when running GitHub pages application. Uses Spotify API
body: <p>I've deployed a React application to GitHub pages that uses the Spotify API to get the currently playing track on Spotify and display it in the web page. This runs great locally and used a local server running on port 8888 to send the api requests and redirect to the app running on localhost:3000. The issue is that the app as is gives the above error when I attempt to request the token from the Spotify API and I'm guessing it's because the redirect uri that I'm using is no longer valid since the app is now on GitHub and can't communicate with localhost:3000.</p>
  
  <p>The following is the code that ran on localhost:8888 and retrieved the access token. I changed my redirect URI to match the location of the hosted version of the app.</p>
  
  <pre><code>var express = require('express'); // Express web server framework
  var request = require('request'); // "Request" library
  var querystring = require('querystring');
  var cookieParser = require('cookie-parser');
  
  var client_id = '[MY_CLIENT_ID]'; // Your client id
  var client_secret = '[MY_CLIENT_SECRET]'; // Your secret
  var redirect_uri = 'http://malcolmross19.github.io/project/'; // Your redirect uri
  
  /**
   * Generates a random string containing numbers and letters
   * @param  {number} length The length of the string
   * @return {string} The generated string
   */
  var generateRandomString = function(length) {
    var text = '';
    var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  
    for (var i = 0; i &lt; length; i++) {
      text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return text;
  };
  
  var stateKey = 'spotify_auth_state';
  
  var app = express();
  
  app.use(express.static(__dirname + '/public'))
     .use(cookieParser());
  
  app.get('/login', function(req, res) {
  
    var state = generateRandomString(16);
    res.cookie(stateKey, state);
  
    // your application requests authorization
    var scope = 'user-read-private user-read-email user-read-playback-state';
    res.redirect('https://accounts.spotify.com/authorize?' +
      querystring.stringify({
        response_type: 'code',
        client_id: client_id,
        scope: scope,
        redirect_uri: redirect_uri,
        state: state
      }));
  });
  
  app.get('/callback', function(req, res) {
  
    // your application requests refresh and access tokens
    // after checking the state parameter
  
    var code = req.query.code || null;
    var state = req.query.state || null;
    var storedState = req.cookies ? req.cookies[stateKey] : null;
  
    if (state === null || state !== storedState) {
      res.redirect('/#' +
        querystring.stringify({
          error: 'state_mismatch'
        }));
    } else {
      res.clearCookie(stateKey);
      var authOptions = {
        url: 'https://accounts.spotify.com/api/token',
        form: {
          code: code,
          redirect_uri: redirect_uri,
          grant_type: 'authorization_code'
        },
        headers: {
          'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
        },
        json: true
      };
  
      request.post(authOptions, function(error, response, body) {
        if (!error &amp;&amp; response.statusCode === 200) {
  
          var access_token = body.access_token,
              refresh_token = body.refresh_token;
  
          var options = {
            url: 'https://api.spotify.com/v1/me',
            headers: { 'Authorization': 'Bearer ' + access_token },
            json: true
          };
  
          // use the access token to access the Spotify Web API
          request.get(options, function(error, response, body) {
            console.log(body);
          });
  
          // we can also pass the token to the browser to make requests from there
          res.redirect('http://malcolmross19.github.io/project/#' +
            querystring.stringify({
              access_token: access_token,
              refresh_token: refresh_token
            }));
        } else {
          res.redirect('/#' +
            querystring.stringify({
              error: 'invalid_token'
            }));
        }
      });
    }
  });
  
  app.get('/refresh_token', function(req, res) {
  
    // requesting access token from refresh token
    var refresh_token = req.query.refresh_token;
    var authOptions = {
      url: 'https://accounts.spotify.com/api/token',
      headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
      form: {
        grant_type: 'refresh_token',
        refresh_token: refresh_token
      },
      json: true
    };
  
    request.post(authOptions, function(error, response, body) {
      if (!error &amp;&amp; response.statusCode === 200) {
        var access_token = body.access_token;
        res.send({
          'access_token': access_token
        });
      }
    });
  });
  
  console.log('Listening on 8888');
  app.listen(8888);
  </code></pre>
  
  <p>The following is the code for the app itself. I'm sure I need to change the href attribute in the link to go somewhere other than localhost:8888 but I'm not sure exactly where.</p>
  
  <pre><code>import React, { Component } from 'react';
  import './App.css';
  import SpotifyWebApi from 'spotify-web-api-js';
  import ReactCountdownClock from 'react-countdown-clock';
  
  const spotifyApi = new SpotifyWebApi();
  
  class App extends Component {
      constructor(){
      super();
      const params = this.getHashParams();
      const token = params.access_token;
      if (token) {
        spotifyApi.setAccessToken(token);
      }
      this.state = {
        loggedIn: token ? true : false,
        nowPlaying: { name: 'Not Checked', albumArt: ''},
        currentCount: 5,
        clockHidden: true,
        songInfoHidden: true,
        answer: ''
      }
      this.getNowPlaying = this.getNowPlaying.bind(this);
      this.getHashParams = this.getHashParams.bind(this);
      this.toggleClock = this.toggleClock.bind(this);
      this.toggleInfo = this.toggleInfo.bind(this);
      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }
  
    getNowPlaying(){
    spotifyApi.getMyCurrentPlaybackState()
      .then((response) =&gt; {
        console.log(response)
        this.setState({
          nowPlaying: {
              name: response.item.name,
              albumArt: response.item.album.images[0].url
            }
        });
      })
    }
  
    getHashParams(){
      var hashParams = {};
      var e, r = /([^&amp;;=]+)=?([^&amp;;]*)/g,
          q = window.location.hash.substring(1);
      e = r.exec(q)
      while (e) {
         hashParams[e[1]] = decodeURIComponent(e[2]);
         e = r.exec(q);
      }
      return hashParams;
    }
  
    toggleClock(){
      this.setState({
        clockHidden: !this.state.clockHidden
      });
    }
  
    toggleInfo(){
      this.toggleClock();
      this.setState({
        songInfoHidden: !this.state.songInfoHidden,
      });
    }
  
    buttonClick(e){
      this.getNowPlaying();
  
      if(this.state.songInfoHidden){
        this.toggleClock();
      } else {
        this.toggleInfo();
        this.setState({
          answer: ''
        });
      }
    }
  
    handleChange(event){
      this.setState({
        answer: event.target.value
      });
    }
  
    handleSubmit(event){
      event.preventDefault();
      this.toggleInfo();
    }
  
    render() {
      return (
        &lt;div className="App"&gt;
          &lt;div className="MainHeader"&gt;
          {!this.state.loggedIn &amp;&amp;
            &lt;div&gt;
              &lt;h1&gt;Login to Spotify to use Name That Tune&lt;/h1&gt;
            &lt;/div&gt;}
  
            {this.state.loggedIn &amp;&amp;
              &lt;div&gt;
                &lt;h1&gt;Name That Tune&lt;/h1&gt;
              &lt;/div&gt;}
  
            &lt;a href="http://localhost:8888"&gt;Request Access From Spotify&lt;/a&gt;
          &lt;/div&gt;
  
            &lt;div className="Body"&gt;
              &lt;hr /&gt;
              {this.state.loggedIn &amp;&amp; !this.state.clockHidden &amp;&amp;
                  &lt;ReactCountdownClock
                    seconds={30}
                    color='#5CC8FF'
                    alpha={0.9}
                    size={720}
                    onComplete={() =&gt; this.toggleInfo()}
                  /&gt;}
              {this.state.loggedIn &amp;&amp; !this.state.songInfoHidden &amp;&amp;
                &lt;div&gt;
                  &lt;h1 className="Header"&gt;How Did You Do?&lt;/h1&gt;
                  &lt;div className="NowPlaying"&gt;Now Playing: { this.state.nowPlaying.name }&lt;/div&gt;
                &lt;/div&gt;}
  
              &lt;br /&gt;
  
              {this.state.loggedIn &amp;&amp; !this.state.songInfoHidden &amp;&amp;
                &lt;div&gt;
                  &lt;img src={this.state.nowPlaying.albumArt} style={{ height: 650 }}/&gt;
                &lt;/div&gt;}
  
              {this.state.loggedIn &amp;&amp; !this.state.clockHidden &amp;&amp;
                &lt;form onSubmit={this.handleSubmit}&gt;
                  &lt;label&gt;
                    &lt;input type="text" value={this.state.answer} placeholder="Enter Your Answer Here" onChange={this.handleChange} /&gt;
                  &lt;/label&gt;
                  &lt;input type="submit" value="Check Answer" /&gt;
                &lt;/form&gt;}
  
              &lt;br /&gt;
  
              {this.state.loggedIn &amp;&amp; !this.state.songInfoHidden &amp;&amp;
                &lt;div&gt;
                  &lt;h1&gt;Your Answer:&lt;br /&gt;&lt;/h1&gt;
                  &lt;div className="Answer"&gt;{this.state.answer}&lt;/div&gt;
                &lt;/div&gt;}
  
              &lt;br /&gt;
  
              {this.state.loggedIn &amp;&amp;
                &lt;button className="NowPlayingButton" onClick={() =&gt; this.buttonClick()}&gt;
                  Check Now Playing
                &lt;/button&gt;
              }
            &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  }
  
  export default App;
  </code></pre>