Jammming project - search not working for other users

Hi all,

Can anyone confirm if the Jamming project works for users other than yourself?
I completed the project and got it running successfully for myself.
However, when the app is accessed frommy boyfriend’s spotify account, it errors out when trying to search.

Here is the CodeCademy project page;
https://www.codecademy.com/paths/front-end-engineer-career-path-21/tracks/fecp-react-part-ii/modules/fecp-challenge-project-jammming/projects/jammming-prj

What I tried;

  1. Open Chrome incognito mode
  2. Access the url I hosted my website at
  3. When prompted, log into my spotify account
  4. Try to search something ( & then add it to playlist)

When I log in with my own spotify credentials, this works fine.

However, if I;
5) Close that browser window, re-open incognito mode & have my boyfriend repeat the above steps on his own spotify account.

It will error out when he tries to search something from his own account.

Is there any reason why this may be happening?
I can see the console is generating different access tokens for us both, if this is relevant.

My app, hosted online;

My app.js;

import React from 'react';
import './App.css';
import SearchResults from './../SearchResults/SearchResults.js'
import Playlist from './../Playlist/Playlist.js'
import SearchBar from './../SearchBar/SearchBar.js';
import Spotify from './../util/Spotify.js';

class App extends React.Component {     

  constructor(props) {
    super(props);
    this.state = {
      searchResults : [
          // {"id": 1, "name": "One True Love", "artist": "Jordan", "album": "Miss U Baby" },
          // {"id": 2, "name": "Favourite Honey", "artist": "DJ Robin", "album": "Never Look Back" },
          // {"id": 3, "name": "Perfect", "artist": "TT Beats", "album": "Actions Not Words" }
      ],
      playlistName : "New Playlist",
      playlistTracks :  [
          // {"id": 4, "name": "song1", "artist": "Jordeen", "album": "Miss U Boby" },
          // {"id": 5, "name": "song2", "artist": "DJ Robeen", "album": "Never Look Bk" },
          // {"id": 6, "name": "song3", "artist": "TT Beatzz", "album": "Actions Not Wordzz" }
      ]
      };
      this.addTrack = this.addTrack.bind(this);
      this.removeTrack = this.removeTrack.bind(this);
      this.updatePlaylistName = this.updatePlaylistName.bind(this);
      this.savePlaylist = this.savePlaylist.bind(this);
      this.search = this.search.bind(this);
  }
 
  render() {
    return (
      <div>
        <h1>Ja<span className="highlight">mmm</span>ing</h1>
        <div className="App">
          <SearchBar onSearch={this.search}/>
          <div className="App-playlist">
            <SearchResults searchResults={this.state.searchResults} onAdd={this.addTrack}/>
            <Playlist 
              playlistName={this.state.playlistName}
              playlistTracks={this.state.playlistTracks}
              onRemove={this.removeTrack}
              onNameChange={this.updatePlaylistName}
              onSave={this.savePlaylist}/>
          </div>
        </div>
      </div>
    );
  }

  componentDidMount() {
    window.addEventListener('load', () => {Spotify.getAccessToken()});
  }

  addTrack(track) {
    const currentPlaylist = this.state.playlistTracks;
    const isSame = currentPlaylist.some(existingTrack => existingTrack.id === track.id);
    if (isSame === false) {
        currentPlaylist.push(track);
        this.setState({playlistTracks : currentPlaylist});
    }
  }

  removeTrack(track) {
    const currentPlaylist = this.state.playlistTracks;
    const newPlaylist = currentPlaylist.filter(existingTrack => existingTrack.id !== track.id);
    this.setState({playlistTracks : newPlaylist });
  }

  updatePlaylistName(newName) {
    this.setState({playlistName : newName});
  }

  savePlaylist() {
    const trackURIs = this.state.playlistTracks.map(track => track.uri); 
    Spotify.savePlaylist(this.state.playlistName, trackURIs);
    this.setState({playlistName : 'New Playlist'});
    this.setState({playlistTracks : []})
  }

  search(searchParameter) {
    Spotify.search(searchParameter).then(searchResults => {
      this.setState({searchResults : searchResults})
    })
  }

}


export default App;

My Spotify.js;
(I removed the lines that console.log the access token/ expires in. as per the console screenshots)

let accessToken;
const client_id = 'a038457604634aac9a912fff8748b31d';
const redirect_uri = 'https://pzakeri22.github.io/spotify_playlist_app'; 
const Spotify = {

    async authorise() {
        let endpoint = this.getEndpoint();
        try {
          const response = await fetch(endpoint);
          if (response.ok) {
            const jsonResponse = await response.json();
            return jsonResponse;
          }
          throw new Error('Request Failed!');
        } catch (error) {
          console.log(error);
        }
    },

    async search(searchTerm) {
        const accessToken = Spotify.getAccessToken();  
        const headers = {
            headers: {Authorization: `Bearer ${accessToken}`}
        }
        try {
          const response = await fetch(`https://api.spotify.com/v1/search?type=track&q=${searchTerm}`, headers);
          if (response.ok) {  
            const jsonResponse = await response.json();

            if (!jsonResponse.tracks) {
                return []; 
            } else {
                return jsonResponse.tracks.items.map(track => ({ 
                     id: track.id,
                     name: track.name,
                     artist: track.artists[0].name,
                     album: track.album.name,
                     uri: track.uri
                }));
            }
          }
          throw new Error('Request Failed!');  
        } catch (error) { 
          console.log(error); 
        }
    },

     getAccessToken() {
      if (accessToken) {
          return accessToken;
      } else {
           const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);   
          const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
          if(accessTokenMatch && expiresInMatch) {
              accessToken = accessTokenMatch[1];
              const expiresIn = Number(expiresInMatch[1]);
              window.setTimeout(() => accessToken ="", expiresIn *1000);
               window.history.pushState('Access Token', null, '/');
              console.log(accessToken);
              console.log(expiresIn);
              return accessToken;
          } else {
              const accessUrl = `https://accounts.spotify.com/authorize?client_id=${client_id}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirect_uri}`;
              window.location = (accessUrl);
          }
      }
  },

  savePlaylist(playlistName, trackURIs) {

    if (!playlistName || !trackURIs) {
      return;
    }
    const accessToken = Spotify.getAccessToken();   
    const headers = {Authorization: `Bearer ${accessToken}`};
    let userID;
    let playlistID;

    async function asyncGetID() {
      const response = await fetch(`https://api.spotify.com/v1/me`, {
        headers: headers
      })
      let jsonResponse;
      if (response.ok) {
        jsonResponse = await response.json();
      }
      else {
        throw new Error(`Request Failed! ${response.status}, ${response.message}`);  
      }
      userID = jsonResponse.id;
    }

    async function asyncCreatePlaylist() {
      const response = await fetch(`https://api.spotify.com/v1/users/${userID}/playlists`, {
        headers: headers,
        method: 'POST',
        body: JSON.stringify({name: playlistName})
      });
        if (response.ok) {
          const jsonResponse = await response.json();
          playlistID = jsonResponse.id;
        } else {
          throw new Error(`Request failed! ${response.status}, ${response.message}`);
        }
      }

    async function asyncUpdatePlaylist() {
      //online it looks like endpoint is `https://api.spotify.com/v1/playlists/${playlistID}/tracks` but i will use what codecademy said
      const response = await fetch(`https://api.spotify.com/v1/users/${userID}/playlists/${playlistID}/tracks`, {
        headers: headers,
        method: 'POST',
        body: JSON.stringify({uris: trackURIs})
      });
      if (response.ok) { 
        return;
      } 
      throw new Error(`Request failed! ${response.status}, ${response.message}`);
    }

    async function save() {
      try {
        await asyncGetID();
        await asyncCreatePlaylist();
        await asyncUpdatePlaylist();
      } catch (error) { 
        console.log(error); 
      }
    }

     save();
  } 
      
}

export default Spotify;

Here are the errors in the console on my boyfriend’s account after searching;



Thanks,

Hello!

So that’s actually a security feature baked into the Spotify API itself, only you as the application owner and users you specifically authorise (up to 15 of them if I recall correctly) can authenticate and use the Spotify API for that application.

If you’d like to add a user to the authorised tester list you can do so through the Spotify Developer Dashboard where you originally registered the API and got the keys on the “Users and Access” tab at the top:

Now while of course there are publicly released apps like stat counters etc that are used by thousands of people that by definition cannot have those restrictions, that’s because Spotify treats apps in “Development Mode” and actual released apps differently, and the restrictions only apply to apps in dev mode. Unfortunately something like Jammming does not meet their required application criteria to qualify for a release app as it’s only a hobby project, so we’re stuck with the 15 authorised users limit :slight_smile: .

Well done completing the project!

1 Like

Thank you so much for the clear response!
Yes that seems to be it.
I will see if there is another way I can include this project in my portfolio without linking to the live version, as I know it won’t work for guests now.

1 Like