How to use Spotify to login to your ReactJS app

·

5 min read

As an avid fan of music, spotify api is one of the api's I love to play with. On this article, I'll show how I use Spotify Oauth on my react app.

Firstly, we need to create an app on Spotify Api Dashboard for us to have spotify Client ID and Client Secret to be used later on.

Screenshot (35).png

After creating our spotify app, we need to configure it by setting its redirect uri to the uri or page we'll be using later on to get the spotify authorization code.

Screenshot (36).png

For you to be aware, I am using the following dependencies on my react app:

  • axios
  • query-string
  • react-router-dom
  • vite
  • tailwindcss

Now that we have all the things we need, let's go to our react app.

On our react base url page or homepage, let's create a button that will redirect us to spotify authorization page.

const Home = () => {
  return (
    <main className="flex justify-center items-center h-screen">
      <button
        className="border-indigo-500/100 border-2 p-2 rounded"
        onClick={authenticate}
      >
        Login with Spotify
      </button>
    </main>
  );
};

Screenshot (38).png

As you can see from above, we have a button that if you click, it will invoke the authenticate function.

import queryString from "query-string";

const authenticate = () => {
  const queries = {
    response_type: "code",
    client_id: import.meta.env.VITE_SPOTIFY_CLIENT_ID,
    scope: "user-read-email",
    redirect_uri: import.meta.env.VITE_SPOTIFY_REDIRECT_URI,
  };
  location.assign(
    `https://accounts.spotify.com/authorize?${queryString.stringify(queries)}`
  );
};

So what does this function do? Well it simply, in layman's terms, says that let us redirect the user to this link https://accounts.spotify.com/en/authorize?scope=user-read-email&response_type=code&redirect_uri=http://localhost:3000/callback/&client_id=1234

Screenshot (37).png

If the user agreed or authorized our app, then spotify will redirect the user to the redirect_url we set on spotify app config which is to localhost:3000/callback plus it will contain an authorization code that we can use to get our refresh token

For example: https://localhost:3000/callback?code=NApCCg..BkWtQ&state=34fFs29kd09

Now that we have the authorization code, we can now extract it on our callback page.

import {
  useSearchParams,
  useNavigate,
} from "react-router-dom";

const Callback = () => {
  const [searchParams] = useSearchParams();
  const navigate = useNavigate();

  useEffect(() => {
    window.localStorage.setItem("authorization", searchParams.get("code"));
    navigate("/");
  }, []);

  return (
    <main className="flex justify-center items-center h-screen">
      redirecting...
    </main>
  );
};

As you can see from the above component, I saved it on localStorage and redirected the user to our homepage.

Now, let's go back to our homepage and write some lines of code.

const Home = () => {
  const [authorization, setAuthorization] = useState(null);
  const [refreshToken, setRefreshToken] = useState(null);
  const [user, setUser] = useState(null);
  const authB64 = btoa(`${import.meta.env.VITE_SPOTIFY_CLIENT_ID}:${import.meta.env.VITE_SPOTIFY_CLIENT_SECRET}`);

   useEffect(() => {
    const authorizationCode = window.localStorage.getItem("authorization");
    const refresh_token = window.localStorage.getItem("refresh_token");
    const localUser = window.localStorage.getItem("user");

    if (authorizationCode) {
      setAuthorization(authorizationCode);
    }

    if (refresh_token) {
      setRefreshToken(refresh_token);
    }

    if (localUser) {
      setUser(localUser);
    }
  }, []);

  return (
    <main className="flex justify-center items-center h-screen">
       {!authorization && (
        <button
          className="border-indigo-500/100 border-2 p-2 rounded"
          onClick={authenticate}
        >
          Login with Spotify
        </button>
      )}

      {user && <span>You are Authorized: {user.display_name}</span>}
    </main>
  );

};

On our first useEffect, we are checking our localStorage if we have authorization, refresh_token, and user. So far, we only have authorization yet.

Let's create another function to use for getting refresh token and use it on our second useEffect.

const getRefreshToken = async ({
  authorizationCode,
  redirect_uri,
  authB64,
}) => {
  const params = {
    grant_type: "authorization_code",
    code: authorizationCode,
    redirect_uri: redirect_uri,
  };

  const config = {
    headers: {
      Authorization: `Basic ${authB64}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
  };

  const {
    data: { refresh_token },
  } = await axios.post(
    "https://accounts.spotify.com/api/token",
    params,
    config
  );

  return refresh_token;
};
const Home = () => {
  const [authorization, setAuthorization] = useState(null);
  const [refreshToken, setRefreshToken] = useState(null);
  const [user, setUser] = useState(null);
  const authB64 = btoa(`${import.meta.env.VITE_SPOTIFY_CLIENT_ID}:${import.meta.env.VITE_SPOTIFY_CLIENT_SECRET}`);

  ...

   useEffect(() => {
    const start = async () => {
      const refresh_token = await getRefreshToken({
        authorizationCode: authorization,
        redirect_uri,
        authB64,
        token: "refresh",
      });

      window.localStorage.setItem("refresh_token", refresh_token);
      setRefreshToken(refresh_token);
    };

    authorization && !refreshToken && start();
  }, [authorization]);

  return (
    <main className="flex justify-center items-center h-screen">
       {!authorization && (
        <button
          className="border-indigo-500/100 border-2 p-2 rounded"
          onClick={authenticate}
        >
          Login with Spotify
        </button>
      )}

      {user && <span>You are Authorized: {user.display_name}</span>}
    </main>
  );
};

As you can see on our second useEffect, it will run again whenever there is a change on our authorization variable.
This useEffect, basically, gets the refreshToken, store it on localStorage and set it on a variable.

Now that we have both the authorization code and refresh_token, we can now get the access_token which we're going to use for getting the user information.

const getAccessToken = async ({ refreshToken, authB64 }) => {
  const params = {
    grant_type: "refresh_token",
    refresh_token: refreshToken,
  };

  const config = {
    headers: {
      Authorization: `Basic ${authB64}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
  };

  const {
    data: { access_token },
  } = await axios.post(
    "https://accounts.spotify.com/api/token",
    params,
    config
  );

  return access_token;
};
const Home = () => {
  const [authorization, setAuthorization] = useState(null);
  const [refreshToken, setRefreshToken] = useState(null);
  const [user, setUser] = useState(null);
  const authB64 = btoa(`${import.meta.env.VITE_SPOTIFY_CLIENT_ID}:${import.meta.env.VITE_SPOTIFY_CLIENT_SECRET}`);

  ...

   useEffect(() => {
    const getUser = async () => {
      const access_token = await getAccessToken({
        refreshToken,
        authB64,
      });

      const config = {
        headers: {
          Authorization: `Bearer ${access_token}`,
          "Content-Type": "application/json",
        },
      };

      const res = await axios.get("https://api.spotify.com/v1/me", config);

      window.localStorage.setItem("user", res.data);
      setUser(res.data);
    };

    authorization && refreshToken && getUser();
  }, [refreshToken]);

  return (
    <main className="flex justify-center items-center h-screen">
       {!authorization && (
        <button
          className="border-indigo-500/100 border-2 p-2 rounded"
          onClick={authenticate}
        >
          Login with Spotify
        </button>
      )}

      {user && <span>You are Authorized: {user.display_name}</span>}
    </main>
  );
};

Here on our third useEffect, we can get the user information if we both have authorization and refreshToken and set it on our user variable. Now, we can show the user name on our homepage when they are authorized/loggedin and hide the login button.

 <main className="flex justify-center items-center h-screen">
       {!authorization && (
        <button
          className="border-indigo-500/100 border-2 p-2 rounded"
          onClick={authenticate}
        >
          Login with Spotify
        </button>
      )}

      {user && <span>You are Authorized: {user.display_name}</span>}
  </main>

spotify.gif

You can see this article's repository here.